Fabric: Custom border rendering on a separate CALayer

Summary:
This diff fixes previously broken custom border rendering.
We need a dedicated CALayer for border bitmap in order to fully support all UIView capabilities in case if some subclass uses that. Otherwise, any call of `drawRect:`  method can override any content which is stored inside `contents` property of CALayer.
Q&A:
How does it work in current RN? - It does not. All `drawRect:` methods in RCTView subclasses are dysfunctional.
How does text view work in current RN? - RCTTextView does not inherit RCTView, so it does not have this problem.
How does text view support custom borders in current RN then? - It does not.

Reviewed By: PeteTheHeat

Differential Revision: D10228805

fbshipit-source-id: 22bc31f41ab1914a97f3a5981cd1b24ebca725cd
This commit is contained in:
Valentin Shergin 2018-10-12 19:16:02 -07:00 committed by Facebook Github Bot
parent 082a869dae
commit c8b6d606a0
1 changed files with 23 additions and 14 deletions

View File

@ -20,6 +20,7 @@ using namespace facebook::react;
@implementation RCTViewComponentView
{
UIColor *_backgroundColor;
CALayer *_borderLayer;
}
- (instancetype)initWithFrame:(CGRect)frame
@ -49,6 +50,10 @@ using namespace facebook::react;
{
[super layoutSubviews];
if (_borderLayer) {
_borderLayer.frame = self.layer.bounds;
}
if (_contentView) {
_contentView.frame = RCTCGRectFromRect(_layoutMetrics.getContentFrame());
}
@ -346,8 +351,11 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle) {
);
if (useCoreAnimationBorderRendering) {
layer.contents = nil;
layer.needsDisplayOnBoundsChange = NO;
if (_borderLayer) {
[_borderLayer removeFromSuperlayer];
_borderLayer = nil;
}
layer.borderWidth = (CGFloat)borderMetrics.borderWidths.left;
layer.borderColor = RCTCGColorRefFromSharedColor(borderMetrics.borderColors.left);
layer.cornerRadius = (CGFloat)borderMetrics.borderRadii.topLeft;
@ -355,6 +363,14 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle) {
_contentView.layer.cornerRadius = (CGFloat)borderMetrics.borderRadii.topLeft;
_contentView.layer.masksToBounds = YES;
} else {
if (!_borderLayer) {
_borderLayer = [[CALayer alloc] init];
_borderLayer.zPosition = -1024.0f;
_borderLayer.frame = layer.bounds;
_borderLayer.magnificationFilter = kCAFilterNearest;
[layer addSublayer:_borderLayer];
}
layer.backgroundColor = nil;
layer.borderWidth = 0;
layer.borderColor = nil;
@ -373,8 +389,7 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle) {
);
if (image == nil) {
layer.contents = nil;
layer.needsDisplayOnBoundsChange = NO;
_borderLayer.contents = nil;
} else {
CGSize imageSize = image.size;
UIEdgeInsets imageCapInsets = image.capInsets;
@ -383,16 +398,14 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle) {
CGSize {(CGFloat)1.0 / imageSize.width, (CGFloat)1.0 / imageSize.height}
};
layer.contents = (id)image.CGImage;
layer.contentsScale = image.scale;
layer.needsDisplayOnBoundsChange = YES;
layer.magnificationFilter = kCAFilterNearest;
_borderLayer.contents = (id)image.CGImage;
_borderLayer.contentsScale = image.scale;
const BOOL isResizable = !UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero);
if (isResizable) {
layer.contentsCenter = contentsCenter;
_borderLayer.contentsCenter = contentsCenter;
} else {
layer.contentsCenter = CGRect { CGPoint {0.0, 0.0}, CGSize {1.0, 1.0}};
_borderLayer.contentsCenter = CGRect { CGPoint {0.0, 0.0}, CGSize {1.0, 1.0}};
}
}
@ -420,10 +433,6 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle) {
layer.cornerRadius = cornerRadius;
layer.mask = maskLayer;
}
// After updating `layer`'s parameters we have to redraw on top of it
// all custom content (calling `drawRect:` implemented in subclasses).
[layer setNeedsDisplay];
}
#pragma mark - Accessibility