Skip to content

Commit

Permalink
fix(ios): backgroundImage may not update in time when reusing hippyVi…
Browse files Browse the repository at this point in the history
…ew (#3736)

* chore(ios): revert rename Hippy prefix to NativeRender

* fix(ios): backgroundImage may not update in time when reusing hippyView

replace some deprecated UIGraphics functions

* fix(ios): backgroundImage may not update in time when reusing hippyView2
  • Loading branch information
wwwcg authored Jan 24, 2024
1 parent e799c47 commit db99542
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 177 deletions.
24 changes: 12 additions & 12 deletions renderer/native/ios/renderer/component/view/HippyBorderDrawing.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,48 +30,48 @@ typedef struct {
CGFloat topRight;
CGFloat bottomLeft;
CGFloat bottomRight;
} NativeRenderCornerRadii;
} HippyCornerRadii;

typedef struct {
CGSize topLeft;
CGSize topRight;
CGSize bottomLeft;
CGSize bottomRight;
} NativeRenderCornerInsets;
} HippyCornerInsets;

typedef struct {
CGColorRef top;
CGColorRef left;
CGColorRef bottom;
CGColorRef right;
} NativeRenderBorderColors;
} HippyBorderColors;

/**
* Determine if the border widths, colors and radii are all equal.
*/
HIPPY_EXTERN BOOL NativeRenderBorderInsetsAreEqual(UIEdgeInsets borderInsets);
HIPPY_EXTERN BOOL NativeRenderCornerRadiiAreEqual(NativeRenderCornerRadii cornerRadii);
HIPPY_EXTERN BOOL NativeRenderBorderColorsAreEqual(NativeRenderBorderColors borderColors);
HIPPY_EXTERN BOOL HippyBorderInsetsAreEqual(UIEdgeInsets borderInsets);
HIPPY_EXTERN BOOL HippyCornerRadiiAreEqual(HippyCornerRadii cornerRadii);
HIPPY_EXTERN BOOL HippyBorderColorsAreEqual(HippyBorderColors borderColors);

/**
* Convert NativeRenderCornerRadii to NativeRenderCornerInsets by applying border insets.
* Convert HippyCornerRadii to HippyCornerInsets by applying border insets.
* Effectively, returns radius - inset, with a lower bound of 0.0.
*/
HIPPY_EXTERN NativeRenderCornerInsets NativeRenderGetCornerInsets(NativeRenderCornerRadii cornerRadii, UIEdgeInsets borderInsets);
HIPPY_EXTERN HippyCornerInsets HippyGetCornerInsets(HippyCornerRadii cornerRadii, UIEdgeInsets borderInsets);

/**
* Create a CGPath representing a rounded rectangle with the specified bounds
* and corner insets. Note that the CGPathRef must be released by the caller.
*/
HIPPY_EXTERN CGPathRef NativeRenderPathCreateWithRoundedRect(CGRect bounds, NativeRenderCornerInsets cornerInsets, const CGAffineTransform *transform);
HIPPY_EXTERN CGPathRef HippyPathCreateWithRoundedRect(CGRect bounds, HippyCornerInsets cornerInsets, const CGAffineTransform *transform);

/**
* Draw a CSS-compliant border as an image. You can determine if it's scalable
* by inspecting the image's `capInsets`.
*
* `borderInsets` defines the border widths for each edge.
*/
HIPPY_EXTERN UIImage *NativeRenderGetBorderImage(NativeRenderBorderStyle borderStyle, CGSize viewSize, NativeRenderCornerRadii cornerRadii, UIEdgeInsets borderInsets,
NativeRenderBorderColors borderColors, CGColorRef backgroundColor, BOOL drawToEdge, BOOL drawBackgroundColor);
HIPPY_EXTERN UIImage *HippyGetBorderImage(HippyBorderStyle borderStyle, CGSize viewSize, HippyCornerRadii cornerRadii, UIEdgeInsets borderInsets,
HippyBorderColors borderColors, CGColorRef backgroundColor, BOOL drawToEdge, BOOL drawBackgroundColor);

HIPPY_EXTERN CGPathRef NativeRenderPathCreateOuterOutline(BOOL drawToEdge, CGRect rect, NativeRenderCornerRadii cornerRadii);
HIPPY_EXTERN CGPathRef HippyPathCreateOuterOutline(BOOL drawToEdge, CGRect rect, HippyCornerRadii cornerRadii);
106 changes: 53 additions & 53 deletions renderer/native/ios/renderer/component/view/HippyBorderDrawing.m

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions renderer/native/ios/renderer/component/view/HippyView.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,13 @@
/**
* Border styles.
*/
@property (nonatomic, assign) NativeRenderBorderStyle borderStyle;
@property (nonatomic, assign) HippyBorderStyle borderStyle;

/**
* BackgroundImage styles.
*/
@property(nonatomic, strong) UIImage *backgroundImage;
@property (nonatomic, strong) UIImage *backgroundImage;
@property (nonatomic, assign) NSUInteger backgroundImageUrlHashValue;
@property (nonatomic, strong) NSString *backgroundSize;
@property (nonatomic, assign) CGFloat backgroundPositionX;
@property (nonatomic, assign) CGFloat backgroundPositionY;
Expand Down
87 changes: 46 additions & 41 deletions renderer/native/ios/renderer/component/view/HippyView.m
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,10 @@ - (void)setBackgroundColor:(UIColor *)backgroundColor {
}

- (void)setBackgroundImage:(UIImage *)backgroundImage {
_backgroundImage = backgroundImage;
[self.layer setNeedsDisplay];
if (_backgroundImage != backgroundImage) {
_backgroundImage = backgroundImage;
[self.layer setNeedsDisplay];
}
}

- (UIEdgeInsets)bordersAsInsets {
Expand All @@ -137,7 +139,7 @@ - (UIEdgeInsets)bordersAsInsets {
};
}

- (NativeRenderCornerRadii)cornerRadii {
- (HippyCornerRadii)cornerRadii {
// Get corner radii
const CGFloat radius = MAX(0, _borderRadius);
const CGFloat topLeftRadius = _borderTopLeftRadius >= 0 ? _borderTopLeftRadius : radius;
Expand All @@ -153,7 +155,7 @@ - (NativeRenderCornerRadii)cornerRadii {
const CGFloat leftScaleFactor = HippyZeroIfNaN(MIN(1, size.height / (topLeftRadius + bottomLeftRadius)));

// Return scaled radii
return (NativeRenderCornerRadii) {
return (HippyCornerRadii) {
topLeftRadius * MIN(topScaleFactor, leftScaleFactor),
topRightRadius * MIN(topScaleFactor, rightScaleFactor),
bottomLeftRadius * MIN(bottomScaleFactor, leftScaleFactor),
Expand All @@ -176,16 +178,16 @@ - (void)setFrame:(CGRect)frame {
[super setFrame:frame];
}

- (NativeRenderBorderColors)borderColors {
return (NativeRenderBorderColors) {
- (HippyBorderColors)borderColors {
return (HippyBorderColors) {
_borderTopColor ?: _borderColor,
_borderLeftColor ?: _borderColor,
_borderBottomColor ?: _borderColor,
_borderRightColor ?: _borderColor,
};
}

void NativeRenderBoarderColorsRetain(NativeRenderBorderColors c) {
void NativeRenderBoarderColorsRetain(HippyBorderColors c) {
if (c.top) {
CGColorRetain(c.top);
}
Expand All @@ -200,7 +202,7 @@ void NativeRenderBoarderColorsRetain(NativeRenderBorderColors c) {
}
}

void NativeRenderBoarderColorsRelease(NativeRenderBorderColors c) {
void NativeRenderBoarderColorsRelease(HippyBorderColors c) {
if (c.top) {
CGColorRelease(c.top);
}
Expand Down Expand Up @@ -239,16 +241,19 @@ - (void)displayLayer:(CALayer *)layer {

[self drawShadowForLayer];

const NativeRenderCornerRadii cornerRadii = [self cornerRadii];
const HippyCornerRadii cornerRadii = [self cornerRadii];
const UIEdgeInsets borderInsets = [self bordersAsInsets];
const NativeRenderBorderColors borderColors = [self borderColors];
const HippyBorderColors borderColors = [self borderColors];
UIColor *backgroundColor = self.backgroundColor;

BOOL isRunningInTest = HippyRunningInTestEnvironment();
BOOL isCornerEqual = NativeRenderCornerRadiiAreEqual(cornerRadii);
BOOL isBorderInsetsEqual = NativeRenderBorderInsetsAreEqual(borderInsets);
BOOL isBorderColorsEqual = NativeRenderBorderColorsAreEqual(borderColors);
BOOL borderStyle = (_borderStyle == NativeRenderBorderStyleSolid || _borderStyle == NativeRenderBorderStyleNone);
BOOL isCornerEqual = HippyCornerRadiiAreEqual(cornerRadii);
BOOL isBorderInsetsEqual = HippyBorderInsetsAreEqual(borderInsets);
BOOL isBorderColorsEqual = HippyBorderColorsAreEqual(borderColors);
BOOL borderStyle = (_borderStyle == HippyBorderStyleSolid || _borderStyle == HippyBorderStyleNone);
// iOS draws borders in front of the content whereas CSS draws them behind
// the content. For this reason, only use iOS border drawing when clipping
// or when the border is hidden.
BOOL borderColorCheck = (borderInsets.top == 0 || (borderColors.top && CGColorGetAlpha(borderColors.top) == 0) || self.clipsToBounds);

BOOL useIOSBorderRendering = !isRunningInTest && isCornerEqual && isBorderInsetsEqual && isBorderColorsEqual && borderStyle && borderColorCheck;
Expand Down Expand Up @@ -312,9 +317,9 @@ - (void)displayLayer:(CALayer *)layer {
}

- (BOOL)getLayerContentForColor:(UIColor *)color completionBlock:(void (^)(UIImage *))contentBlock {
const NativeRenderCornerRadii cornerRadii = [self cornerRadii];
const HippyCornerRadii cornerRadii = [self cornerRadii];
const UIEdgeInsets borderInsets = [self bordersAsInsets];
const NativeRenderBorderColors borderColors = [self borderColors];
const HippyBorderColors borderColors = [self borderColors];
UIColor *backgroundColor = color?:self.backgroundColor;

CGRect theFrame = self.frame;
Expand All @@ -328,7 +333,7 @@ - (BOOL)getLayerContentForColor:(UIColor *)color completionBlock:(void (^)(UIIma
}
NSInteger clipToBounds = self.clipsToBounds;
NSString *backgroundSize = self.backgroundSize;
UIImage *borderImage = NativeRenderGetBorderImage(self.borderStyle, theFrame.size, cornerRadii, borderInsets,
UIImage *borderImage = HippyGetBorderImage(self.borderStyle, theFrame.size, cornerRadii, borderInsets,
borderColors, backgroundColor.CGColor, clipToBounds, !self.gradientObject);
if (!self.backgroundImage && !self.gradientObject) {
contentBlock(borderImage);
Expand All @@ -338,27 +343,27 @@ - (BOOL)getLayerContentForColor:(UIColor *)color completionBlock:(void (^)(UIIma
UIImage *decodedImage = self.backgroundImage;
CGFloat backgroundPositionX = self.backgroundPositionX;
CGFloat backgroundPositionY = self.backgroundPositionY;
UIGraphicsBeginImageContextWithOptions(theFrame.size, NO, 0);
//draw background image
CGSize imageSize = decodedImage.size;
CGSize targetSize = UIEdgeInsetsInsetRect(theFrame, [self bordersAsInsets]).size;
CGSize drawSize = makeSizeConstrainWithType(imageSize, targetSize, backgroundSize);
CGPoint originOffset = CGPointMake((targetSize.width - drawSize.width) / 2.0,
(targetSize.height - drawSize.height) / 2.0);
[decodedImage drawInRect:CGRectMake(borderInsets.left + backgroundPositionX + originOffset.x,
borderInsets.top + backgroundPositionY + originOffset.y,
drawSize.width,
drawSize.height)];
//draw border
if (borderImage) {
CGSize size = theFrame.size;
[borderImage drawInRect:(CGRect) { CGPointZero, size }];
}

//output image
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
contentBlock(resultingImage);
UIGraphicsImageRendererFormat *rendererFormat = [UIGraphicsImageRendererFormat preferredFormat];
rendererFormat.scale = borderImage.scale;
UIGraphicsImageRenderer *imageRenderer = [[UIGraphicsImageRenderer alloc] initWithSize:theFrame.size format:rendererFormat];
UIImage *renderedImage = [imageRenderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
// draw background image
CGSize imageSize = decodedImage.size;
CGSize targetSize = UIEdgeInsetsInsetRect(theFrame, borderInsets).size;
CGSize drawSize = makeSizeConstrainWithType(imageSize, targetSize, backgroundSize);
CGPoint originOffset = CGPointMake((targetSize.width - drawSize.width) / 2.0, (targetSize.height - drawSize.height) / 2.0);
[decodedImage drawInRect:CGRectMake(borderInsets.left + backgroundPositionX + originOffset.x,
borderInsets.top + backgroundPositionY + originOffset.y,
drawSize.width,
drawSize.height)];
// draw border
if (borderImage) {
CGSize size = theFrame.size;
[borderImage drawInRect:(CGRect) { CGPointZero, size }];
}
}];
contentBlock(renderedImage);
}
else if (self.gradientObject) {
CGSize size = theFrame.size;
Expand Down Expand Up @@ -395,13 +400,13 @@ - (void)updateClippingForLayer:(CALayer *)layer {
CGFloat cornerRadius = 0;

if (self.clipsToBounds) {
const NativeRenderCornerRadii cornerRadii = [self cornerRadii];
if (NativeRenderCornerRadiiAreEqual(cornerRadii)) {
const HippyCornerRadii cornerRadii = [self cornerRadii];
if (HippyCornerRadiiAreEqual(cornerRadii)) {
cornerRadius = cornerRadii.topLeft;

} else {
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
CGPathRef path = NativeRenderPathCreateWithRoundedRect(self.bounds, NativeRenderGetCornerInsets(cornerRadii, UIEdgeInsetsZero), NULL);
CGPathRef path = HippyPathCreateWithRoundedRect(self.bounds, HippyGetCornerInsets(cornerRadii, UIEdgeInsetsZero), NULL);
shapeLayer.path = path;
CGPathRelease(path);
mask = shapeLayer;
Expand Down Expand Up @@ -467,7 +472,7 @@ -(void)setBorder##side##Radius : (CGFloat)radius { \
#pragma mark - Border Style

#define setBorderStyle(side) \
-(void)setBorder##side##Style : (NativeRenderBorderStyle)style { \
-(void)setBorder##side##Style : (HippyBorderStyle)style { \
if (_border##side##Style == style) { \
return; \
} \
Expand Down
25 changes: 17 additions & 8 deletions renderer/native/ios/renderer/component/view/HippyViewManager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,18 @@ - (void)measureInAppWindow:(NSNumber *)componentTag
HIPPY_CUSTOM_VIEW_PROPERTY(backgroundImage, NSString, HippyView) {
if (json) {
NSString *imagePath = [HippyConvert NSString:json];
// Old background image need to be cleaned up in time due to view's reuse
NSUInteger oldHash = view.backgroundImageUrlHashValue;
if (oldHash != imagePath.hash) {
if (oldHash > 0) {
view.backgroundImage = nil;
}
view.backgroundImageUrlHashValue = imagePath.hash;
}
[self loadImageSource:imagePath forView:view];
} else {
view.backgroundImage = nil;
view.backgroundImageUrlHashValue = 0;
view.backgroundImage = defaultView.backgroundImage;
}
}

Expand Down Expand Up @@ -372,9 +381,9 @@ - (void)loadImageSource:(NSString *)path forView:(HippyView *)view {
view.layer.transform = json ? [HippyConvert CATransform3D:json] : defaultView.layer.transform;
view.layer.allowsEdgeAntialiasing = !CATransform3DIsIdentity(view.layer.transform);
}
HIPPY_CUSTOM_VIEW_PROPERTY(pointerEvents, NativeRenderPointerEvents, HippyView) {
HIPPY_CUSTOM_VIEW_PROPERTY(pointerEvents, HippyPointerEvents, HippyView) {
if ([view respondsToSelector:@selector(setPointerEvents:)]) {
view.pointerEvents = json ? [HippyConvert NativeRenderPointerEvents:json] : defaultView.pointerEvents;
view.pointerEvents = json ? [HippyConvert HippyPointerEvents:json] : defaultView.pointerEvents;
return;
}

Expand All @@ -383,16 +392,16 @@ - (void)loadImageSource:(NSString *)path forView:(HippyView *)view {
return;
}

switch ([HippyConvert NativeRenderPointerEvents:json]) {
case NativeRenderPointerEventsUnspecified:
switch ([HippyConvert HippyPointerEvents:json]) {
case HippyPointerEventsUnspecified:
// Pointer events "unspecified" acts as if a stylesheet had not specified,
// which is different than "auto" in CSS (which cannot and will not be
// supported in `Hippy`. "auto" may override a parent's "none".
// Unspecified values do not.
// This wouldn't override a container view's `userInteractionEnabled = NO`
view.userInteractionEnabled = YES;
break;
case NativeRenderPointerEventsNone:
case HippyPointerEventsNone:
view.userInteractionEnabled = NO;
break;
default:
Expand Down Expand Up @@ -434,9 +443,9 @@ - (void)loadImageSource:(NSString *)path forView:(HippyView *)view {
view.layer.borderWidth = json ? [HippyConvert CGFloat:json] : defaultView.layer.borderWidth;
}
}
HIPPY_CUSTOM_VIEW_PROPERTY(borderStyle, NativeRenderBorderStyle, HippyView) {
HIPPY_CUSTOM_VIEW_PROPERTY(borderStyle, HippyBorderStyle, HippyView) {
if ([view respondsToSelector:@selector(setBorderStyle:)]) {
view.borderStyle = json ? [HippyConvert NativeRenderBorderStyle:json] : defaultView.borderStyle;
view.borderStyle = json ? [HippyConvert HippyBorderStyle:json] : defaultView.borderStyle;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
* Used to control how touch events are processed.
*/
@property (nonatomic, assign) NativeRenderPointerEvents pointerEvents;
@property (nonatomic, assign) HippyPointerEvents pointerEvents;

@end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ - (void)didMoveToSuperview {
}


- (void)setPointerEvents:(NativeRenderPointerEvents)pointerEvents {
- (void)setPointerEvents:(HippyPointerEvents)pointerEvents {
_pointerEvents = pointerEvents;
self.userInteractionEnabled = (pointerEvents != NativeRenderPointerEventsNone);
if (pointerEvents == NativeRenderPointerEventsBoxNone) {
self.userInteractionEnabled = (pointerEvents != HippyPointerEventsNone);
if (pointerEvents == HippyPointerEventsBoxNone) {
self.accessibilityViewIsModal = NO;
}
}
Expand All @@ -59,7 +59,7 @@ - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// be outside the bounds of `view` (e.g., if -clipsToBounds is NO).
UIView *hitSubview = nil;
BOOL isPointInside = [self pointInside:point withEvent:event];
BOOL needsHitSubview = !(_pointerEvents == NativeRenderPointerEventsNone || _pointerEvents == NativeRenderPointerEventsBoxOnly);
BOOL needsHitSubview = !(_pointerEvents == HippyPointerEventsNone || _pointerEvents == HippyPointerEventsBoxOnly);
if (needsHitSubview && (![self clipsToBounds] || isPointInside)) {
// The default behaviour of UIKit is that if a view does not contain a point,
// then no subviews will be returned from hit testing, even if they contain
Expand All @@ -79,13 +79,13 @@ - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *hitView = (isPointInside ? self : nil);

switch (_pointerEvents) {
case NativeRenderPointerEventsNone:
case HippyPointerEventsNone:
return nil;
case NativeRenderPointerEventsUnspecified:
case HippyPointerEventsUnspecified:
return hitSubview ?: hitView;
case NativeRenderPointerEventsBoxOnly:
case HippyPointerEventsBoxOnly:
return hitView;
case NativeRenderPointerEventsBoxNone:
case HippyPointerEventsBoxNone:
return hitSubview;
default:
HippyLogError(@"Invalid pointer-events specified %ld on %@", (long)_pointerEvents, self);
Expand Down
Loading

0 comments on commit db99542

Please sign in to comment.