-
Notifications
You must be signed in to change notification settings - Fork 24.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[iOS] Fabric: Fixes crash of dynamic color when light/dark mode changed #48496
Changes from 5 commits
795e684
adeafa9
a720b3d
8949170
3e72b4d
dad55b5
fabf86f
6329f2e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -6,6 +6,7 @@ | |||||||
*/ | ||||||||
|
||||||||
#import "HostPlatformColor.h" | ||||||||
#import "UIColor+Graphics.h" | ||||||||
|
||||||||
#import <Foundation/Foundation.h> | ||||||||
#import <UIKit/UIKit.h> | ||||||||
|
@@ -19,13 +20,32 @@ | |||||||
namespace facebook::react { | ||||||||
|
||||||||
namespace { | ||||||||
|
||||||||
bool UIColorIsP3ColorSpace(const std::shared_ptr<void> &uiColor) | ||||||||
{ | ||||||||
UIColor *color = unwrapManagedObject(uiColor); | ||||||||
CGColorSpaceRef colorSpace = CGColorGetColorSpace(color.CGColor); | ||||||||
|
||||||||
if (CGColorSpaceGetModel(colorSpace) == kCGColorSpaceModelRGB) { | ||||||||
CFStringRef name = CGColorSpaceGetName(colorSpace); | ||||||||
if (name != NULL && CFEqual(name, kCGColorSpaceDisplayP3)) { | ||||||||
return true; | ||||||||
} | ||||||||
} | ||||||||
return false; | ||||||||
} | ||||||||
|
||||||||
UIColor *_Nullable UIColorFromInt32(int32_t intColor) | ||||||||
{ | ||||||||
CGFloat a = CGFloat((intColor >> 24) & 0xFF) / 255.0; | ||||||||
CGFloat r = CGFloat((intColor >> 16) & 0xFF) / 255.0; | ||||||||
CGFloat g = CGFloat((intColor >> 8) & 0xFF) / 255.0; | ||||||||
CGFloat b = CGFloat(intColor & 0xFF) / 255.0; | ||||||||
return [UIColor colorWithRed:r green:g blue:b alpha:a]; | ||||||||
|
||||||||
UIColor *color = [UIColor colorWithRed:r green:g blue:b alpha:a]; | ||||||||
auto colorHash = facebook::react::hash_combine(intColor, 0); | ||||||||
color.reactHash = colorHash; | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
return color; | ||||||||
} | ||||||||
|
||||||||
UIColor *_Nullable UIColorFromDynamicColor(const facebook::react::DynamicColor &dynamicColor) | ||||||||
|
@@ -56,6 +76,8 @@ | |||||||
} | ||||||||
} | ||||||||
}]; | ||||||||
auto colorHash = facebook::react::hash_combine(dark, light, highContrastDark, highContrastLight, 0); | ||||||||
color.reactHash = colorHash; | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
return color; | ||||||||
} else { | ||||||||
return nil; | ||||||||
|
@@ -64,37 +86,96 @@ | |||||||
return nil; | ||||||||
} | ||||||||
|
||||||||
int32_t ColorFromUIColor(UIColor *color) | ||||||||
int32_t ColorFromColorComponents(const facebook::react::ColorComponents &components) | ||||||||
{ | ||||||||
float ratio = 255; | ||||||||
auto color = ((int32_t)round((float)components.alpha * ratio) & 0xff) << 24 | | ||||||||
((int)round((float)components.red * ratio) & 0xff) << 16 | | ||||||||
((int)round((float)components.green * ratio) & 0xff) << 8 | ((int)round((float)components.blue * ratio) & 0xff); | ||||||||
return color; | ||||||||
} | ||||||||
|
||||||||
int32_t ColorFromUIColor(UIColor *color) | ||||||||
{ | ||||||||
CGFloat rgba[4]; | ||||||||
[color getRed:&rgba[0] green:&rgba[1] blue:&rgba[2] alpha:&rgba[3]]; | ||||||||
return ((int32_t)round((float)rgba[3] * ratio) & 0xff) << 24 | ((int)round((float)rgba[0] * ratio) & 0xff) << 16 | | ||||||||
((int)round((float)rgba[1] * ratio) & 0xff) << 8 | ((int)round((float)rgba[2] * ratio) & 0xff); | ||||||||
return ColorFromColorComponents({(float)rgba[0], (float)rgba[1], (float)rgba[2], (float)rgba[3]}); | ||||||||
} | ||||||||
|
||||||||
int32_t ColorFromUIColor(const std::shared_ptr<void> &uiColor) | ||||||||
int32_t ColorFromUIColorForSpecificTraitCollection( | ||||||||
const std::shared_ptr<void> &uiColor, | ||||||||
UITraitCollection *traitCollection) | ||||||||
{ | ||||||||
UIColor *color = (UIColor *)unwrapManagedObject(uiColor); | ||||||||
if (color) { | ||||||||
UITraitCollection *currentTraitCollection = [UITraitCollection currentTraitCollection]; | ||||||||
color = [color resolvedColorWithTraitCollection:currentTraitCollection]; | ||||||||
color = [color resolvedColorWithTraitCollection:traitCollection]; | ||||||||
return ColorFromUIColor(color); | ||||||||
} | ||||||||
|
||||||||
return 0; | ||||||||
} | ||||||||
|
||||||||
int32_t ColorFromUIColor(const std::shared_ptr<void> &uiColor) | ||||||||
{ | ||||||||
return ColorFromUIColorForSpecificTraitCollection(uiColor, [UITraitCollection currentTraitCollection]); | ||||||||
} | ||||||||
|
||||||||
UIColor *_Nullable UIColorFromComponentsColor(const facebook::react::ColorComponents &components) | ||||||||
{ | ||||||||
UIColor *uiColor = nil; | ||||||||
if (components.colorSpace == ColorSpace::DisplayP3) { | ||||||||
return [UIColor colorWithDisplayP3Red:components.red | ||||||||
green:components.green | ||||||||
blue:components.blue | ||||||||
alpha:components.alpha]; | ||||||||
uiColor = [UIColor colorWithDisplayP3Red:components.red | ||||||||
green:components.green | ||||||||
blue:components.blue | ||||||||
alpha:components.alpha]; | ||||||||
} else { | ||||||||
uiColor = [UIColor colorWithRed:components.red green:components.green blue:components.blue alpha:components.alpha]; | ||||||||
} | ||||||||
|
||||||||
auto color = ColorFromColorComponents(components); | ||||||||
auto colorHash = facebook::react::hash_combine(color, components.colorSpace == ColorSpace::DisplayP3); | ||||||||
uiColor.reactHash = colorHash; | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
|
||||||||
return uiColor; | ||||||||
} | ||||||||
|
||||||||
int32_t hashFromUIColor(const std::shared_ptr<void> &uiColor) | ||||||||
{ | ||||||||
if (uiColor == nullptr) { | ||||||||
return 0; | ||||||||
} | ||||||||
return [UIColor colorWithRed:components.red green:components.green blue:components.blue alpha:components.alpha]; | ||||||||
|
||||||||
static UITraitCollection *darkModeTraitCollection = | ||||||||
[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark]; | ||||||||
auto darkColor = ColorFromUIColorForSpecificTraitCollection(uiColor, darkModeTraitCollection); | ||||||||
|
||||||||
static UITraitCollection *lightModeTraitCollection = | ||||||||
[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight]; | ||||||||
auto lightColor = ColorFromUIColorForSpecificTraitCollection(uiColor, lightModeTraitCollection); | ||||||||
|
||||||||
static UITraitCollection *darkModeAccessibilityContrastTraitCollection = | ||||||||
[UITraitCollection traitCollectionWithTraitsFromCollections:@[ | ||||||||
darkModeTraitCollection, | ||||||||
[UITraitCollection traitCollectionWithAccessibilityContrast:UIAccessibilityContrastHigh] | ||||||||
]]; | ||||||||
auto darkAccessibilityContrastColor = | ||||||||
ColorFromUIColorForSpecificTraitCollection(uiColor, darkModeAccessibilityContrastTraitCollection); | ||||||||
|
||||||||
static UITraitCollection *lightModeAccessibilityContrastTraitCollection = | ||||||||
[UITraitCollection traitCollectionWithTraitsFromCollections:@[ | ||||||||
lightModeTraitCollection, | ||||||||
[UITraitCollection traitCollectionWithAccessibilityContrast:UIAccessibilityContrastHigh] | ||||||||
]]; | ||||||||
auto lightAccessibilityContrastColor = | ||||||||
ColorFromUIColorForSpecificTraitCollection(uiColor, lightModeAccessibilityContrastTraitCollection); | ||||||||
return facebook::react::hash_combine( | ||||||||
darkColor, | ||||||||
lightColor, | ||||||||
darkAccessibilityContrastColor, | ||||||||
lightAccessibilityContrastColor, | ||||||||
UIColorIsP3ColorSpace(uiColor)); | ||||||||
} | ||||||||
|
||||||||
} // anonymous namespace | ||||||||
|
||||||||
Color::Color(int32_t color) | ||||||||
|
@@ -114,14 +195,20 @@ int32_t ColorFromUIColor(const std::shared_ptr<void> &uiColor) | |||||||
|
||||||||
Color::Color(std::shared_ptr<void> uiColor) | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems |
||||||||
{ | ||||||||
UIColor *color = ((UIColor *)unwrapManagedObject(uiColor)); | ||||||||
if (color && color.reactHash == 0) { | ||||||||
auto colorHash = hashFromUIColor(uiColor); | ||||||||
color.reactHash = colorHash; | ||||||||
} | ||||||||
uiColor_ = std::move(uiColor); | ||||||||
} | ||||||||
|
||||||||
bool Color::operator==(const Color &other) const | ||||||||
{ | ||||||||
return (!uiColor_ && !other.uiColor_) || | ||||||||
(uiColor_ && other.uiColor_ && | ||||||||
[unwrapManagedObject(getUIColor()) isEqual:unwrapManagedObject(other.getUIColor())]); | ||||||||
((UIColor *)unwrapManagedObject(getUIColor())).reactHash == | ||||||||
((UIColor *)unwrapManagedObject(other.getUIColor())).reactHash); | ||||||||
} | ||||||||
|
||||||||
bool Color::operator!=(const Color &other) const | ||||||||
|
@@ -142,6 +229,11 @@ int32_t ColorFromUIColor(const std::shared_ptr<void> &uiColor) | |||||||
return static_cast<float>(rgba[channelId]); | ||||||||
} | ||||||||
|
||||||||
int32_t Color::getUIColorHash() const | ||||||||
{ | ||||||||
return [(UIColor *)unwrapManagedObject(uiColor_) reactHash]; | ||||||||
} | ||||||||
|
||||||||
} // namespace facebook::react | ||||||||
|
||||||||
NS_ASSUME_NONNULL_END |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
#pragma once | ||
|
||
#import <UIKit/UIKit.h> | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@interface UIColor (Graphics) | ||
@property (nonatomic, assign) int32_t reactHash; | ||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
#import <objc/runtime.h> | ||
#import "UIColor+Graphics.h" | ||
|
||
@implementation UIColor (Graphics) | ||
|
||
- (int32_t)reactHash | ||
{ | ||
return [objc_getAssociatedObject(self, _cmd) intValue]; | ||
} | ||
|
||
- (void)setReactHash:(int32_t)reactHash | ||
{ | ||
objc_setAssociatedObject(self, @selector(reactHash), @(reactHash), OBJC_ASSOCIATION_RETAIN_NONATOMIC); | ||
} | ||
|
||
@end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@javache No value in calling hash_combine again with seed 0.