From 5bff60fe0d6d8dfc4f493cb4ecb8b9c8145b432a Mon Sep 17 00:00:00 2001 From: Tom Underhill Date: Thu, 22 Aug 2019 09:17:31 -0700 Subject: [PATCH] ios 13 semantic and dynamic color support (#105) * Got RNTester building using XCode 11 * Updated JS to enable dark mode options on ios as well as mac. * Initial port of RCTConvert * Enable light/dark change reaction for background colors. * Updated semantic colors for ios. * Get macos target and ios target building again on XCode 10. * Added DarkModeExample * Implemented ios color fallbacks. * Fallbacks working in iOS 12 * Cleanup * Fixed target version back to 9 * Added Semantic color list to RNTester page. * Refactor to use RCTPlatformDisplayLink instead of CADisplayLink as CADisplayLink is marked unavaible on Mac as of XCode 11. * Fixed mac integation tests * Fixed format-check issue * Removed unused code from js. Updated comments in objC code. * Removed UIColor alias and defined RCTUIColor for mac and ios. --- Libraries/ART/ARTSurfaceView.m | 4 +- Libraries/Color/normalizeColor.js | 8 +- .../RCTNativeAnimatedNodesManager.h | 2 +- .../RCTNativeAnimatedNodesManager.m | 6 +- .../RCTTest/FBSnapshotTestCase/UIImage+Diff.m | 2 +- Libraries/StyleSheet/processColor.js | 3 +- Libraries/Text/RCTTextAttributes.h | 12 +- Libraries/Text/RCTTextAttributes.m | 12 +- Libraries/Text/Text/RCTTextView.m | 6 +- .../Multiline/RCTMultilineTextInputView.m | 6 +- .../Text/TextInput/Multiline/RCTUITextView.h | 4 +- .../Text/TextInput/Multiline/RCTUITextView.m | 12 +- .../RCTBackedTextInputViewProtocol.h | 4 +- .../TextInput/Singleline/RCTUITextField.h | 4 +- .../TextInput/Singleline/RCTUITextField.m | 14 +- RNTester/RNTester.xcodeproj/project.pbxproj | 4 + .../RCTConvert_UIColorTests.m | 200 ++++++++++++++++++ .../RCTNativeAnimatedNodesManagerTests.m | 2 +- RNTester/js/DarkModeExample.js | 181 ++++++++++++++++ RNTester/js/RNTesterApp.ios.js | 5 +- RNTester/js/RNTesterBlock.js | 42 +++- RNTester/js/RNTesterExampleFilter.js | 16 +- RNTester/js/RNTesterExampleList.js | 20 ++ RNTester/js/RNTesterList.ios.js | 5 + RNTester/js/RNTesterList.macos.js | 5 + RNTester/js/RNTesterPage.js | 13 +- React/Base/RCTConvert.h | 4 +- React/Base/RCTConvert.m | 179 +++++++++++++--- React/Base/RCTDisplayLink.m | 7 +- React/Base/RCTFrameUpdate.h | 5 +- React/Base/RCTFrameUpdate.m | 2 +- React/Base/RCTModuleMethod.mm | 1 + React/Base/RCTPlatformDisplayLink.h | 3 +- React/Base/RCTUIKit.h | 4 +- React/Base/RCTUtils.h | 3 + React/Base/RCTUtils.m | 2 +- React/DevSupport/RCTDevLoadingView.h | 2 +- React/DevSupport/RCTDevLoadingView.m | 14 +- React/Views/RCTActivityIndicatorView.h | 2 +- React/Views/RCTActivityIndicatorView.m | 2 +- React/Views/RCTBorderDrawing.m | 2 +- React/Views/RCTPicker.h | 2 +- React/Views/RCTPicker.m | 2 +- React/Views/RCTProgressView.h | 4 +- React/Views/RCTProgressView.m | 2 +- React/Views/RCTView.m | 38 +++- 46 files changed, 755 insertions(+), 117 deletions(-) create mode 100644 RNTester/RNTesterUnitTests/RCTConvert_UIColorTests.m create mode 100644 RNTester/js/DarkModeExample.js diff --git a/Libraries/ART/ARTSurfaceView.m b/Libraries/ART/ARTSurfaceView.m index e1ba02ffb4bfc0..8fbbda8ace58ab 100644 --- a/Libraries/ART/ARTSurfaceView.m +++ b/Libraries/ART/ARTSurfaceView.m @@ -47,9 +47,9 @@ - (void)invalidate - (void)drawRect:(CGRect)rect { -#if TARGET_OS_OSX // [TODO(macOS ISS#2323203) +// [TODO(OSS Candidate ISS#2710739): for macOS and iOS dark mode [super drawRect:rect]; -#endif // ]TODO(macOS ISS#2323203) +// ]TODO(OSS Candidate ISS#2710739) CGContextRef context = UIGraphicsGetCurrentContext(); for (ARTNode *node in self.subviews) { [node renderTo:context]; diff --git a/Libraries/Color/normalizeColor.js b/Libraries/Color/normalizeColor.js index 83df4162017f46..d3985cd12c6c01 100755 --- a/Libraries/Color/normalizeColor.js +++ b/Libraries/Color/normalizeColor.js @@ -38,8 +38,12 @@ function normalizeColor( return null; } - if (typeof color === 'object' && color !== null && Platform.OS === 'macos') { - // [TODO(macOS ISS#2323203) + // [TODO(macOS ISS#2323203) + if ( + typeof color === 'object' && + color !== null && + (Platform.OS === 'macos' || Platform.OS === 'ios') + ) { if ('semantic' in color) { // a macos semantic color return color; diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h index d5ba6128a0375d..0f92b579b3b04b 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h +++ b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h @@ -20,7 +20,7 @@ - (void)updateAnimations; -- (void)stepAnimations:(nonnull CADisplayLink *)displaylink; +- (void)stepAnimations:(nonnull RCTPlatformDisplayLink *)displaylink; // TODO(macOS ISS#2323203) // graph diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m index 30ec0e42dafc05..e4b8b0fce5cde3 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m +++ b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m @@ -36,7 +36,7 @@ @implementation RCTNativeAnimatedNodesManager // there will be only one driver per mapping so all code code should be optimized around that. NSMutableDictionary *> *_eventDrivers; NSMutableSet> *_activeAnimations; - CADisplayLink *_displayLink; + RCTPlatformDisplayLink *_displayLink; // TODO(macOS ISS#2323203) } - (instancetype)initWithUIManager:(nonnull RCTUIManager *)uiManager @@ -388,7 +388,7 @@ - (void)stopListeningToAnimatedNodeValue:(nonnull NSNumber *)tag - (void)startAnimationLoopIfNeeded { if (!_displayLink && _activeAnimations.count > 0) { - _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(stepAnimations:)]; + _displayLink = [RCTPlatformDisplayLink displayLinkWithTarget:self selector:@selector(stepAnimations:)]; // TODO(macOS ISS#2323203) [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; } } @@ -408,7 +408,7 @@ - (void)stopAnimationLoop } } -- (void)stepAnimations:(CADisplayLink *)displaylink +- (void)stepAnimations:(RCTPlatformDisplayLink *)displaylink // TODO(macOS ISS#2323203) { NSTimeInterval time = displaylink.timestamp; for (id animationDriver in _activeAnimations) { diff --git a/Libraries/RCTTest/FBSnapshotTestCase/UIImage+Diff.m b/Libraries/RCTTest/FBSnapshotTestCase/UIImage+Diff.m index 5e9f06c028f889..0c244bdc3cc4a9 100644 --- a/Libraries/RCTTest/FBSnapshotTestCase/UIImage+Diff.m +++ b/Libraries/RCTTest/FBSnapshotTestCase/UIImage+Diff.m @@ -50,7 +50,7 @@ - (UIImage *)diffWithImage:(UIImage *)image CGContextBeginTransparencyLayer(context, NULL); [image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)]; CGContextSetBlendMode(context, kCGBlendModeDifference); - CGContextSetFillColorWithColor(context,[UIColor whiteColor].CGColor); + CGContextSetFillColorWithColor(context,[RCTUIColor whiteColor].CGColor); // TODO(OSS Candidate ISS#2710739) CGContextFillRect(context, CGRectMake(0, 0, self.size.width, self.size.height)); CGContextEndTransparencyLayer(context); UIImage *returnImage = UIGraphicsGetImageFromCurrentImageContext(); diff --git a/Libraries/StyleSheet/processColor.js b/Libraries/StyleSheet/processColor.js index b369267f66b6d0..b49afd5d7b71c1 100644 --- a/Libraries/StyleSheet/processColor.js +++ b/Libraries/StyleSheet/processColor.js @@ -28,9 +28,10 @@ function processColor( return undefined; } + // [TODO(macOS ISS#2323203) if ( typeof int32Color === 'object' && - Platform.OS === 'macos' /* [TODO(macOS ISS#2323203) */ + (Platform.OS === 'macos' || Platform.OS === 'ios') ) { if ('dynamic' in int32Color && int32Color.dynamic !== undefined) { const dynamic = int32Color.dynamic; diff --git a/Libraries/Text/RCTTextAttributes.h b/Libraries/Text/RCTTextAttributes.h index 5c65ed9eafa326..ed6ed2c68ab4db 100644 --- a/Libraries/Text/RCTTextAttributes.h +++ b/Libraries/Text/RCTTextAttributes.h @@ -24,8 +24,8 @@ extern NSString *const RCTTextAttributesTagAttributeName; @interface RCTTextAttributes : NSObject // Color -@property (nonatomic, strong, nullable) UIColor *foregroundColor; -@property (nonatomic, strong, nullable) UIColor *backgroundColor; +@property (nonatomic, strong, nullable) RCTUIColor *foregroundColor; // TODO(OSS Candidate ISS#2710739) +@property (nonatomic, strong, nullable) RCTUIColor *backgroundColor; // TODO(OSS Candidate ISS#2710739) @property (nonatomic, assign) CGFloat opacity; // Font @property (nonatomic, copy, nullable) NSString *fontFamily; @@ -42,13 +42,13 @@ extern NSString *const RCTTextAttributesTagAttributeName; @property (nonatomic, assign) NSTextAlignment alignment; @property (nonatomic, assign) NSWritingDirection baseWritingDirection; // Decoration -@property (nonatomic, strong, nullable) UIColor *textDecorationColor; +@property (nonatomic, strong, nullable) RCTUIColor *textDecorationColor; // TODO(OSS Candidate ISS#2710739) @property (nonatomic, assign) NSUnderlineStyle textDecorationStyle; @property (nonatomic, assign) RCTTextDecorationLineType textDecorationLine; // Shadow @property (nonatomic, assign) CGSize textShadowOffset; @property (nonatomic, assign) CGFloat textShadowRadius; -@property (nonatomic, strong, nullable) UIColor *textShadowColor; +@property (nonatomic, strong, nullable) RCTUIColor *textShadowColor; // TODO(OSS Candidate ISS#2710739) // Special @property (nonatomic, assign) BOOL isHighlighted; @property (nonatomic, strong, nullable) NSNumber *tag; @@ -79,8 +79,8 @@ extern NSString *const RCTTextAttributesTagAttributeName; /** * Foreground and background colors with opacity and right defaults. */ -- (UIColor *)effectiveForegroundColor; -- (UIColor *)effectiveBackgroundColor; +- (RCTUIColor *)effectiveForegroundColor; // TODO(OSS Candidate ISS#2710739) +- (RCTUIColor *)effectiveBackgroundColor; // TODO(OSS Candidate ISS#2710739) /** * Text transformed per 'none', 'uppercase', 'lowercase', 'capitalize' diff --git a/Libraries/Text/RCTTextAttributes.m b/Libraries/Text/RCTTextAttributes.m index f1bf6beab67f1a..385b88106c3505 100644 --- a/Libraries/Text/RCTTextAttributes.m +++ b/Libraries/Text/RCTTextAttributes.m @@ -91,7 +91,7 @@ - (void)applyTextAttributes:(RCTTextAttributes *)textAttributes } // Colors - UIColor *effectiveForegroundColor = self.effectiveForegroundColor; + RCTUIColor *effectiveForegroundColor = self.effectiveForegroundColor; // TODO(OSS Candidate ISS#2710739) if (_foregroundColor || !isnan(_opacity)) { attributes[NSForegroundColorAttributeName] = effectiveForegroundColor; @@ -204,9 +204,9 @@ - (CGFloat)effectiveFontSizeMultiplier } } -- (UIColor *)effectiveForegroundColor +- (RCTUIColor *)effectiveForegroundColor // TODO(OSS Candidate ISS#2710739) { - UIColor *effectiveForegroundColor = _foregroundColor ?: [UIColor blackColor]; + RCTUIColor *effectiveForegroundColor = _foregroundColor ?: [RCTUIColor blackColor]; // TODO(OSS Candidate ISS#2710739) if (!isnan(_opacity)) { effectiveForegroundColor = [effectiveForegroundColor colorWithAlphaComponent:CGColorGetAlpha(effectiveForegroundColor.CGColor) * _opacity]; @@ -215,15 +215,15 @@ - (UIColor *)effectiveForegroundColor return effectiveForegroundColor; } -- (UIColor *)effectiveBackgroundColor +- (RCTUIColor *)effectiveBackgroundColor // TODO(OSS Candidate ISS#2710739) { - UIColor *effectiveBackgroundColor = _backgroundColor;// ?: [[UIColor whiteColor] colorWithAlphaComponent:0]; + RCTUIColor *effectiveBackgroundColor = _backgroundColor;// ?: [[UIColor whiteColor] colorWithAlphaComponent:0]; // TODO(OSS Candidate ISS#2710739) if (effectiveBackgroundColor && !isnan(_opacity)) { effectiveBackgroundColor = [effectiveBackgroundColor colorWithAlphaComponent:CGColorGetAlpha(effectiveBackgroundColor.CGColor) * _opacity]; } - return effectiveBackgroundColor ?: [UIColor clearColor]; + return effectiveBackgroundColor ?: [RCTUIColor clearColor]; // TODO(OSS Candidate ISS#2710739) } - (NSString *)applyTextAttributesToText:(NSString *)text diff --git a/Libraries/Text/Text/RCTTextView.m b/Libraries/Text/Text/RCTTextView.m index fb31ab772e0d16..5a2a2af268e0c7 100644 --- a/Libraries/Text/Text/RCTTextView.m +++ b/Libraries/Text/Text/RCTTextView.m @@ -148,9 +148,9 @@ - (void)setTextStorage:(NSTextStorage *)textStorage - (void)drawRect:(CGRect)rect { -#if TARGET_OS_OSX // [TODO(macOS ISS#2323203) +// [TODO(OSS Candidate ISS#2710739): for macOS and iOS dark mode [super drawRect:rect]; -#endif // ]TODO(macOS ISS#2323203) +// ]TODO(OSS Candidate ISS#2710739) if (!_textStorage) { return; } @@ -193,7 +193,7 @@ - (void)drawRect:(CGRect)rect if (highlightPath) { if (!_highlightLayer) { _highlightLayer = [CAShapeLayer layer]; - _highlightLayer.fillColor = [UIColor colorWithWhite:0 alpha:0.25].CGColor; + _highlightLayer.fillColor = [RCTUIColor colorWithWhite:0 alpha:0.25].CGColor; // TODO(OSS Candidate ISS#2710739) [self.layer addSublayer:_highlightLayer]; } _highlightLayer.position = _contentFrame.origin; diff --git a/Libraries/Text/TextInput/Multiline/RCTMultilineTextInputView.m b/Libraries/Text/TextInput/Multiline/RCTMultilineTextInputView.m index a38edf5fd9acfa..de023f9678b630 100644 --- a/Libraries/Text/TextInput/Multiline/RCTMultilineTextInputView.m +++ b/Libraries/Text/TextInput/Multiline/RCTMultilineTextInputView.m @@ -27,8 +27,8 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge _backedTextInputView = [[RCTUITextView alloc] initWithFrame:self.bounds]; _backedTextInputView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - _backedTextInputView.backgroundColor = [UIColor clearColor]; - _backedTextInputView.textColor = [UIColor blackColor]; + _backedTextInputView.backgroundColor = [RCTUIColor clearColor]; // TODO(OSS Candidate ISS#2710739) + _backedTextInputView.textColor = [RCTUIColor blackColor]; // TODO(OSS Candidate ISS#2710739) // This line actually removes 5pt (default value) left and right padding in UITextView. _backedTextInputView.textContainer.lineFragmentPadding = 0; #if !TARGET_OS_OSX // TODO(macOS ISS#2323203) @@ -38,7 +38,7 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge _backedTextInputView.scrollEnabled = YES; #else // [TODO(macOS ISS#2323203) _scrollView = [[RCTUIScrollView alloc] initWithFrame:self.bounds]; // TODO(macOS ISS#3536887) - _scrollView.backgroundColor = [UIColor clearColor]; + _scrollView.backgroundColor = [RCTUIColor clearColor]; _scrollView.drawsBackground = NO; _scrollView.borderType = NSNoBorder; _scrollView.hasHorizontalRuler = NO; diff --git a/Libraries/Text/TextInput/Multiline/RCTUITextView.h b/Libraries/Text/TextInput/Multiline/RCTUITextView.h index ab8d5b6e42d578..df92da5befc61b 100644 --- a/Libraries/Text/TextInput/Multiline/RCTUITextView.h +++ b/Libraries/Text/TextInput/Multiline/RCTUITextView.h @@ -32,7 +32,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) BOOL textWasPasted; #endif // ]TODO(macOS ISS#2323203) @property (nonatomic, copy, nullable) NSString *placeholder; -@property (nonatomic, strong, nullable) UIColor *placeholderColor; +@property (nonatomic, strong, nullable) RCTUIColor *placeholderColor; // TODO(OSS Candidate ISS#2710739) @property (nonatomic, assign) CGFloat preferredMaxLayoutWidth; @@ -48,7 +48,7 @@ NS_ASSUME_NONNULL_BEGIN #if TARGET_OS_OSX // [TODO(macOS ISS#2323203) @property (nonatomic, assign) BOOL scrollEnabled; -@property (nonatomic, strong, nullable) UIColor *selectionColor; +@property (nonatomic, strong, nullable) RCTUIColor *selectionColor; // TODO(OSS Candidate ISS#2710739) @property (nonatomic, assign) UIEdgeInsets textContainerInsets; @property (nonatomic, copy) NSString *text; @property (nonatomic, assign) NSTextAlignment textAlignment; diff --git a/Libraries/Text/TextInput/Multiline/RCTUITextView.m b/Libraries/Text/TextInput/Multiline/RCTUITextView.m index 4319e2b1e395cd..153fa6676e685d 100644 --- a/Libraries/Text/TextInput/Multiline/RCTUITextView.m +++ b/Libraries/Text/TextInput/Multiline/RCTUITextView.m @@ -26,10 +26,10 @@ @implementation RCTUITextView return [UIFont systemFontOfSize:17]; } -static UIColor *defaultPlaceholderColor() +static RCTUIColor *defaultPlaceholderColor() // TODO(OSS Candidate ISS#2710739) { // Default placeholder color from UITextField. - return [UIColor colorWithRed:0 green:0 blue:0.0980392 alpha:0.22]; + return [RCTUIColor colorWithRed:0 green:0 blue:0.0980392 alpha:0.22]; // TODO(OSS Candidate ISS#2710739) } - (instancetype)initWithFrame:(CGRect)frame @@ -93,7 +93,7 @@ - (void)setPlaceholder:(NSString *)placeholder #endif // ]TODO(macOS ISS#2323203) } -- (void)setPlaceholderColor:(UIColor *)placeholderColor +- (void)setPlaceholderColor:(RCTUIColor *)placeholderColor // TODO(OSS Candidate ISS#2710739) { _placeholderColor = placeholderColor; #if !TARGET_OS_OSX // TODO(macOS ISS#2323203) @@ -104,7 +104,7 @@ - (void)setPlaceholderColor:(UIColor *)placeholderColor } #if TARGET_OS_OSX // [TODO(macOS ISS#2323203) -- (void)setSelectionColor:(UIColor *)selectionColor +- (void)setSelectionColor:(RCTUIColor *)selectionColor { NSMutableDictionary *selectTextAttributes = self.selectedTextAttributes.mutableCopy; selectTextAttributes[NSBackgroundColorAttributeName] = selectionColor ?: [NSColor selectedControlColor]; @@ -112,9 +112,9 @@ - (void)setSelectionColor:(UIColor *)selectionColor self.insertionPointColor = self.selectionColor ?: [NSColor selectedControlColor]; } -- (UIColor*)selectionColor +- (RCTUIColor*)selectionColor { - return (UIColor*)self.selectedTextAttributes[NSBackgroundColorAttributeName]; + return (RCTUIColor*)self.selectedTextAttributes[NSBackgroundColorAttributeName]; } - (void)setEnabledTextCheckingTypes:(NSTextCheckingTypes)checkingType diff --git a/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h b/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h index 19f411e84bf7b5..033d2661d5bdcb 100644 --- a/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h +++ b/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h @@ -31,11 +31,11 @@ NS_ASSUME_NONNULL_BEGIN @protocol RCTBackedTextInputViewProtocol #endif // ]TODO(macOS ISS#2323203) -@property (nonatomic, strong, nullable) UIColor *textColor; +@property (nonatomic, strong, nullable) RCTUIColor *textColor; // TODO(OSS Candidate ISS#2710739) @property (nonatomic, strong, nullable) UIFont *font; @property (nonatomic, copy, nullable) NSAttributedString *attributedText; @property (nonatomic, copy, nullable) NSString *placeholder; -@property (nonatomic, strong, nullable) UIColor *placeholderColor; +@property (nonatomic, strong, nullable) RCTUIColor *placeholderColor; // TODO(OSS Candidate ISS#2710739) @property (nonatomic, assign) NSTextAlignment textAlignment; #if !TARGET_OS_OSX // TODO(macOS ISS#2323203) @property (nonatomic, assign, readonly) BOOL textWasPasted; diff --git a/Libraries/Text/TextInput/Singleline/RCTUITextField.h b/Libraries/Text/TextInput/Singleline/RCTUITextField.h index f1a02b99753c61..8c9dc1bb312f6d 100644 --- a/Libraries/Text/TextInput/Singleline/RCTUITextField.h +++ b/Libraries/Text/TextInput/Singleline/RCTUITextField.h @@ -29,7 +29,7 @@ NS_ASSUME_NONNULL_BEGIN #else // [TODO(macOS ISS#2323203) @property (nonatomic, assign) BOOL textWasPasted; #endif // ]TODO(macOS ISS#2323203) -@property (nonatomic, strong, nullable) UIColor *placeholderColor; +@property (nonatomic, strong, nullable) RCTUIColor *placeholderColor; // TODO(OSS Candidate ISS#2710739) @property (nonatomic, assign) UIEdgeInsets textContainerInset; #if !TARGET_OS_OSX // TODO(macOS ISS#2323203) @property (nonatomic, assign, getter=isEditable) BOOL editable; @@ -42,7 +42,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, copy, nullable) NSString *text; @property (nonatomic, getter=isAutomaticTextReplacementEnabled) BOOL automaticTextReplacementEnabled; @property (nonatomic, getter=isAutomaticSpellingCorrectionEnabled) BOOL automaticSpellingCorrectionEnabled; -@property (nonatomic, strong, nullable) UIColor *selectionColor; +@property (nonatomic, strong, nullable) RCTUIColor *selectionColor; @property (weak, nullable) id delegate; #endif // ]TODO(macOS ISS#2323203) diff --git a/Libraries/Text/TextInput/Singleline/RCTUITextField.m b/Libraries/Text/TextInput/Singleline/RCTUITextField.m index e21edace02b1d6..b991a3555c1671 100644 --- a/Libraries/Text/TextInput/Singleline/RCTUITextField.m +++ b/Libraries/Text/TextInput/Singleline/RCTUITextField.m @@ -18,7 +18,7 @@ @interface RCTUITextFieldCell : NSTextFieldCell @property (nonatomic, assign) UIEdgeInsets textContainerInset; @property (nonatomic, getter=isAutomaticTextReplacementEnabled) BOOL automaticTextReplacementEnabled; @property (nonatomic, getter=isAutomaticSpellingCorrectionEnabled) BOOL automaticSpellingCorrectionEnabled; -@property (nonatomic, strong, nullable) UIColor *selectionColor; +@property (nonatomic, strong, nullable) RCTUIColor *selectionColor; @end @@ -65,7 +65,7 @@ - (NSText *)setUpFieldEditorAttributes:(NSText *)textObj NSMutableDictionary *selectTextAttributes = fieldEditor.selectedTextAttributes.mutableCopy; selectTextAttributes[NSBackgroundColorAttributeName] = self.selectionColor ?: [NSColor selectedControlColor]; fieldEditor.selectedTextAttributes = selectTextAttributes; - fieldEditor.insertionPointColor = self.selectionColor ?: [NSColor selectedControlColor]; + fieldEditor.insertionPointColor = self.selectionColor ?: [RCTUIColor selectedControlColor]; return fieldEditor; } @@ -85,10 +85,10 @@ @implementation RCTUITextField { return [UIFont systemFontOfSize:17]; } -static UIColor *defaultPlaceholderTextColor() +static RCTUIColor *defaultPlaceholderTextColor() { // Default placeholder color from UITextField. - return [UIColor colorWithRed:0 green:0 blue:0.0980392 alpha:0.22]; + return [RCTUIColor colorWithRed:0 green:0 blue:0.0980392 alpha:0.22]; } #endif // ]TODO(macOS ISS#2323203) @@ -183,12 +183,12 @@ - (BOOL)isAutomaticSpellingCorrectionEnabled return ((RCTUITextFieldCell*)self.cell).isAutomaticSpellingCorrectionEnabled; } -- (void)setSelectionColor:(UIColor *)selectionColor +- (void)setSelectionColor:(RCTUIColor *)selectionColor // TODO(OSS Candidate ISS#2710739) { ((RCTUITextFieldCell*)self.cell).selectionColor = selectionColor; } -- (UIColor*)selectionColor +- (RCTUIColor*)selectionColor // TODO(OSS Candidate ISS#2710739) { return ((RCTUITextFieldCell*)self.cell).selectionColor; } @@ -215,7 +215,7 @@ - (void)setPlaceholder:(NSString *)placeholder [self _updatePlaceholder]; } -- (void)setPlaceholderColor:(UIColor *)placeholderColor +- (void)setPlaceholderColor:(RCTUIColor *)placeholderColor // TODO(OSS Candidate ISS#2710739) { _placeholderColor = placeholderColor; [self _updatePlaceholder]; diff --git a/RNTester/RNTester.xcodeproj/project.pbxproj b/RNTester/RNTester.xcodeproj/project.pbxproj index 99635b7973227a..76818290b686dd 100644 --- a/RNTester/RNTester.xcodeproj/project.pbxproj +++ b/RNTester/RNTester.xcodeproj/project.pbxproj @@ -145,6 +145,7 @@ 2DE7E8061FB2A4F3009E225D /* libRCTWebSocket-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DD323D51DA2DD8B000FE1B8 /* libRCTWebSocket-tvOS.a */; }; 2DE7E8071FB2A4F3009E225D /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DD323D91DA2DD8B000FE1B8 /* libReact.a */; }; 3578590A1B28D2CF00341EDB /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 357859011B28D2C500341EDB /* libRCTLinking.a */; }; + 38C500E222D3CF2E00BCD999 /* RCTConvert_UIColorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 38C500E122D3CF2E00BCD999 /* RCTConvert_UIColorTests.m */; }; 39AA31A41DC1DFDC000F7EBB /* RCTUnicodeDecodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 39AA31A31DC1DFDC000F7EBB /* RCTUnicodeDecodeTests.m */; }; 3D05746D1DE6008900184BB4 /* libRCTPushNotification-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D05746C1DE6008900184BB4 /* libRCTPushNotification-tvOS.a */; }; 3D0E379D1F1CC77200DCAC9F /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14AADF041AC3DB95002390C9 /* libReact.a */; }; @@ -979,6 +980,7 @@ 2DD323A51DA2DD8B000FE1B8 /* RNTester-tvOSUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RNTester-tvOSUnitTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 2DDEF00F1F84BF7B00DBDF73 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = RNTester/Images.xcassets; sourceTree = ""; }; 357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = ""; }; + 38C500E122D3CF2E00BCD999 /* RCTConvert_UIColorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTConvert_UIColorTests.m; sourceTree = ""; }; 39AA31A31DC1DFDC000F7EBB /* RCTUnicodeDecodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUnicodeDecodeTests.m; sourceTree = ""; }; 3D13F83E1D6F6AE000E69E0E /* RNTesterBundle.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RNTesterBundle.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 3D13F8401D6F6AE000E69E0E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = ../Info.plist; sourceTree = ""; }; @@ -1374,6 +1376,7 @@ 68FF44371CF6111500720EFD /* RCTBundleURLProviderTests.m */, 1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */, 1879ECDE21E84E2800D98372 /* RCTConvert_NSColorTests.m */, + 38C500E122D3CF2E00BCD999 /* RCTConvert_UIColorTests.m */, 1497CFA71B21F5E400C1F8F2 /* RCTConvert_NSURLTests.m */, 1497CFA81B21F5E400C1F8F2 /* RCTFontTests.m */, 1497CFA91B21F5E400C1F8F2 /* RCTEventDispatcherTests.m */, @@ -2807,6 +2810,7 @@ 19BA88D51F84344F00741C5A /* RCTBlobManagerTests.m in Sources */, 68FF44381CF6111500720EFD /* RCTBundleURLProviderTests.m in Sources */, 8385CEF51B873B5C00C6273E /* RCTImageLoaderTests.m in Sources */, + 38C500E222D3CF2E00BCD999 /* RCTConvert_UIColorTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/RNTester/RNTesterUnitTests/RCTConvert_UIColorTests.m b/RNTester/RNTesterUnitTests/RCTConvert_UIColorTests.m new file mode 100644 index 00000000000000..01507bd9fa0b0b --- /dev/null +++ b/RNTester/RNTesterUnitTests/RCTConvert_UIColorTests.m @@ -0,0 +1,200 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +#import + +#import + +@interface RCTConvert_NSColorTests : XCTestCase + +@end + +@implementation RCTConvert_NSColorTests + +- (void)testColor +{ + id json = RCTJSONParse(@"{ \"semantic\": \"lightTextColor\" }", nil); + UIColor *value = [RCTConvert UIColor:json]; + XCTAssertEqualObjects(value, [UIColor lightTextColor]); +} + +- (void)testColorFailure +{ + id json = RCTJSONParse(@"{ \"semantic\": \"bogusColor\" }", nil); + + __block NSString *errorMessage = nil; + RCTLogFunction defaultLogFunction = RCTGetLogFunction(); + RCTSetLogFunction(^(__unused RCTLogLevel level, __unused RCTLogSource source, __unused NSString *fileName, __unused NSNumber *lineNumber, NSString *message) { + errorMessage = message; + }); + + UIColor *value = [RCTConvert UIColor:json]; + + RCTSetLogFunction(defaultLogFunction); + + XCTAssertEqualObjects(value, nil); + XCTAssertTrue([errorMessage containsString:@"labelColor"]); // the RedBox message will contain a list of the valid color names. +} + +- (void)testFallbackColor +{ + id json = RCTJSONParse(@"{ \"semantic\": \"unitTestFallbackColorIOS\" }", nil); + UIColor *value = [RCTConvert UIColor:json]; + XCTAssertEqualObjects(value, [UIColor blueColor]); +} + +- (void)testDynamicColor +{ + // 0 == 0x00000000 == black + // 16777215 == 0x00FFFFFF == white + id json = RCTJSONParse(@"{ \"dynamic\": { \"light\":0, \"dark\":16777215 } }", nil); + UIColor *value = [RCTConvert UIColor:json]; + XCTAssertNotNil(value); + +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 + if (@available(iOS 13.0, *)) { + id savedTraitCollection = [UITraitCollection currentTraitCollection]; + + [UITraitCollection setCurrentTraitCollection:[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight]]; + CGFloat rgba[4]; + RCTGetRGBAColorComponents([value CGColor], rgba); + XCTAssertEqual(rgba[0], 0); + XCTAssertEqual(rgba[1], 0); + XCTAssertEqual(rgba[2], 0); + XCTAssertEqual(rgba[3], 0); + + [UITraitCollection setCurrentTraitCollection:[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark]]; + RCTGetRGBAColorComponents([value CGColor], rgba); + XCTAssertEqual(rgba[0], 1); + XCTAssertEqual(rgba[1], 1); + XCTAssertEqual(rgba[2], 1); + XCTAssertEqual(rgba[3], 0); + + [UITraitCollection setCurrentTraitCollection:savedTraitCollection]; + } +#endif +} + +- (void)testCompositeDynamicColor +{ + id json = RCTJSONParse(@"{ \"dynamic\": { \"light\": { \"semantic\": \"systemRedColor\" }, \"dark\":{ \"semantic\": \"systemBlueColor\" } } }", nil); + UIColor *value = [RCTConvert UIColor:json]; + XCTAssertNotNil(value); + +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 + if (@available(iOS 13.0, *)) { + id savedTraitCollection = [UITraitCollection currentTraitCollection]; + + [UITraitCollection setCurrentTraitCollection:[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight]]; + + CGFloat rgba1[4]; + CGFloat rgba2[4]; + RCTGetRGBAColorComponents([value CGColor], rgba1); + RCTGetRGBAColorComponents([[UIColor systemRedColor] CGColor], rgba2); + XCTAssertEqual(rgba1[0], rgba2[0]); + XCTAssertEqual(rgba1[1], rgba2[1]); + XCTAssertEqual(rgba1[2], rgba2[2]); + XCTAssertEqual(rgba1[3], rgba2[3]); + + [UITraitCollection setCurrentTraitCollection:[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark]]; + + RCTGetRGBAColorComponents([value CGColor], rgba1); + RCTGetRGBAColorComponents([[UIColor systemBlueColor] CGColor], rgba2); + XCTAssertEqual(rgba1[0], rgba2[0]); + XCTAssertEqual(rgba1[1], rgba2[1]); + XCTAssertEqual(rgba1[2], rgba2[2]); + XCTAssertEqual(rgba1[3], rgba2[3]); + + [UITraitCollection setCurrentTraitCollection:savedTraitCollection]; + } +#endif +} + +- (void)testGenerateFallbacks +{ + NSDictionary* semanticColors = @{ + // https://developer.apple.com/documentation/uikit/uicolor/ui_element_colors + // Label Colors + @"labelColor": @(0xFF000000), + @"secondaryLabelColor": @(0x993c3c43), + @"tertiaryLabelColor": @(0x4c3c3c43), + @"quaternaryLabelColor": @(0x2d3c3c43), + // Fill Colors + @"systemFillColor": @(0x33787880), + @"secondarySystemFillColor": @(0x28787880), + @"tertiarySystemFillColor": @(0x1e767680), + @"quaternarySystemFillColor": @(0x14747480), + // Text Colors + @"placeholderTextColor": @(0x4c3c3c43), + // Standard Content Background Colors + @"systemBackgroundColor": @(0xFFffffff), + @"secondarySystemBackgroundColor": @(0xFFf2f2f7), + @"tertiarySystemBackgroundColor": @(0xFFffffff), + // Grouped Content Background Colors + @"systemGroupedBackgroundColor": @(0xFFf2f2f7), + @"secondarySystemGroupedBackgroundColor": @(0xFFffffff), + @"tertiarySystemGroupedBackgroundColor": @(0xFFf2f2f7), + // Separator Colors + @"separatorColor": @(0x493c3c43), + @"opaqueSeparatorColor": @(0xFFc6c6c8), + // Link Color + @"linkColor": @(0xFF007aff), + // https://developer.apple.com/documentation/uikit/uicolor/standard_colors + // Adaptable Colors + @"systemBrownColor": @(0xFFa2845e), + @"systemIndigoColor": @(0xFF5856d6), + // Adaptable Gray Colors + @"systemGray2Color": @(0xFFaeaeb2), + @"systemGray3Color": @(0xFFc7c7cc), + @"systemGray4Color": @(0xFFd1d1d6), + @"systemGray5Color": @(0xFFe5e5ea), + @"systemGray6Color": @(0xFFf2f2f7), + }; + +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 + id savedTraitCollection = nil; + if (@available(iOS 13.0, *)) { + savedTraitCollection = [UITraitCollection currentTraitCollection]; + + [UITraitCollection setCurrentTraitCollection:[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight]]; + } +#endif + + for (NSString *semanticColor in semanticColors) { + id json = RCTJSONParse([NSString stringWithFormat:@"{ \"semantic\": \"%@\" }", semanticColor], nil); + UIColor *value = [RCTConvert UIColor:json]; + XCTAssertNotNil(value); + + NSNumber *fallback = [semanticColors objectForKey:semanticColor]; + NSUInteger rgbValue = [fallback unsignedIntegerValue]; + NSUInteger alpha1 = ((rgbValue & 0xFF000000) >> 24); + NSUInteger red1 = ((rgbValue & 0x00FF0000) >> 16); + NSUInteger green1 = ((rgbValue & 0x0000FF00) >> 8); + NSUInteger blue1 = ((rgbValue & 0x000000FF) >> 0); + + CGFloat rgba[4]; + RCTGetRGBAColorComponents([value CGColor], rgba); + NSUInteger red2 = rgba[0] * 255; + NSUInteger green2 = rgba[1] * 255; + NSUInteger blue2 = rgba[2] * 255; + NSUInteger alpha2 = rgba[3] * 255; + + XCTAssertEqual(red1, red2); + XCTAssertEqual(green1, green2); + XCTAssertEqual(blue1, blue2); + XCTAssertEqual(alpha1, alpha2); + } + +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 + if (@available(iOS 13.0, *)) { + [UITraitCollection setCurrentTraitCollection:savedTraitCollection]; + } +#endif +} + +@end diff --git a/RNTester/RNTesterUnitTests/RCTNativeAnimatedNodesManagerTests.m b/RNTester/RNTesterUnitTests/RCTNativeAnimatedNodesManagerTests.m index c1be6262c32b53..ce26057af06041 100644 --- a/RNTester/RNTesterUnitTests/RCTNativeAnimatedNodesManagerTests.m +++ b/RNTester/RNTesterUnitTests/RCTNativeAnimatedNodesManagerTests.m @@ -17,7 +17,7 @@ static const NSTimeInterval FRAME_LENGTH = 1.0 / 60.0; -@interface RCTFakeDisplayLink : CADisplayLink +@interface RCTFakeDisplayLink : RCTPlatformDisplayLink // TODO(macOS ISS#2323203) @end diff --git a/RNTester/js/DarkModeExample.js b/RNTester/js/DarkModeExample.js new file mode 100644 index 00000000000000..b73607b612cf54 --- /dev/null +++ b/RNTester/js/DarkModeExample.js @@ -0,0 +1,181 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + +'use strict'; // TODO(OSS Candidate ISS#2710739) + +const React = require('react'); +const ReactNative = require('react-native'); +const Platform = require('Platform'); +const {Text, View} = ReactNative; + +type State = {}; + +class SemanticColorsExample extends React.Component<{}, State> { + state: State = {}; + + createTable() { + let colors = []; + if (Platform.OS === 'macos') { + colors = [ + // https://developer.apple.com/documentation/appkit/nscolor/ui_element_colors + // Label Colors + 'labelColor', + 'secondaryLabelColor', + 'tertiaryLabelColor', + 'quaternaryLabelColor', + // Text Colors + 'textColor', + 'placeholderTextColor', + 'selectedTextColor', + 'textBackgroundColor', + 'selectedTextBackgroundColor', + 'keyboardFocusIndicatorColor', + 'unemphasizedSelectedTextColor', + 'unemphasizedSelectedTextBackgroundColor', + // Content Colors + 'linkColor', + 'separatorColor', + 'selectedContentBackgroundColor', + 'unemphasizedSelectedContentBackgroundColor', + // Menu Colors + 'selectedMenuItemTextColor', + // Table Colors + 'gridColor', + 'headerTextColor', + 'alternatingContentBackgroundColorEven', + 'alternatingContentBackgroundColorOdd', + // Control Colors + 'controlAccentColor', + 'controlColor', + 'controlBackgroundColor', + 'controlTextColor', + 'disabledControlTextColor', + 'selectedControlColor', + 'selectedControlTextColor', + 'alternateSelectedControlTextColor', + 'scrubberTexturedBackgroundColor', + // Window Colors + 'windowBackgroundColor', + 'windowFrameTextColor', + 'underPageBackgroundColor', + // Highlights and Shadows + 'findHighlightColor', + 'highlightColor', + 'shadowColor', + // https://developer.apple.com/documentation/appkit/nscolor/standard_colors + // Standard Colors + 'systemBlueColor', + 'systemBrownColor', + 'systemGrayColor', + 'systemGreenColor', + 'systemOrangeColor', + 'systemPinkColor', + 'systemPurpleColor', + 'systemRedColor', + 'systemYellowColor', + ]; + } else if (Platform.OS === 'ios') { + colors = [ + // https://developer.apple.com/documentation/uikit/uicolor/ui_element_colors + // Label Colors + 'labelColor', + 'secondaryLabelColor', + 'tertiaryLabelColor', + 'quaternaryLabelColor', + // Fill Colors + 'systemFillColor', + 'secondarySystemFillColor', + 'tertiarySystemFillColor', + 'quaternarySystemFillColor', + // Text Colors + 'placeholderTextColor', + // Standard Content Background Colors + 'systemBackgroundColor', + 'secondarySystemBackgroundColor', + 'tertiarySystemBackgroundColor', + // Grouped Content Background Colors + 'systemGroupedBackgroundColor', + 'secondarySystemGroupedBackgroundColor', + 'tertiarySystemGroupedBackgroundColor', + // Separator Colors + 'separatorColor', + 'opaqueSeparatorColor', + // Link Color + 'linkColor', + // Nonadaptable Colors + 'darkTextColor', + 'lightTextColor', + // https://developer.apple.com/documentation/uikit/uicolor/standard_colors + // Adaptable Colors + 'systemBlueColor', + 'systemBrownColor', + 'systemGreenColor', + 'systemIndigoColor', + 'systemOrangeColor', + 'systemPinkColor', + 'systemPurpleColor', + 'systemRedColor', + 'systemTealColor', + 'systemYellowColor', + // Adaptable Gray Colors + 'systemGrayColor', + 'systemGray2Color', + 'systemGray3Color', + 'systemGray4Color', + 'systemGray5Color', + 'systemGray6Color', + ]; + } + + let table = []; + for (let color of colors) { + table.push( + + + {color} + + + , + ); + } + return table; + } + + render() { + return ( + + {this.createTable()} + + ); + } +} + +exports.title = 'Dark Mode'; +exports.description = + 'Examples that show how Dark Mode may be implemented in an app.'; +exports.examples = [ + { + title: 'Semantic Colors', + render: function(): React.Element { + return ; + }, + }, +]; diff --git a/RNTester/js/RNTesterApp.ios.js b/RNTester/js/RNTesterApp.ios.js index c85f8cb93183d2..c8b116756ff605 100644 --- a/RNTester/js/RNTesterApp.ios.js +++ b/RNTester/js/RNTesterApp.ios.js @@ -150,8 +150,8 @@ class RNTesterApp extends React.Component { const styles = StyleSheet.create({ headerContainer: { borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: '#96969A', - backgroundColor: '#F5F5F6', + borderBottomColor: {semantic: 'separatorColor'}, // TODO(OSS Candidate ISS#2710739) + backgroundColor: {semantic: 'tertiarySystemBackgroundColor'}, // TODO(OSS Candidate ISS#2710739) }, header: { height: 40, @@ -169,6 +169,7 @@ const styles = StyleSheet.create({ fontSize: 19, fontWeight: '600', textAlign: 'center', + color: {dynamic: {light: 'black', dark: 'white'}}, // TODO(OSS Candidate ISS#2710739) }, exampleContainer: { flex: 1, diff --git a/RNTester/js/RNTesterBlock.js b/RNTester/js/RNTesterBlock.js index 6999668e6ac78b..8ff85f825f1355 100644 --- a/RNTester/js/RNTesterBlock.js +++ b/RNTester/js/RNTesterBlock.js @@ -12,6 +12,7 @@ const React = require('react'); const {StyleSheet, Text, View} = require('react-native'); +const Platform = require('Platform'); // TODO(macOS ISS#2323203) type Props = $ReadOnly<{| children?: React.Node, @@ -47,8 +48,20 @@ const styles = StyleSheet.create({ container: { borderRadius: 3, borderWidth: 0.5, - borderColor: '#d6d7da', - backgroundColor: '#ffffff', + ...Platform.select({ + macos: { + borderColor: {semantic: 'separatorColor'}, + backgroundColor: {semantic: 'windowBackgroundColor'}, + }, + ios: { + borderColor: {semantic: 'separatorColor'}, + backgroundColor: {semantic: 'tertiarySystemBackgroundColor'}, + }, + default: { + borderColor: '#d6d7da', + backgroundColor: '#ffffff', + }, + }), margin: 10, marginVertical: 5, overflow: 'hidden', @@ -57,12 +70,33 @@ const styles = StyleSheet.create({ borderBottomWidth: 0.5, borderTopLeftRadius: 3, borderTopRightRadius: 2.5, - borderBottomColor: '#d6d7da', - backgroundColor: '#f6f7f8', + ...Platform.select({ + macos: { + borderBottomColor: {semantic: 'separatorColor'}, + backgroundColor: {semantic: 'controlBackgroundColor'}, + }, + ios: { + borderBottomColor: {semantic: 'separatorColor'}, + backgroundColor: {semantic: 'tertiarySystemBackgroundColor'}, + }, + default: { + borderBottomColor: '#d6d7da', + backgroundColor: '#f6f7f8', + }, + }), paddingHorizontal: 10, paddingVertical: 5, }, titleText: { + ...Platform.select({ + macos: { + color: {semantic: 'labelColor'}, + }, + ios: { + color: {semantic: 'labelColor'}, + }, + default: undefined, + }), fontSize: 14, fontWeight: '500', }, diff --git a/RNTester/js/RNTesterExampleFilter.js b/RNTester/js/RNTesterExampleFilter.js index e2c2a307d5061a..4c3164bedff65b 100644 --- a/RNTester/js/RNTesterExampleFilter.js +++ b/RNTester/js/RNTesterExampleFilter.js @@ -76,9 +76,11 @@ class RNTesterExampleFilter extends React.Component { }} placeholder="Search..." placeholderTextColor={ - Platform.OS === 'macos' - ? {semantic: 'placeholderTextColor'} - : undefined /*TODO(macOS ISS#2323203)*/ + Platform.select({ + macos: {semantic: 'placeholderTextColor'}, + ios: {semantic: 'placeholderTextColor'}, + default: undefined, + }) /*TODO(macOS ISS#2323203)*/ } underlineColorAndroid="transparent" style={styles.searchTextInput} @@ -97,6 +99,9 @@ const styles = StyleSheet.create({ macos: { backgroundColor: {semantic: 'windowBackgroundColor'}, }, + ios: { + backgroundColor: {semantic: 'systemGroupedBackgroundColor'}, + }, default: { // ]TODO(macOS ISS#2323203) backgroundColor: '#eeeeee', @@ -112,6 +117,11 @@ const styles = StyleSheet.create({ backgroundColor: {semantic: 'textBackgroundColor'}, borderColor: {semantic: 'quaternaryLabelColor'}, }, + ios: { + color: {semantic: 'labelColor'}, + backgroundColor: {semantic: 'secondarySystemGroupedBackgroundColor'}, + borderColor: {semantic: 'quaternaryLabelColor'}, + }, default: { // ]TODO(macOS ISS#2323203) backgroundColor: 'white', diff --git a/RNTester/js/RNTesterExampleList.js b/RNTester/js/RNTesterExampleList.js index 5eb858c9e7f283..9c628def59d224 100644 --- a/RNTester/js/RNTesterExampleList.js +++ b/RNTester/js/RNTesterExampleList.js @@ -113,6 +113,7 @@ class RNTesterExampleList extends React.Component { renderSectionHeader={renderSectionHeader} backgroundColor={Platform.select({ macos: 'transparent', + ios: 'transparent', default: undefined, })} // TODO(macOS ISS#2323203) /> @@ -183,6 +184,9 @@ const styles = StyleSheet.create({ macos: { backgroundColor: {semantic: 'controlBackgroundColor'}, }, + ios: { + backgroundColor: {semantic: 'systemBackgroundColor'}, + }, default: { // ]TODO(macOS ISS#2323203) backgroundColor: '#eeeeee', @@ -198,6 +202,12 @@ const styles = StyleSheet.create({ }, color: {semantic: 'headerTextColor'}, }, + ios: { + backgroundColor: { + semantic: 'systemGroupedBackgroundColor', + }, + color: {semantic: 'secondaryLabelColor'}, + }, default: { // ]TODO(macOS ISS#2323203) backgroundColor: '#eeeeee', @@ -214,6 +224,9 @@ const styles = StyleSheet.create({ macos: { backgroundColor: {semantic: 'controlBackgroundColor'}, }, + ios: { + backgroundColor: {semantic: 'secondarySystemGroupedBackgroundColor'}, + }, default: { // ]TODO(macOS ISS#2323203) backgroundColor: 'white', @@ -236,6 +249,9 @@ const styles = StyleSheet.create({ macos: { backgroundColor: {semantic: 'separatorColor'}, }, + ios: { + backgroundColor: {semantic: 'separatorColor'}, + }, default: { // ]TODO(macOS ISS#2323203) backgroundColor: '#bbbbbb', @@ -249,6 +265,7 @@ const styles = StyleSheet.create({ }, sectionListContentContainer: Platform.select({ macos: {backgroundColor: {semantic: 'separatorColor'}}, + ios: {backgroundColor: {semantic: 'separatorColor'}}, default: {backgroundColor: 'white'}, }), rowTitleText: { @@ -259,6 +276,9 @@ const styles = StyleSheet.create({ macos: { color: {semantic: 'controlTextColor'}, }, + ios: { + color: {semantic: 'labelColor'}, + }, default: { // ]TODO(macOS ISS#2323203) color: 'black', diff --git a/RNTester/js/RNTesterList.ios.js b/RNTester/js/RNTesterList.ios.js index ededc06ccbd589..848957d90598bc 100644 --- a/RNTester/js/RNTesterList.ios.js +++ b/RNTester/js/RNTesterList.ios.js @@ -28,6 +28,11 @@ const ComponentExamples: Array = [ module: require('./ButtonExample'), supportsTVOS: true, }, + { + key: 'DarkModeExample', + module: require('./DarkModeExample'), + supportsTVOS: false, + }, { key: 'DatePickerIOSExample', module: require('./DatePickerIOSExample'), diff --git a/RNTester/js/RNTesterList.macos.js b/RNTester/js/RNTesterList.macos.js index a6a1178cd9f3b5..fabdc84db820c2 100644 --- a/RNTester/js/RNTesterList.macos.js +++ b/RNTester/js/RNTesterList.macos.js @@ -32,6 +32,11 @@ const ComponentExamples: Array = [ module: require('./ButtonExample'), supportsTVOS: false, }, + { + key: 'DarkModeExample', + module: require('./DarkModeExample'), + supportsTVOS: false, + }, { key: 'DatePickerMacOSExample', module: require('./DatePickerMacOSExample'), diff --git a/RNTester/js/RNTesterPage.js b/RNTester/js/RNTesterPage.js index 6b1b0fe1db6112..c87d9d2779c48e 100644 --- a/RNTester/js/RNTesterPage.js +++ b/RNTester/js/RNTesterPage.js @@ -11,6 +11,7 @@ 'use strict'; const React = require('react'); +const Platform = require('Platform'); // TODO(OSS Candidate ISS#2710739) const {ScrollView, StyleSheet, View} = require('react-native'); const RNTesterTitle = require('./RNTesterTitle'); @@ -52,7 +53,17 @@ class RNTesterPage extends React.Component { const styles = StyleSheet.create({ container: { - backgroundColor: '#e9eaed', + ...Platform.select({ + ios: { + backgroundColor: {semantic: 'secondarySystemBackgroundColor'}, + }, + macos: { + backgroundColor: {semantic: 'underPageBackgroundColor'}, + }, + default: { + backgroundColor: '#e9eaed', + }, + }), flex: 1, }, spacer: { diff --git a/React/Base/RCTConvert.h b/React/Base/RCTConvert.h index 768e06e9af6d86..4c42ad4b899678 100644 --- a/React/Base/RCTConvert.h +++ b/React/Base/RCTConvert.h @@ -99,7 +99,7 @@ typedef NSURL RCTFileURL; + (CGAffineTransform)CGAffineTransform:(id)json; -+ (UIColor *)UIColor:(id)json; ++ (RCTUIColor *)UIColor:(id)json; // TODO(OSS Candidate ISS#2710739) + (CGColorRef)CGColor:(id)json CF_RETURNS_NOT_RETAINED; + (YGValue)YGValue:(id)json; @@ -111,7 +111,7 @@ typedef NSURL RCTFileURL; + (NSArray *)NSURLArray:(id)json; + (NSArray *)RCTFileURLArray:(id)json; + (NSArray *)NSNumberArray:(id)json; -+ (NSArray *)UIColorArray:(id)json; ++ (NSArray *)UIColorArray:(id)json; // TODO(OSS Candidate ISS#2710739) #if TARGET_OS_OSX // [TODO(macOS ISS#2323203) + (NSArray *)NSPasteboardTypeArray:(id)json; #endif // ]TODO(macOS ISS#2323203) diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index 20ec9217e534d7..5900896993bcb8 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -516,19 +516,20 @@ + (type)type:(id)json \ @"a", @"b", @"c", @"d", @"tx", @"ty" ])) -#if TARGET_OS_OSX // [TODO(macOS ISS#2323203) +// [TODO(macOS ISS#2323203) static NSString *const RCTFallback = @"fallback"; +static NSString *const RCTFallbackARGB = @"fallback-argb"; static NSString *const RCTSelector = @"selector"; static NSString *const RCTIndex = @"index"; -/** The following dictionary defines the react-native semantic colors for macos. +/** The following dictionary defines the react-native semantic colors for macos and ios. * If the value for a given name is empty then the name itself - * is used as the NSColor selector. + * is used as the UIColor selector. * If the RCTSelector key is present then that value is used for a selector instead * of the key name. * If the given selector is not available on the running OS version then * the RCTFallback selector is used instead. - * If the RCTIndex key is present then object returned from NSColor is an + * If the RCTIndex key is present then object returned from UIColor is an * NSArray and the object at index RCTIndex is to be used. */ static NSDictionary* RCTSemanticColorsMap() @@ -536,6 +537,7 @@ + (type)type:(id)json \ static NSDictionary *colorMap = nil; if (colorMap == nil) { colorMap = @{ +#if TARGET_OS_OSX // https://developer.apple.com/documentation/appkit/nscolor/ui_element_colors // Label Colors @"labelColor": @{}, // 10_10 @@ -614,11 +616,114 @@ + (type)type:(id)json \ @"systemPurpleColor": @{}, // 10_10 @"systemRedColor": @{}, // 10_10 @"systemYellowColor": @{}, // 10_10 +#else // TARGET_OS_IOS + // https://developer.apple.com/documentation/uikit/uicolor/ui_element_colors + // Label Colors + @"labelColor": @{ // iOS 13.0 + RCTFallbackARGB: @(0xFF000000) // fallback for iOS<=12: RGBA returned by this semantic color in light mode on iOS 13 + }, + @"secondaryLabelColor": @{ // iOS 13.0 + RCTFallbackARGB: @(0x993c3c43) + }, + @"tertiaryLabelColor": @{ // iOS 13.0 + RCTFallbackARGB: @(0x4c3c3c43) + }, + @"quaternaryLabelColor": @{ // iOS 13.0 + RCTFallbackARGB: @(0x2d3c3c43) + }, + // Fill Colors + @"systemFillColor": @{ // iOS 13.0 + RCTFallbackARGB: @(0x33787880) + }, + @"secondarySystemFillColor": @{ // iOS 13.0 + RCTFallbackARGB: @(0x28787880) + }, + @"tertiarySystemFillColor": @{ // iOS 13.0 + RCTFallbackARGB: @(0x1e767680) + }, + @"quaternarySystemFillColor": @{ // iOS 13.0 + RCTFallbackARGB: @(0x14747480) + }, + // Text Colors + @"placeholderTextColor": @{ // iOS 13.0 + RCTFallbackARGB: @(0x4c3c3c43) + }, + // Standard Content Background Colors + @"systemBackgroundColor": @{ // iOS 13.0 + RCTFallbackARGB: @(0xFFffffff) + }, + @"secondarySystemBackgroundColor": @{ // iOS 13.0 + RCTFallbackARGB: @(0xFFf2f2f7) + }, + @"tertiarySystemBackgroundColor": @{ // iOS 13.0 + RCTFallbackARGB: @(0xFFffffff) + }, + // Grouped Content Background Colors + @"systemGroupedBackgroundColor": @{ // iOS 13.0 + RCTFallbackARGB: @(0xFFf2f2f7) + }, + @"secondarySystemGroupedBackgroundColor": @{ // iOS 13.0 + RCTFallbackARGB: @(0xFFffffff) + }, + @"tertiarySystemGroupedBackgroundColor": @{ // iOS 13.0 + RCTFallbackARGB: @(0xFFf2f2f7) + }, + // Separator Colors + @"separatorColor": @{ // iOS 13.0 + RCTFallbackARGB: @(0x493c3c43) + }, + @"opaqueSeparatorColor": @{ // iOS 13.0 + RCTFallbackARGB: @(0xFFc6c6c8) + }, + // Link Color + @"linkColor": @{ // iOS 13.0 + RCTFallbackARGB: @(0xFF007aff) + }, + // Nonadaptable Colors + @"darkTextColor": @{}, + @"lightTextColor": @{}, + // https://developer.apple.com/documentation/uikit/uicolor/standard_colors + // Adaptable Colors + @"systemBlueColor": @{}, + @"systemBrownColor": @{ // iOS 13.0 + RCTFallbackARGB: @(0xFFa2845e) + }, + @"systemGreenColor": @{}, + @"systemIndigoColor": @{ // iOS 13.0 + RCTFallbackARGB: @(0xFF5856d6) + }, + @"systemOrangeColor": @{}, + @"systemPinkColor": @{}, + @"systemPurpleColor": @{}, + @"systemRedColor": @{}, + @"systemTealColor": @{}, + @"systemYellowColor": @{}, + // Adaptable Gray Colors + @"systemGrayColor": @{}, + @"systemGray2Color": @{ // iOS 13.0 + RCTFallbackARGB: @(0xFFaeaeb2) + }, + @"systemGray3Color": @{ // iOS 13.0 + RCTFallbackARGB: @(0xFFc7c7cc) + }, + @"systemGray4Color": @{ // iOS 13.0 + RCTFallbackARGB: @(0xFFd1d1d6) + }, + @"systemGray5Color": @{ // iOS 13.0 + RCTFallbackARGB: @(0xFFe5e5ea) + }, + @"systemGray6Color": @{ // iOS 13.0 + RCTFallbackARGB: @(0xFFf2f2f7) + }, +#endif #if DEBUG // The follow exist for Unit Tests @"unitTestFallbackColor": @{ RCTFallback: @"gridColor" }, + @"unitTestFallbackColorIOS": @{ + RCTFallback: @"blueColor" + }, @"unitTestFallbackColorEven": @{ RCTSelector: @"unitTestFallbackColorEven", RCTIndex: @0, @@ -635,13 +740,13 @@ + (type)type:(id)json \ return colorMap; } -/** Returns an NSColor based on a semantic color name. +/** Returns a UIColor based on a semantic color name. * Returns nil if the semantic color name is invalid. */ -static NSColor *RCTColorFromSemanticColorName(NSString *semanticColorName) +static RCTUIColor *RCTColorFromSemanticColorName(NSString *semanticColorName) { NSDictionary *colorMap = RCTSemanticColorsMap(); - NSColor *color = nil; + RCTUIColor *color = nil; NSDictionary *colorInfo = colorMap[semanticColorName]; if (colorInfo) { NSString *semanticColorSelector = colorInfo[RCTSelector]; @@ -649,16 +754,21 @@ + (type)type:(id)json \ semanticColorSelector = semanticColorName; } SEL selector = NSSelectorFromString(semanticColorSelector); - if (![NSColor respondsToSelector:selector]) { + if (![RCTUIColor respondsToSelector:selector]) { + NSNumber *fallbackRGB = colorInfo[RCTFallbackARGB]; + if (fallbackRGB != nil) { + RCTAssert([fallbackRGB isKindOfClass:[NSNumber class]], @"fallback ARGB is not a number"); + return [RCTConvert UIColor:fallbackRGB]; + } semanticColorSelector = colorInfo[RCTFallback]; selector = NSSelectorFromString(semanticColorSelector); } - RCTAssert ([NSColor respondsToSelector:selector], @"NSColor does not respond to a semantic color selector."); - Class klass = [NSColor class]; + RCTAssert ([RCTUIColor respondsToSelector:selector], @"RCTUIColor does not respond to a semantic color selector."); + Class klass = [RCTUIColor class]; IMP imp = [klass methodForSelector:selector]; id (*getSemanticColorObject)(id, SEL) = (void *)imp; id colorObject = getSemanticColorObject(klass, selector); - if ([colorObject isKindOfClass:[NSColor class]]) { + if ([colorObject isKindOfClass:[RCTUIColor class]]) { color = colorObject; } else if ([colorObject isKindOfClass:[NSArray class]]) { NSArray *colors = colorObject; @@ -688,9 +798,9 @@ + (type)type:(id)json \ } return names; } -#endif // ]TODO(macOS ISS#2323203) +// ]TODO(macOS ISS#2323203) -+ (UIColor *)UIColor:(id)json ++ (RCTUIColor *)UIColor:(id)json // TODO(OSS Candidate ISS#2710739) { if (!json) { return nil; @@ -698,46 +808,61 @@ + (UIColor *)UIColor:(id)json if ([json isKindOfClass:[NSArray class]]) { NSArray *components = [self NSNumberArray:json]; CGFloat alpha = components.count > 3 ? [self CGFloat:components[3]] : 1.0; - return [UIColor colorWithRed:[self CGFloat:components[0]] - green:[self CGFloat:components[1]] - blue:[self CGFloat:components[2]] - alpha:alpha]; + return [RCTUIColor colorWithRed:[self CGFloat:components[0]] // TODO(OSS Candidate ISS#2710739) + green:[self CGFloat:components[1]] + blue:[self CGFloat:components[2]] + alpha:alpha]; } else if ([json isKindOfClass:[NSNumber class]]) { NSUInteger argb = [self NSUInteger:json]; CGFloat a = ((argb >> 24) & 0xFF) / 255.0; CGFloat r = ((argb >> 16) & 0xFF) / 255.0; CGFloat g = ((argb >> 8) & 0xFF) / 255.0; CGFloat b = (argb & 0xFF) / 255.0; - return [UIColor colorWithRed:r green:g blue:b alpha:a]; -#if TARGET_OS_OSX // [TODO(macOS ISS#2323203) + return [RCTUIColor colorWithRed:r green:g blue:b alpha:a]; // TODO(OSS Candidate ISS#2710739) +// [TODO(macOS ISS#2323203) } else if ([json isKindOfClass:[NSDictionary class]]) { NSDictionary *dictionary = json; id value = nil; if ((value = [dictionary objectForKey:@"semantic"])) { NSString *semanticName = value; - NSColor *color = RCTColorFromSemanticColorName(semanticName); + RCTUIColor *color = RCTColorFromSemanticColorName(semanticName); if (color == nil) { - RCTLogConvertError(json, [@"an NSColor. Expected one of the following values: " stringByAppendingString:RCTSemanticColorNames()]); + RCTLogConvertError(json, [@"a UIColor. Expected one of the following values: " stringByAppendingString:RCTSemanticColorNames()]); } return color; } else if ((value = [dictionary objectForKey:@"dynamic"])) { NSDictionary *appearances = value; id light = [appearances objectForKey:@"light"]; - NSColor *lightColor = [RCTConvert UIColor:light]; + RCTUIColor *lightColor = [RCTConvert UIColor:light]; id dark = [appearances objectForKey:@"dark"]; - NSColor *darkColor = [RCTConvert UIColor:dark]; + RCTUIColor *darkColor = [RCTConvert UIColor:dark]; if (lightColor != nil && darkColor != nil) { +#if TARGET_OS_OSX RCTDynamicColor *color = [[RCTDynamicColor alloc] initWithAquaColor:lightColor darkAquaColor:darkColor]; return color; +#else +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 + if (@available(iOS 13.0, *)) { + UIColor *color = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull collection) { + return collection.userInterfaceStyle == UIUserInterfaceStyleLight ? lightColor : darkColor; + }]; + return color; + } else { +#endif + return lightColor; +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 + } +#endif +#endif } else { - RCTLogConvertError(json, @"an NSColor. Expected a mac dynamic appearance aware color."); + RCTLogConvertError(json, @"a UIColor. Expected a mac dynamic appearance aware color."); return nil; } } else { - RCTLogConvertError(json, @"an NSColor. Expected a mac semantic color or dynamic appearance aware color."); + RCTLogConvertError(json, @"a UIColor. Expected a mac semantic color or dynamic appearance aware color."); return nil; } -#endif // [TODO(macOS ISS#2323203) +// ]TODO(macOS ISS#2323203) } else { RCTLogConvertError(json, @"a UIColor. Did you forget to call processColor() on the JS side?"); return nil; @@ -797,7 +922,7 @@ + (YGValue)YGValue:(id)json RCT_ARRAY_CONVERTER(NSURL) RCT_ARRAY_CONVERTER(RCTFileURL) -RCT_ARRAY_CONVERTER(UIColor) +RCT_ARRAY_CONVERTER(RCTUIColor) // TODO(OSS Candidate ISS#2710739) /** * This macro is used for creating converter functions for directly diff --git a/React/Base/RCTDisplayLink.m b/React/Base/RCTDisplayLink.m index eefd42a89e26e9..04877594f7dc33 100644 --- a/React/Base/RCTDisplayLink.m +++ b/React/Base/RCTDisplayLink.m @@ -22,7 +22,8 @@ @implementation RCTDisplayLink { - CADisplayLink *_jsDisplayLink; + RCTPlatformDisplayLink *_jsDisplayLink; // TODO(macOS ISS#2323203) + NSMutableSet *_frameUpdateObservers; NSRunLoop *_runLoop; } @@ -31,7 +32,7 @@ - (instancetype)init { if ((self = [super init])) { _frameUpdateObservers = [NSMutableSet new]; - _jsDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_jsThreadUpdate:)]; + _jsDisplayLink = [RCTPlatformDisplayLink displayLinkWithTarget:self selector:@selector(_jsThreadUpdate:)]; // TODO(macOS ISS#2323203) } return self; @@ -102,7 +103,7 @@ - (void)dispatchBlock:(dispatch_block_t)block } } -- (void)_jsThreadUpdate:(CADisplayLink *)displayLink +- (void)_jsThreadUpdate:(RCTPlatformDisplayLink *)displayLink // TODO(macOS ISS#2323203) { RCTAssertRunLoop(); diff --git a/React/Base/RCTFrameUpdate.h b/React/Base/RCTFrameUpdate.h index 1e93491552f5b4..d77510411361d3 100644 --- a/React/Base/RCTFrameUpdate.h +++ b/React/Base/RCTFrameUpdate.h @@ -6,8 +6,7 @@ */ #import - -@class CADisplayLink; +#import "RCTPlatformDisplayLink.h" // TODO(macOS ISS#2323203) /** * Interface containing the information about the last screen refresh. @@ -24,7 +23,7 @@ */ @property (nonatomic, readonly) NSTimeInterval deltaTime; -- (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithDisplayLink:(RCTPlatformDisplayLink *)displayLink NS_DESIGNATED_INITIALIZER; // TODO(OSS Candidate ISS#2710739) @end diff --git a/React/Base/RCTFrameUpdate.m b/React/Base/RCTFrameUpdate.m index 981c663a842b5e..1f12af4a5f62ef 100644 --- a/React/Base/RCTFrameUpdate.m +++ b/React/Base/RCTFrameUpdate.m @@ -15,7 +15,7 @@ @implementation RCTFrameUpdate RCT_NOT_IMPLEMENTED(- (instancetype)init) -- (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink +- (instancetype)initWithDisplayLink:(RCTPlatformDisplayLink *)displayLink // TODO(macOS ISS#2323203) { if ((self = [super init])) { _timestamp = displayLink.timestamp; diff --git a/React/Base/RCTModuleMethod.mm b/React/Base/RCTModuleMethod.mm index e9a36575849c11..d228667a2105ba 100644 --- a/React/Base/RCTModuleMethod.mm +++ b/React/Base/RCTModuleMethod.mm @@ -91,6 +91,7 @@ static BOOL RCTParseSelectorPart(const char **input, NSMutableString *selector) static BOOL RCTParseUnused(const char **input) { return RCTReadString(input, "__unused") || + RCTReadString(input, "__attribute__((__unused__))") || RCTReadString(input, "__attribute__((unused))"); } diff --git a/React/Base/RCTPlatformDisplayLink.h b/React/Base/RCTPlatformDisplayLink.h index fc1910b3ef10f6..c81f017ec725d1 100644 --- a/React/Base/RCTPlatformDisplayLink.h +++ b/React/Base/RCTPlatformDisplayLink.h @@ -11,6 +11,7 @@ #if !TARGET_OS_OSX #import +#define RCTPlatformDisplayLink CADisplayLink #else #import @@ -35,8 +36,6 @@ NS_ASSUME_NONNULL_BEGIN @end -@compatibility_alias CADisplayLink RCTPlatformDisplayLink; - NS_ASSUME_NONNULL_END #endif diff --git a/React/Base/RCTUIKit.h b/React/Base/RCTUIKit.h index 71cc506d938416..3d0b35a22b5e7b 100644 --- a/React/Base/RCTUIKit.h +++ b/React/Base/RCTUIKit.h @@ -107,6 +107,8 @@ UIKIT_STATIC_INLINE CGRect CGRectValue(NSValue *value) // semantically equivalent types // +#define RCTUIColor UIColor + UIKIT_STATIC_INLINE UIFont *UIFontWithSize(UIFont *font, CGFloat pointSize) { return [font fontWithSize:pointSize]; @@ -269,7 +271,7 @@ void UIGraphicsEndImageContext(void); // // UIColor.h/NSColor.h -@compatibility_alias UIColor NSColor; +#define RCTUIColor NSColor // UIFont.h/NSFont.h // Both NSFont and UIFont are toll-free bridged to CTFontRef so we'll assume they're semantically equivalent diff --git a/React/Base/RCTUtils.h b/React/Base/RCTUtils.h index 5e1ce717a4015f..516a204dc024b2 100644 --- a/React/Base/RCTUtils.h +++ b/React/Base/RCTUtils.h @@ -146,6 +146,9 @@ RCT_EXTERN UIImage *__nullable RCTImageFromLocalAssetURL(NSURL *imageURL); // Creates a new, unique temporary file path with the specified extension RCT_EXTERN NSString *__nullable RCTTempFilePath(NSString *__nullable extension, NSError **error); +// Get RGBA components of CGColor +RCT_EXTERN void RCTGetRGBAColorComponents(CGColorRef color, CGFloat rgba[_Nonnull 4]); // TODO(OSS Candidate ISS#2710739) + // Converts a CGColor to a hex string RCT_EXTERN NSString *RCTColorToHexString(CGColorRef color); diff --git a/React/Base/RCTUtils.m b/React/Base/RCTUtils.m index 683b9eea97ec1b..526add2a1cdefb 100644 --- a/React/Base/RCTUtils.m +++ b/React/Base/RCTUtils.m @@ -843,7 +843,7 @@ BOOL RCTIsLocalAssetURL(NSURL *__nullable imageURL) return [directory stringByAppendingPathComponent:filename]; } -static void RCTGetRGBAColorComponents(CGColorRef color, CGFloat rgba[4]) +RCT_EXTERN void RCTGetRGBAColorComponents(CGColorRef color, CGFloat rgba[_Nonnull 4]) // TODO(OSS Candidate ISS#2710739) { CGColorSpaceModel model = CGColorSpaceGetModel(CGColorGetColorSpace(color)); const CGFloat *components = CGColorGetComponents(color); diff --git a/React/DevSupport/RCTDevLoadingView.h b/React/DevSupport/RCTDevLoadingView.h index be39b14ab5b4ed..86f9fb8fbd01ee 100644 --- a/React/DevSupport/RCTDevLoadingView.h +++ b/React/DevSupport/RCTDevLoadingView.h @@ -14,7 +14,7 @@ @interface RCTDevLoadingView : NSObject + (void)setEnabled:(BOOL)enabled; -- (void)showMessage:(NSString *)message color:(UIColor *)color backgroundColor:(UIColor *)backgroundColor; +- (void)showMessage:(NSString *)message color:(RCTUIColor *)color backgroundColor:(RCTUIColor *)backgroundColor; // TODO(OSS Candidate ISS#2710739) - (void)showWithURL:(NSURL *)URL; - (void)updateProgress:(RCTLoadingProgress *)progress; - (void)hide; diff --git a/React/DevSupport/RCTDevLoadingView.m b/React/DevSupport/RCTDevLoadingView.m index 1c45005fb04fad..4cc82ab89e2d2a 100644 --- a/React/DevSupport/RCTDevLoadingView.m +++ b/React/DevSupport/RCTDevLoadingView.m @@ -67,7 +67,7 @@ - (void)setBridge:(RCTBridge *)bridge } } -RCT_EXPORT_METHOD(showMessage:(NSString *)message color:(UIColor *)color backgroundColor:(UIColor *)backgroundColor) +RCT_EXPORT_METHOD(showMessage:(NSString *)message color:(RCTUIColor *)color backgroundColor:(RCTUIColor *)backgroundColor) // TODO(OSS Candidate ISS#2710739) { if (!isEnabled) { return; @@ -167,20 +167,20 @@ - (void)setBridge:(RCTBridge *)bridge - (void)showWithURL:(NSURL *)URL { - UIColor *color; - UIColor *backgroundColor; + RCTUIColor *color; // TODO(OSS Candidate ISS#2710739) + RCTUIColor *backgroundColor; // TODO(OSS Candidate ISS#2710739) NSString *source; if (URL.fileURL) { // If dev mode is not enabled, we don't want to show this kind of notification #if !RCT_DEV return; #endif - color = [UIColor grayColor]; - backgroundColor = [UIColor blackColor]; + color = [RCTUIColor grayColor]; // TODO(OSS Candidate ISS#2710739) + backgroundColor = [RCTUIColor blackColor]; // TODO(OSS Candidate ISS#2710739) source = @"pre-bundled file"; } else { - color = [UIColor whiteColor]; - backgroundColor = [UIColor colorWithHue:1./3 saturation:1 brightness:.35 alpha:1]; + color = [RCTUIColor whiteColor]; // TODO(OSS Candidate ISS#2710739) + backgroundColor = [RCTUIColor colorWithHue:1./3 saturation:1 brightness:.35 alpha:1]; // TODO(OSS Candidate ISS#2710739) source = [NSString stringWithFormat:@"%@:%@", URL.host, URL.port]; } diff --git a/React/Views/RCTActivityIndicatorView.h b/React/Views/RCTActivityIndicatorView.h index 1f5c8a0ade3725..70ec287838e4df 100644 --- a/React/Views/RCTActivityIndicatorView.h +++ b/React/Views/RCTActivityIndicatorView.h @@ -12,7 +12,7 @@ #if TARGET_OS_OSX // [TODO(macOS ISS#2323203) @property (nonatomic, assign) UIActivityIndicatorViewStyle activityIndicatorViewStyle; @property (nonatomic, assign) BOOL hidesWhenStopped; -@property (nullable, readwrite, nonatomic, strong) UIColor *color; +@property (nullable, readwrite, nonatomic, strong) RCTUIColor *color; // TODO(OSS Candidate ISS#2710739) @property (nonatomic, readonly, getter=isAnimating) BOOL animating; - (void)startAnimating; - (void)stopAnimating; diff --git a/React/Views/RCTActivityIndicatorView.m b/React/Views/RCTActivityIndicatorView.m index 7ae4eecda960ea..af79b6abc82789 100644 --- a/React/Views/RCTActivityIndicatorView.m +++ b/React/Views/RCTActivityIndicatorView.m @@ -66,7 +66,7 @@ - (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndi } } -- (void)setColor: (UIColor*)color +- (void)setColor: (RCTUIColor*)color { if (_color != color) { _color = color; diff --git a/React/Views/RCTBorderDrawing.m b/React/Views/RCTBorderDrawing.m index c597e5c8c318ff..5542ae8265021e 100644 --- a/React/Views/RCTBorderDrawing.m +++ b/React/Views/RCTBorderDrawing.m @@ -526,7 +526,7 @@ static CGContextRef RCTUIGraphicsBeginImageContext(CGSize size, CGColorRef backg CGContextSetLineWidth(ctx, lineWidth); CGContextSetLineDash(ctx, 0, dashLengths, sizeof(dashLengths) / sizeof(*dashLengths)); - CGContextSetStrokeColorWithColor(ctx, [UIColor yellowColor].CGColor); + CGContextSetStrokeColorWithColor(ctx, [RCTUIColor yellowColor].CGColor); // TODO(OSS Candidate ISS#2710739) CGContextAddPath(ctx, path); CGContextSetStrokeColorWithColor(ctx, borderColors.top); diff --git a/React/Views/RCTPicker.h b/React/Views/RCTPicker.h index 0b85556a8c0d33..1681b52a1a2006 100644 --- a/React/Views/RCTPicker.h +++ b/React/Views/RCTPicker.h @@ -17,7 +17,7 @@ @property (nonatomic, copy) NSArray *items; @property (nonatomic, assign) NSInteger selectedIndex; -@property (nonatomic, strong) UIColor *color; +@property (nonatomic, strong) RCTUIColor *color; // TODO(OSS Candidate ISS#2710739) @property (nonatomic, strong) UIFont *font; @property (nonatomic, assign) NSTextAlignment textAlign; diff --git a/React/Views/RCTPicker.m b/React/Views/RCTPicker.m index f18ee823132d33..4e72060dd79b63 100644 --- a/React/Views/RCTPicker.m +++ b/React/Views/RCTPicker.m @@ -23,7 +23,7 @@ @implementation RCTPicker - (instancetype)initWithFrame:(CGRect)frame { if ((self = [super initWithFrame:frame])) { - _color = [UIColor blackColor]; + _color = [RCTUIColor blackColor]; // TODO(OSS Candidate ISS#2710739) #if !TARGET_OS_OSX // TODO(macOS ISS#2323203) _font = [UIFont systemFontOfSize:21]; // TODO: selected title default should be 23.5 #else // [TODO(macOS ISS#2323203) diff --git a/React/Views/RCTProgressView.h b/React/Views/RCTProgressView.h index 622886b771bd6e..42671cff7943a0 100644 --- a/React/Views/RCTProgressView.h +++ b/React/Views/RCTProgressView.h @@ -17,8 +17,8 @@ #endif #if TARGET_OS_OSX -@property (nonatomic, strong, nullable) UIColor *progressTintColor; -@property (nonatomic, strong, nullable) UIColor *trackTintColor; +@property (nonatomic, strong, nullable) RCTUIColor *progressTintColor; +@property (nonatomic, strong, nullable) RCTUIColor *trackTintColor; @property(nonatomic, strong, nullable) UIImage *progressImage; @property(nonatomic, strong, nullable) UIImage *trackImage; #endif diff --git a/React/Views/RCTProgressView.m b/React/Views/RCTProgressView.m index c56d1668ab4e41..404959ac766da7 100644 --- a/React/Views/RCTProgressView.m +++ b/React/Views/RCTProgressView.m @@ -19,7 +19,7 @@ - (instancetype)initWithFrame:(CGRect)frame self.indeterminate = NO; // Default track color from NSProgressIndicator. - self.trackTintColor = [UIColor colorWithRed: 237/255.0 green:237/255.0 blue:237/255.0 alpha:1.0]; + self.trackTintColor = [RCTUIColor colorWithRed: 237/255.0 green:237/255.0 blue:237/255.0 alpha:1.0]; } return self; } diff --git a/React/Views/RCTView.m b/React/Views/RCTView.m index 07cf36ef663959..b11a0b7362bf98 100644 --- a/React/Views/RCTView.m +++ b/React/Views/RCTView.m @@ -103,7 +103,7 @@ - (RCTPlatformView *)react_findClipView // TODO(macOS ISS#2323203) @implementation RCTView { - UIColor *_backgroundColor; + RCTUIColor *_backgroundColor; // TODO(OSS Candidate ISS#2710739) #if TARGET_OS_OSX // [TODO(macOS ISS#2323203) NSTrackingArea *_trackingArea; BOOL _hasMouseOver; @@ -598,16 +598,30 @@ - (BOOL)resignFirstResponder } return YES; } + +#if !TARGET_OS_OSX +- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { + [super traitCollectionDidChange: previousTraitCollection]; +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 + if (@available(iOS 13.0, *)) { + if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) { + [self.layer setNeedsDisplay]; + } + } +#endif +} +#endif // !TARGET_OS_OSX + // ]TODO(OSS Candidate ISS#2710739) #pragma mark - Borders -- (UIColor *)backgroundColor +- (RCTUIColor *)backgroundColor // TODO(OSS Candidate ISS#2710739) { return _backgroundColor; } -- (void)setBackgroundColor:(UIColor *)backgroundColor +- (void)setBackgroundColor:(RCTUIColor *)backgroundColor // TODO(OSS Candidate ISS#2710739) { if ([_backgroundColor isEqual:backgroundColor]) { return; @@ -776,6 +790,16 @@ - (void)displayLayer:(CALayer *)layer // solve this, we'll need to add a container view inside the main view to // correctly clip the subviews. +#if !TARGET_OS_OSX + id savedTraitCollection = nil; +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 + if (@available(iOS 13.0, *)) { + savedTraitCollection = [UITraitCollection currentTraitCollection]; + [UITraitCollection setCurrentTraitCollection:[self traitCollection]]; + } +#endif +#endif + if (useIOSBorderRendering) { layer.cornerRadius = cornerRadii.topLeft; layer.borderColor = borderColors.left; @@ -787,6 +811,14 @@ - (void)displayLayer:(CALayer *)layer return; } +#if !TARGET_OS_OSX +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 + if (@available(iOS 13.0, *)) { + [UITraitCollection setCurrentTraitCollection:savedTraitCollection]; + } +#endif +#endif + #if TARGET_OS_OSX // [TODO(macOS ISS#2323203) CGFloat scaleFactor = self.window.backingScaleFactor; if (scaleFactor == 0.0 && RCTRunningInTestEnvironment()) {