Skip to content

Commit

Permalink
[iOS] Add Fabric dynamic color support
Browse files Browse the repository at this point in the history
  • Loading branch information
zhongwuzw committed Jan 15, 2024
1 parent f30acc6 commit 8911682
Show file tree
Hide file tree
Showing 11 changed files with 291 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,15 @@ - (void)setBackgroundColor:(UIColor *)backgroundColor
_backgroundColor = backgroundColor;
}

- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
{
[super traitCollectionDidChange:previousTraitCollection];

if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) {
[self invalidateLayer];
}
}

#pragma mark - RCTComponentViewProtocol

+ (ComponentDescriptorProvider)componentDescriptorProvider
Expand Down Expand Up @@ -597,6 +606,8 @@ - (void)invalidateLayer
borderMetrics.borderWidths.left == 0 ||
colorComponentsFromColor(borderMetrics.borderColors.left).alpha == 0 || self.clipsToBounds);

CGColorRef backgroundColor = [_backgroundColor resolvedColorWithTraitCollection:self.traitCollection].CGColor;

if (useCoreAnimationBorderRendering) {
layer.mask = nil;
if (_borderLayer) {
Expand All @@ -612,7 +623,7 @@ - (void)invalidateLayer

layer.cornerCurve = CornerCurveFromBorderCurve(borderMetrics.borderCurves.topLeft);

layer.backgroundColor = _backgroundColor.CGColor;
layer.backgroundColor = backgroundColor;
} else {
if (!_borderLayer) {
_borderLayer = [CALayer new];
Expand All @@ -635,7 +646,7 @@ - (void)invalidateLayer
RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii),
RCTUIEdgeInsetsFromEdgeInsets(borderMetrics.borderWidths),
borderColors,
_backgroundColor.CGColor,
backgroundColor,
self.clipsToBounds);

RCTReleaseRCTBorderColors(borderColors);
Expand Down
20 changes: 2 additions & 18 deletions packages/react-native/React/Fabric/RCTConversions.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#import <react/renderer/components/view/primitives.h>
#import <react/renderer/core/LayoutPrimitives.h>
#import <react/renderer/graphics/Color.h>
#import <react/renderer/graphics/RCTPlatformColorUtils.h>
#import <react/renderer/graphics/Transform.h>

NS_ASSUME_NONNULL_BEGIN
Expand All @@ -36,24 +37,7 @@ inline std::string RCTStringFromNSString(NSString *string)

inline UIColor *_Nullable RCTUIColorFromSharedColor(const facebook::react::SharedColor &sharedColor)
{
if (!sharedColor) {
return nil;
}

if (*facebook::react::clearColor() == *sharedColor) {
return [UIColor clearColor];
}

if (*facebook::react::blackColor() == *sharedColor) {
return [UIColor blackColor];
}

if (*facebook::react::whiteColor() == *sharedColor) {
return [UIColor whiteColor];
}

auto components = facebook::react::colorComponentsFromColor(sharedColor);
return [UIColor colorWithRed:components.red green:components.green blue:components.blue alpha:components.alpha];
return RCTPlatformColorFromColor(*sharedColor);
}

inline CF_RETURNS_RETAINED CGColorRef _Nullable RCTCreateCGColorRefFromSharedColor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ class SharedColor {

SharedColor(Color color) : color_(color) {}

Color operator*() const {
Color& operator*() {
return color_;
}

const Color& operator*() const {
return color_;
}

Expand Down Expand Up @@ -61,7 +65,7 @@ SharedColor whiteColor();

template <>
struct std::hash<facebook::react::SharedColor> {
size_t operator()(facebook::react::SharedColor color) const {
return std::hash<decltype(*color)>{}(*color);
size_t operator()(const facebook::react::SharedColor& color) const {
return std::hash<facebook::react::Color>{}(*color);
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Pod::Spec.new do |s|
"\"$(PODS_ROOT)/boost\"",
"\"$(PODS_TARGET_SRCROOT)/../../../\"",
"\"$(PODS_ROOT)/RCT-Folly\"",
"\"$(PODS_ROOT)/DoubleConversion\"",
"\"$(PODS_ROOT)/fmt/include\""
]

s.name = "React-graphics"
Expand Down Expand Up @@ -60,4 +62,6 @@ Pod::Spec.new do |s|
s.dependency "RCT-Folly/Fabric", folly_version
s.dependency "React-Core/Default", version
s.dependency "React-utils"
s.dependency "DoubleConversion"
s.dependency "fmt", "9.1.0"
end
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,65 @@
#pragma once

#include <react/renderer/graphics/ColorComponents.h>
#include <react/utils/hash_combine.h>
#include <cmath>

namespace facebook::react {

using Color = int32_t;
struct DynamicColor {
int32_t lightColor = 0;
int32_t darkColor = 0;
int32_t highContrastLightColor = 0;
int32_t highContrastDarkColor = 0;
};

struct Color {
Color(int32_t color);
Color(const DynamicColor& dynamicColor);
Color(const ColorComponents& components);
Color(std::shared_ptr<void> uiColor);
int32_t getColor() const;
std::shared_ptr<void> getUIColor() const {
return uiColor_;
}
ColorComponents getColorComponents() const {
float ratio = 255;
int32_t primitiveColor = getColor();
return ColorComponents{
(float)((primitiveColor >> 16) & 0xff) / ratio,
(float)((primitiveColor >> 8) & 0xff) / ratio,
(float)((primitiveColor >> 0) & 0xff) / ratio,
(float)((primitiveColor >> 24) & 0xff) / ratio};
}
bool operator==(const Color& other) const;
bool operator!=(const Color& other) const;
operator int32_t() const {
return getColor();
}

private:
std::shared_ptr<void> uiColor_;
};

namespace HostPlatformColor {
static const facebook::react::Color UndefinedColor =
std::numeric_limits<facebook::react::Color>::max();
static const facebook::react::Color UndefinedColor = Color(nullptr);
}

inline Color hostPlatformColorFromComponents(ColorComponents components) {
float ratio = 255;
return ((int)round(components.alpha * ratio) & 0xff) << 24 |
((int)round(components.red * ratio) & 0xff) << 16 |
((int)round(components.green * ratio) & 0xff) << 8 |
((int)round(components.blue * ratio) & 0xff);
return Color(components);
}

inline ColorComponents colorComponentsFromHostPlatformColor(Color color) {
float ratio = 255;
return ColorComponents{
(float)((color >> 16) & 0xff) / ratio,
(float)((color >> 8) & 0xff) / ratio,
(float)((color >> 0) & 0xff) / ratio,
(float)((color >> 24) & 0xff) / ratio};
return color.getColorComponents();
}

} // namespace facebook::react

template <>
struct std::hash<facebook::react::Color> {
size_t operator()(const facebook::react::Color& color) const {
auto seed = size_t{0};
facebook::react::hash_combine(seed, color.getColor());
return seed;
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* 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 "HostPlatformColor.h"

#import <Foundation/Foundation.h>
#import <React/RCTConvert.h>
#import <UIKit/UIKit.h>
#import <react/utils/ManagedObjectWrapper.h>
#import <string>

using namespace facebook::react;

NS_ASSUME_NONNULL_BEGIN

namespace facebook::react {

namespace {
UIColor *_Nullable UIColorFromDynamicColor(const facebook::react::DynamicColor &dynamicColor)
{
int32_t light = dynamicColor.lightColor;
int32_t dark = dynamicColor.darkColor;
int32_t highContrastLight = dynamicColor.highContrastLightColor;
int32_t highContrastDark = dynamicColor.highContrastDarkColor;

UIColor *lightColor = [RCTConvert UIColor:@(light)];
UIColor *darkColor = [RCTConvert UIColor:@(dark)];
UIColor *highContrastLightColor = [RCTConvert UIColor:@(highContrastLight)];
UIColor *highContrastDarkColor = [RCTConvert UIColor:@(highContrastDark)];

if (lightColor != nil && darkColor != nil) {
UIColor *color = [UIColor colorWithDynamicProvider:^UIColor *_Nonnull(UITraitCollection *_Nonnull collection) {
if (collection.userInterfaceStyle == UIUserInterfaceStyleDark) {
if (collection.accessibilityContrast == UIAccessibilityContrastHigh && highContrastDarkColor != nil) {
return highContrastDarkColor;
} else {
return darkColor;
}
} else {
if (collection.accessibilityContrast == UIAccessibilityContrastHigh && highContrastLightColor != nil) {
return highContrastLightColor;
} else {
return lightColor;
}
}
}];
return color;
} else {
return nil;
}

return nil;
}

int32_t ColorFromUIColor(UIColor *color)
{
float ratio = 255;
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);
}

int32_t ColorFromUIColor(const std::shared_ptr<void> &uiColor)
{
UIColor *color = (UIColor *)unwrapManagedObject(uiColor);
if (color) {
UITraitCollection *currentTraitCollection = [UITraitCollection currentTraitCollection];
color = [color resolvedColorWithTraitCollection:currentTraitCollection];
return ColorFromUIColor(color);
}

return 0;
}

UIColor *_Nullable UIColorFromComponentsColor(const facebook::react::ColorComponents &components)
{
return [UIColor colorWithRed:components.red green:components.green blue:components.blue alpha:components.alpha];
}
} // anonymous namespace

Color::Color(int32_t color)
{
uiColor_ = wrapManagedObject([RCTConvert UIColor:@(color)]);
}

Color::Color(const DynamicColor &dynamicColor)
{
uiColor_ = wrapManagedObject(UIColorFromDynamicColor(dynamicColor));
}

Color::Color(const ColorComponents &components)
{
uiColor_ = wrapManagedObject(UIColorFromComponentsColor(components));
}

Color::Color(std::shared_ptr<void> uiColor)
{
uiColor_ = std::move(uiColor);
}

bool Color::operator==(const Color &other) const
{
return (!uiColor_ && !other.uiColor_) ||
(uiColor_ && other.uiColor_ &&
[unwrapManagedObject(getUIColor()) isEqual:unwrapManagedObject(other.getUIColor())]);
}

bool Color::operator!=(const Color &other) const
{
return !(*this == other);
}

int32_t Color::getColor() const
{
return ColorFromUIColor(uiColor_);
}
} // namespace facebook::react

NS_ASSUME_NONNULL_END
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,15 @@

#pragma once

#include <react/renderer/core/PropsParserContext.h>
#include <react/renderer/core/RawProps.h>
#include <react/renderer/graphics/Color.h>
#include <react/renderer/graphics/RCTPlatformColorUtils.h>
#include <unordered_map>

namespace facebook::react {

inline SharedColor parsePlatformColor(
const PropsParserContext& context,
const RawValue& value) {
if (value.hasType<std::unordered_map<std::string, RawValue>>()) {
auto items = (std::unordered_map<std::string, RawValue>)value;
if (items.find("semantic") != items.end() &&
items.at("semantic").hasType<std::vector<std::string>>()) {
auto semanticItems = (std::vector<std::string>)items.at("semantic");
return {colorFromComponents(
RCTPlatformColorComponentsFromSemanticItems(semanticItems))};
}
}
class RawValue;
struct PropsParserContext;

return clearColor();
}
SharedColor parsePlatformColor(
const PropsParserContext& context,
const RawValue& value);

} // namespace facebook::react
Loading

0 comments on commit 8911682

Please sign in to comment.