Skip to content

Commit

Permalink
fix(ios): resolve floating point size comparison issues (#3989)
Browse files Browse the repository at this point in the history
  • Loading branch information
wwwcg committed Sep 13, 2024
1 parent 3951757 commit c911d56
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 21 deletions.
2 changes: 1 addition & 1 deletion renderer/native/ios/renderer/HippyUIManager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ - (void)setFrame:(CGRect)frame forView:(UIView *)view{
return;
}
if (!CGRectEqualToRect(frame, renderObject.frame)) {
if (!HippyCGRectRoundInPixelNearlyEqual(frame, renderObject.frame)) {
//renderObject.frame = frame;
[renderObject setLayoutFrame:frame];
std::weak_ptr<RootNode> rootNode = [strongSelf->_shadowViewRegistry rootNodeForTag:rootTag];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#import "UIView+Hippy.h"
#import "UIView+MountEvent.h"
#import "UIView+DirectionalLayout.h"
#import "HippyRenderUtils.h"

@implementation HippyCustomScrollView

Expand Down Expand Up @@ -661,7 +662,7 @@ - (CGSize)contentSize {

- (void)hippyBridgeDidFinishTransaction {
CGSize contentSize = self.contentSize;
if (!CGSizeEqualToSize(_scrollView.contentSize, contentSize)) {
if (!HippyCGSizeRoundInPixelNearlyEqual(_scrollView.contentSize, contentSize)) {
// When contentSize is set manually, ScrollView internals will reset
// contentOffset to {0, 0}. Since we potentially set contentSize whenever
// anything in the ScrollView updates, we workaround this issue by manually
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#import "HippyUtils.h"
#import "HippyTextSelection.h"
#import "UIView+Hippy.h"
#import "HippyRenderUtils.h"

@implementation HippyUITextView

Expand Down Expand Up @@ -290,7 +291,8 @@ - (void)updateContentSize {
CGSize contentSize = (CGSize) { CGRectGetMaxX(_scrollView.frame), INFINITY };
contentSize.height = [_textView sizeThatFits:contentSize].height;

if (_viewDidCompleteInitialLayout && _onContentSizeChange && !CGSizeEqualToSize(_previousContentSize, contentSize)) {
if (_viewDidCompleteInitialLayout && _onContentSizeChange
&& !HippyCGSizeRoundInPixelNearlyEqual(_previousContentSize, contentSize)) {
_previousContentSize = contentSize;
_onContentSizeChange(@{
@"contentSize": @ {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#import "UIView+Hippy.h"
#import "HippyShadowView+Internal.h"
#import "HippyAssert.h"
#import "HippyRenderUtils.h"


static NSString *const HippyBackgroundColorPropKey = @"backgroundColor";
Expand Down Expand Up @@ -286,7 +287,7 @@ - (void)setLayoutFrame:(CGRect)frame {

- (void)setLayoutFrame:(CGRect)frame dirtyPropagation:(BOOL)dirtyPropagation {
CGRect currentFrame = self.frame;
if (CGRectEqualToRect(currentFrame, frame)) {
if (HippyCGRectRoundInPixelNearlyEqual(currentFrame, frame)) {
return;
}
[self setFrame:frame];
Expand Down
5 changes: 3 additions & 2 deletions renderer/native/ios/renderer/component/view/HippyView.m
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#import "HippyView.h"
#import "UIView+DomEvent.h"
#import "UIView+Hippy.h"
#import "HippyRenderUtils.h"

static CGSize makeSizeConstrainWithType(CGSize originSize, CGSize constrainSize, NSString *resizeMode) {
// width / height
Expand Down Expand Up @@ -169,7 +170,7 @@ - (void)hippySetFrame:(CGRect)frame {
// TODO: detect up-front if re-rendering is necessary
CGSize oldSize = self.bounds.size;
[super hippySetFrame:frame];
if (!CGSizeEqualToSize(self.bounds.size, oldSize)) {
if (!HippyCGSizeNearlyEqual(self.bounds.size, oldSize)) {
[self.layer setNeedsDisplay];
}
}
Expand Down Expand Up @@ -235,7 +236,7 @@ - (CALayerContentsFilter)magnificationFilter {
}

- (void)displayLayer:(CALayer *)layer {
if (CGSizeEqualToSize(layer.bounds.size, CGSizeZero)) {
if (HippyCGSizeNearlyEqual(layer.bounds.size, CGSizeZero)) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
#import "UIView+DirectionalLayout.h"
#import "UIView+MountEvent.h"
#import "HippyLog.h"
#import "HippyRenderUtils.h"

#include "float.h"

@interface HippyViewPager ()
@property (nonatomic, strong) NSMutableArray<UIView *> *viewPagerItems;
Expand Down Expand Up @@ -435,8 +435,8 @@ - (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated {
}

- (void)hippyBridgeDidFinishTransaction {
BOOL isFrameEqual = CGRectEqualToRect(self.frame, self.previousFrame);
BOOL isContentSizeEqual = CGSizeEqualToSize(self.contentSize, self.previousSize);
BOOL isFrameEqual = HippyCGRectRoundInPixelNearlyEqual(self.frame, self.previousFrame);
BOOL isContentSizeEqual = HippyCGSizeRoundInPixelNearlyEqual(self.contentSize, self.previousSize);
if (!isContentSizeEqual || !isFrameEqual) {
self.previousFrame = self.frame;
self.previousSize = self.contentSize;
Expand Down Expand Up @@ -474,7 +474,7 @@ - (void)layoutSubviews {

CGSize updatedSize = CGSizeMake(lastViewPagerItem.frame.origin.x + lastViewPagerItem.frame.size.width,
lastViewPagerItem.frame.origin.y + lastViewPagerItem.frame.size.height);
if (!CGSizeEqualToSize(self.contentSize, updatedSize)) {
if (!HippyCGSizeRoundInPixelNearlyEqual(self.contentSize, updatedSize)) {
self.contentSize = updatedSize;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*/

#import "HippyShadowWaterfallItem.h"
#import "HippyRenderUtils.h"

@implementation HippyShadowWaterfallItem

Expand All @@ -35,7 +36,7 @@ - (instancetype)init {
- (void)setFrame:(CGRect)frame {
CGRect originFrame = self.frame;
[super setFrame:frame];
if (!CGSizeEqualToSize(originFrame.size, frame.size) &&
if (!HippyCGSizeRoundInPixelNearlyEqual(originFrame.size, frame.size) &&
[self.observer respondsToSelector:@selector(itemFrameChanged:)]) {
[self.observer itemFrameChanged:self];
}
Expand Down
11 changes: 9 additions & 2 deletions renderer/native/ios/utils/HippyRenderUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,18 @@ HIPPY_EXTERN CGFloat HippyRoundPixelValue(CGFloat value);
HIPPY_EXTERN CGFloat HippyCeilPixelValue(CGFloat value);
HIPPY_EXTERN CGFloat HippyFloorPixelValue(CGFloat value);

// Convert a size in points to pixels, rounded up to the nearest integral size
HIPPY_EXTERN CGSize HippySizeInPixels(CGSize pointSize, CGFloat scale);
/// Convert a size in points to pixels, rounded up to the nearest integral size
FOUNDATION_EXTERN CGSize HippySizeCeilInPixels(CGSize pointSize, CGFloat scale);
/// Convert a size in points to pixels, rounded to the nearest integral size
FOUNDATION_EXTERN CGSize HippySizeRoundInPixels(CGSize pointSize, CGFloat scale);

HIPPY_EXTERN BOOL HippyCGRectNearlyEqual(CGRect frame1, CGRect frame2);
HIPPY_EXTERN BOOL HippyCGPointNearlyEqual(CGPoint point1, CGPoint point2);
HIPPY_EXTERN BOOL HippyCGSizeNearlyEqual(CGSize size1, CGSize size2);

/// First convert size in points to pixels by HippySizeRoundInPixels, then compare.
HIPPY_EXTERN BOOL HippyCGSizeRoundInPixelNearlyEqual(CGSize size1, CGSize size2);
HIPPY_EXTERN BOOL HippyCGRectRoundInPixelNearlyEqual(CGRect frame1, CGRect frame2);
HIPPY_EXTERN BOOL HippyCGPointRoundInPixelNearlyEqual(CGPoint point1, CGPoint point2);

NS_ASSUME_NONNULL_END
51 changes: 43 additions & 8 deletions renderer/native/ios/utils/HippyRenderUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@
#import "HippyUtils.h"
#import "HippyRenderUtils.h"

// Use global variable to facilitate unit test
CGFloat gHippyScreenScaleValue = CGFLOAT_MAX;

CGFloat HippyScreenScale(void) {
static CGFloat scale = CGFLOAT_MAX;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
scale = [UIScreen mainScreen].scale;
gHippyScreenScaleValue = [UIScreen mainScreen].scale;
});
return scale;
return gHippyScreenScaleValue;
}

CGSize HippyScreenSize(void) {
Expand All @@ -56,24 +58,57 @@ CGFloat HippyFloorPixelValue(CGFloat value) {
return floor(value * scale) / scale;
}

CGSize HippySizeInPixels(CGSize pointSize, CGFloat scale) {
CGSize HippySizeCeilInPixels(CGSize pointSize, CGFloat scale) {
return (CGSize) {
ceil(pointSize.width * scale),
ceil(pointSize.height * scale),
};
}

CGSize HippySizeRoundInPixels(CGSize pointSize, CGFloat scale) {
return (CGSize) {
round(pointSize.width * scale),
round(pointSize.height * scale),
};
}

BOOL HippyCGRectNearlyEqual(CGRect frame1, CGRect frame2) {
return HippyCGPointNearlyEqual(frame1.origin, frame2.origin) &&
HippyCGSizeNearlyEqual(frame1.size, frame2.size);
}

BOOL HippyCGPointNearlyEqual(CGPoint point1, CGPoint point2) {
return fabs(point1.x - point2.x) < CGFLOAT_EPSILON &&
fabs(point1.y - point2.y) < CGFLOAT_EPSILON;
return fabs(point1.x - point2.x) < 3 * CGFLOAT_EPSILON &&
fabs(point1.y - point2.y) < 3 * CGFLOAT_EPSILON;
}

BOOL HippyCGSizeNearlyEqual(CGSize size1, CGSize size2) {
return fabs(size1.width - size2.width) < CGFLOAT_EPSILON &&
fabs(size1.height - size2.height) < CGFLOAT_EPSILON;
return fabs(size1.width - size2.width) < 3 * CGFLOAT_EPSILON &&
fabs(size1.height - size2.height) < 3 * CGFLOAT_EPSILON;
}

BOOL HippyCGSizeRoundInPixelNearlyEqual(CGSize size1, CGSize size2) {
CGFloat scale = HippyScreenScale();
CGSize sizeA = HippySizeRoundInPixels(size1, scale);
CGSize sizeB = HippySizeRoundInPixels(size2, scale);
return HippyCGSizeNearlyEqual(sizeA,sizeB);
}

BOOL HippyCGPointRoundInPixelNearlyEqual(CGPoint point1, CGPoint point2) {
CGFloat scale = HippyScreenScale();
CGPoint pointA = (CGPoint) {
round(point1.x * scale),
round(point1.y * scale),
};
CGPoint pointB = (CGPoint) {
round(point2.x * scale),
round(point2.y * scale),
};
return fabs(pointA.x - pointB.x) < CGFLOAT_EPSILON &&
fabs(pointA.y - pointB.y) < CGFLOAT_EPSILON;
}

BOOL HippyCGRectRoundInPixelNearlyEqual(CGRect frame1, CGRect frame2) {
return HippyCGPointRoundInPixelNearlyEqual(frame1.origin, frame2.origin) &&
HippyCGSizeRoundInPixelNearlyEqual(frame1.size, frame2.size);
}
Loading

0 comments on commit c911d56

Please sign in to comment.