Support dark mode.
diff --git a/ios/sdk/WeexSDK.xcodeproj/project.pbxproj b/ios/sdk/WeexSDK.xcodeproj/project.pbxproj
index e006b27..3502f78 100644
--- a/ios/sdk/WeexSDK.xcodeproj/project.pbxproj
+++ b/ios/sdk/WeexSDK.xcodeproj/project.pbxproj
@@ -618,6 +618,12 @@
D735F1B222D761F800B53CDF /* log_utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D735F1AC22D761F800B53CDF /* log_utils.cpp */; };
D735F1B322D761F800B53CDF /* log_utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D735F1AC22D761F800B53CDF /* log_utils.cpp */; };
D77286FF22C9B22C00E1DA7D /* eagle_bridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BD9205FA223651D800EDF93D /* eagle_bridge.cpp */; };
+ D7C96CF3237AA13400A4599C /* WXDarkThemeProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D7C96CF2237AA13400A4599C /* WXDarkThemeProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D7C96CF4237AA13400A4599C /* WXDarkThemeProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D7C96CF2237AA13400A4599C /* WXDarkThemeProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D7C96CF7237AA16100A4599C /* WXDarkThemeDefaultImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = D7C96CF5237AA16100A4599C /* WXDarkThemeDefaultImpl.h */; };
+ D7C96CF8237AA16100A4599C /* WXDarkThemeDefaultImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = D7C96CF5237AA16100A4599C /* WXDarkThemeDefaultImpl.h */; };
+ D7C96CF9237AA16100A4599C /* WXDarkThemeDefaultImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = D7C96CF6237AA16100A4599C /* WXDarkThemeDefaultImpl.m */; };
+ D7C96CFA237AA16100A4599C /* WXDarkThemeDefaultImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = D7C96CF6237AA16100A4599C /* WXDarkThemeDefaultImpl.m */; };
DC03ADB91D508719003F76E7 /* WXTextAreaComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = DC03ADB71D508719003F76E7 /* WXTextAreaComponent.mm */; };
DC03ADBA1D508719003F76E7 /* WXTextAreaComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = DC03ADB81D508719003F76E7 /* WXTextAreaComponent.h */; };
DC15A3DB2010BC93009C8977 /* weex-main-jsfm.js in Resources */ = {isa = PBXBuildFile; fileRef = DC15A3D92010BC93009C8977 /* weex-main-jsfm.js */; };
@@ -1351,6 +1357,9 @@
D3FC0DF61C508B2A002B9E31 /* WXTimerModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXTimerModule.m; sourceTree = "<group>"; };
D735F1AB22D761F800B53CDF /* log_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = log_utils.h; sourceTree = "<group>"; };
D735F1AC22D761F800B53CDF /* log_utils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = log_utils.cpp; sourceTree = "<group>"; };
+ D7C96CF2237AA13400A4599C /* WXDarkThemeProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXDarkThemeProtocol.h; sourceTree = "<group>"; };
+ D7C96CF5237AA16100A4599C /* WXDarkThemeDefaultImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXDarkThemeDefaultImpl.h; sourceTree = "<group>"; };
+ D7C96CF6237AA16100A4599C /* WXDarkThemeDefaultImpl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXDarkThemeDefaultImpl.m; sourceTree = "<group>"; };
DAB176F008F516E4F9391C61 /* libPods-WeexSDK.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-WeexSDK.a"; sourceTree = BUILT_PRODUCTS_DIR; };
DC03ADB71D508719003F76E7 /* WXTextAreaComponent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WXTextAreaComponent.mm; sourceTree = "<group>"; };
DC03ADB81D508719003F76E7 /* WXTextAreaComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXTextAreaComponent.h; sourceTree = "<group>"; };
@@ -1476,6 +1485,8 @@
59A583031CF5B2FD0081FD3E /* Handler */ = {
isa = PBXGroup;
children = (
+ D7C96CF5237AA16100A4599C /* WXDarkThemeDefaultImpl.h */,
+ D7C96CF6237AA16100A4599C /* WXDarkThemeDefaultImpl.m */,
33CE190C2153443000CF9670 /* WXJSFrameworkLoadDefaultImpl.h */,
33CE190D2153443000CF9670 /* WXJSFrameworkLoadDefaultImpl.m */,
59A583041CF5B2FD0081FD3E /* WXNavigationDefaultImpl.h */,
@@ -1857,6 +1868,7 @@
77D1611C1C02DD3C0010B15B /* Protocol */ = {
isa = PBXGroup;
children = (
+ D7C96CF2237AA13400A4599C /* WXDarkThemeProtocol.h */,
33CE19122153444900CF9670 /* WXJSFrameworkLoadProtocol.h */,
17036A5220FDE7490029AE3D /* WXApmProtocol.h */,
17C74F0E2072147A00AB4CAB /* WXAnalyzerProtocol.h */,
@@ -2410,6 +2422,7 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
+ D7C96CF3237AA13400A4599C /* WXDarkThemeProtocol.h in Headers */,
F75C591C2313C1FC002FFF94 /* WXStreamModule.h in Headers */,
74A4BA9E1CB3C0A100195969 /* WXHandlerFactory.h in Headers */,
59A583081CF5B2FD0081FD3E /* WXNavigationDefaultImpl.h in Headers */,
@@ -2497,6 +2510,7 @@
59CE27E81CC387DB000BE37A /* WXEmbedComponent.h in Headers */,
B8D66C0F21255730003960BD /* core_side_in_platform.h in Headers */,
B8D66C9121255730003960BD /* render_factory_interface.h in Headers */,
+ D7C96CF7237AA16100A4599C /* WXDarkThemeDefaultImpl.h in Headers */,
DCE2CF9B1F46D4220021BDC4 /* WXVoiceOverModule.h in Headers */,
453F3756219A76CA00A03F1D /* default_request_handler.h in Headers */,
74BB5FB91DFEE81A004FC3DF /* WXMetaModule.h in Headers */,
@@ -2656,6 +2670,7 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
+ D7C96CF4237AA13400A4599C /* WXDarkThemeProtocol.h in Headers */,
DCA4461D1EFA5AAA00D0CFA8 /* WXHandlerFactory.h in Headers */,
DCA446101EFA5A8500D0CFA8 /* WXBridgeMethod.h in Headers */,
DCA4461A1EFA5AA000D0CFA8 /* WXInvocationConfig.h in Headers */,
@@ -2724,6 +2739,7 @@
DCA445A21EFA570100D0CFA8 /* WXScrollerComponent.h in Headers */,
B8D66C7621255730003960BD /* render_object.h in Headers */,
DCA445B71EFA579200D0CFA8 /* WXImgLoaderProtocol.h in Headers */,
+ D7C96CF8237AA16100A4599C /* WXDarkThemeDefaultImpl.h in Headers */,
1746EA7420E9D253007E55BD /* WXComponent_performance.h in Headers */,
B8D66CEB21255B2A003960BD /* WXWebSocketLoader.h in Headers */,
DCA445C21EFA57D700D0CFA8 /* WXBaseViewController.h in Headers */,
@@ -3324,6 +3340,7 @@
749DC27C1D40827B009E1C91 /* WXMonitor.m in Sources */,
C4B834271DE69B09007AD27E /* WXPickerModule.m in Sources */,
745B2D691E5A8E1E0092D38A /* WXMultiColumnLayout.m in Sources */,
+ D7C96CF9237AA16100A4599C /* WXDarkThemeDefaultImpl.m in Sources */,
77788B752229252D000D5102 /* render_page_custom.cpp in Sources */,
77D161391C02DE940010B15B /* WXBridgeManager.m in Sources */,
74BF19F91F5139BB00AEE3D7 /* WXJSASTParser.mm in Sources */,
@@ -3515,6 +3532,7 @@
DCA445981EFA55B300D0CFA8 /* WXSDKInstance.m in Sources */,
DCA445991EFA55B300D0CFA8 /* WXJSExceptionInfo.m in Sources */,
DCA4459A1EFA55B300D0CFA8 /* WXResourceRequest.m in Sources */,
+ D7C96CFA237AA16100A4599C /* WXDarkThemeDefaultImpl.m in Sources */,
77788B762229252D000D5102 /* render_page_custom.cpp in Sources */,
DCA4459B1EFA55B300D0CFA8 /* WXResourceRequestHandlerDefaultImpl.m in Sources */,
DCA4459C1EFA55B300D0CFA8 /* WXResourceResponse.m in Sources */,
diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXJSASTParser.mm b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXJSASTParser.mm
index cfea370..2d26ff0 100644
--- a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXJSASTParser.mm
+++ b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXJSASTParser.mm
@@ -760,7 +760,7 @@
- (WXJSExpression *)parsePrimaryExpression
{
- int type = _lookahead->type;
+ WXJSTokenType type = _lookahead->type;
if (type == WXJSTokenTypePunctuator) {
if (_lookahead->value == "[") {
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h b/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h
index a97dfcb..6dff036 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h
+++ b/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h
@@ -50,7 +50,9 @@
* View
*/
UIColor *_styleBackgroundColor;
+ UIColor *_darkThemeBackgroundColor;
NSString *_backgroundImage;
+ NSString *_darkThemeBackgroundImage;
NSString *_clipRadius;
WXClipType _clipToBounds;
UIView *_view;
@@ -115,9 +117,13 @@
WXThreadSafeCounter *_displayCounter;
UIColor *_borderTopColor;
+ UIColor *_darkThemeBorderTopColor;
UIColor *_borderRightColor;
+ UIColor *_darkThemeBorderRightColor;
UIColor *_borderLeftColor;
+ UIColor *_darkThemeBorderLeftColor;
UIColor *_borderBottomColor;
+ UIColor *_darkThemeBorderBottomColor;
CGFloat _borderTopWidth;
CGFloat _borderRightWidth;
@@ -179,6 +185,7 @@
DO NOT use "_backgroundColor" directly. The same reason as '_transform'.
*/
@property (atomic, strong) UIColor* styleBackgroundColor;
+@property (atomic, strong) UIColor* darkThemeBackgroundColor;
///--------------------------------------
/// @name Package Internal Methods
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXImageComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXImageComponent.m
index 1cb8245..2a83fee 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXImageComponent.m
+++ b/ios/sdk/WeexSDK/Sources/Component/WXImageComponent.m
@@ -53,14 +53,14 @@
@interface WXImageComponent ()
{
- NSString * _imageSrc;
- pthread_mutex_t _imageSrcMutex;
- pthread_mutexattr_t _propertMutexAttr;
BOOL _shouldUpdateImage;
BOOL _mainImageSuccess;
}
+@property (atomic, strong) NSString *src;
+@property (atomic, strong) NSString *darkThemeSrc;
@property (atomic, strong) NSString *placeholdSrc;
+@property (atomic, strong) NSString *darkThemePlaceholderSrc;
@property (nonatomic, assign) CGFloat blurRadius;
@property (nonatomic, assign) UIViewContentMode resizeMode;
@property (nonatomic, assign) WXImageQuality imageQuality;
@@ -71,7 +71,7 @@
@property (nonatomic) BOOL imageLoadEvent;
@property (nonatomic) BOOL imageDownloadFinish;
@property (nonatomic) BOOL downloadImageWithURL;
-@property (nonatomic ,strong) NSString* preUrl;
+@property (nonatomic, strong) NSString* preUrl;
@end
@@ -88,17 +88,15 @@
WXImageUpdateQueue = dispatch_queue_create("com.taobao.weex.ImageUpdateQueue", DISPATCH_QUEUE_SERIAL);
}
- pthread_mutexattr_init(&(_propertMutexAttr));
- pthread_mutexattr_settype(&(_propertMutexAttr), PTHREAD_MUTEX_RECURSIVE);
- pthread_mutex_init(&(_imageSrcMutex), &(_propertMutexAttr));
-
if (attributes[@"src"]) {
- pthread_mutex_lock(&(_imageSrcMutex));
- _imageSrc = [[WXConvert NSString:attributes[@"src"]] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
- pthread_mutex_unlock(&(_imageSrcMutex));
+ self.src = [[WXConvert NSString:attributes[@"src"]] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
} else {
WXLogWarning(@"image src is nil");
}
+ if (attributes[@"darkThemeSrc"]) {
+ self.darkThemeSrc = [[WXConvert NSString:attributes[@"darkThemeSrc"]] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ }
+
[self configPlaceHolder:attributes];
NSString *resizeMode = attributes[@"resize"];
@@ -135,6 +133,9 @@
if (attributes[@"placeHolder"] || attributes[@"placeholder"]) {
self.placeholdSrc = [[WXConvert NSString:attributes[@"placeHolder"]?:attributes[@"placeholder"]]stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
+ if (attributes[@"darkThemePlaceholder"]) {
+ self.darkThemePlaceholderSrc = [[WXConvert NSString:attributes[@"darkThemePlaceholder"]] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ }
}
- (void)configFilter:(NSDictionary *)styles needUpdate:(BOOL)needUpdate
@@ -258,12 +259,13 @@
- (void)dealloc
{
[self cancelImage];
- pthread_mutex_destroy(&(_imageSrcMutex));
- pthread_mutexattr_destroy(&_propertMutexAttr);
}
- (void)updateAttributes:(NSDictionary *)attributes
{
+ if (attributes[@"darkThemeSrc"]) {
+ self.darkThemeSrc = [[WXConvert NSString:attributes[@"darkThemeSrc"]] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ }
if (attributes[@"src"]) {
[self setImageSrc:[[WXConvert NSString:attributes[@"src"]] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
}
@@ -293,7 +295,16 @@
[self _clipsToBounds];
[self updateImage];
-
+}
+
+- (void)themeDidChange:(NSString*)theme
+{
+ [super themeDidChange:theme];
+ if (_view) {
+ if (self.darkThemeSrc || self.darkThemePlaceholderSrc) {
+ [self updateImage];
+ }
+ }
}
- (BOOL)_needsDrawBorder
@@ -358,26 +369,15 @@
}
}
-- (NSString *)imageSrc
-{
- pthread_mutex_lock(&(_imageSrcMutex));
- NSString * imageSrcCpy = [_imageSrc copy];
- pthread_mutex_unlock(&(_imageSrcMutex));
-
- return imageSrcCpy;
-}
-
- (void)setImageSrc:(NSString*)src
{
- if ([src isEqualToString:_imageSrc]) {
+ if ([src isEqualToString:self.src]) {
// if image src is equal to then ignore it.
return;
}
- pthread_mutex_lock(&(_imageSrcMutex));
- _imageSrc = src;
+ self.src = src;
_imageDownloadFinish = NO;
((UIImageView*)self.view).image = nil;
- pthread_mutex_unlock(&(_imageSrcMutex));
[self updateImage];
}
@@ -397,14 +397,19 @@
_shouldUpdateImage = YES;
return;
}
+
+ BOOL isDarkMode = [self.weexInstance isDarkTheme];
+ NSString* choosedSrc = isDarkMode ? (self.darkThemeSrc ?: self.src) : self.src;
+ NSString* choosedPlaceholder = isDarkMode ? (self.darkThemePlaceholderSrc ?: self.placeholdSrc) : self.placeholdSrc;
+
__weak typeof(self) weakSelf = self;
if (_downloadImageWithURL && [[self imageLoader] respondsToSelector:@selector(setImageViewWithURL:url:placeholderImage:options:progress:completed:)]) {
_mainImageSuccess = NO;
NSString *newURL = nil;
- if (self.placeholdSrc) {
- newURL = [self.placeholdSrc copy];
- WX_REWRITE_URL([self placeholdSrc], WXResourceTypeImage, self.weexInstance)
+ if (choosedPlaceholder) {
+ newURL = [choosedPlaceholder copy];
+ WX_REWRITE_URL(choosedPlaceholder, WXResourceTypeImage, self.weexInstance)
NSDictionary* extInfo = @{@"instanceId":[self _safeInstanceId], @"pageURL": self.weexInstance.scriptURL ?: @""};
[[self imageLoader] setImageViewWithURL:(UIImageView*)self.view url:[NSURL URLWithString:newURL] placeholderImage:nil options:extInfo progress:nil completed:^(UIImage *image, NSError *error, WXImageLoaderCacheType cacheType, NSURL *imageURL) {
/* We cannot rely on image library even if we call setImage with placeholer before calling setImage with real url.
@@ -414,8 +419,8 @@
UIImageView *imageView = (UIImageView *)strongSelf.view;
if (imageView && imageView.image == image && strongSelf->_mainImageSuccess) {
// reload image with main image url
- NSString* newURL = [[strongSelf imageSrc] copy];
- WX_REWRITE_URL([strongSelf imageSrc], WXResourceTypeImage, strongSelf.weexInstance)
+ NSString* newURL = [choosedSrc copy];
+ WX_REWRITE_URL(choosedSrc, WXResourceTypeImage, strongSelf.weexInstance)
NSDictionary *userInfo = @{@"imageQuality":@(strongSelf.imageQuality), @"imageSharp":@(strongSelf.imageSharp), @"blurRadius":@(strongSelf.blurRadius), @"instanceId":[strongSelf _safeInstanceId], @"pageURL": strongSelf.weexInstance.scriptURL ?: @""};
[[strongSelf imageLoader] setImageViewWithURL:imageView url:[NSURL URLWithString:newURL] placeholderImage:nil options:userInfo progress:nil completed:^(UIImage *image, NSError *error, WXImageLoaderCacheType cacheType, NSURL *imageURL) {
WXLogInfo(@"Image re-requested because placeholder may override main image. %@", imageURL);
@@ -424,11 +429,11 @@
}
}];
}
- newURL = [[self imageSrc] copy];
+ newURL = [choosedSrc copy];
if ([newURL length] == 0) {
return;
}
- WX_REWRITE_URL([self imageSrc], WXResourceTypeImage, self.weexInstance)
+ WX_REWRITE_URL(choosedSrc, WXResourceTypeImage, self.weexInstance)
NSDictionary *userInfo = @{@"imageQuality":@(self.imageQuality), @"imageSharp":@(self.imageSharp), @"blurRadius":@(self.blurRadius), @"instanceId":[self _safeInstanceId], @"pageURL": self.weexInstance.scriptURL ?: @""};
[[self imageLoader] setImageViewWithURL:(UIImageView*)self.view url:[NSURL URLWithString:newURL] placeholderImage:nil options:userInfo progress:^(NSInteger receivedSize, NSInteger expectedSize) {
// progress when loading image
@@ -444,9 +449,9 @@
WXLogError(@"Error downloading image: %@, detail:%@", imageURL.absoluteString, [error localizedDescription]);
// retry set placeholder, maybe placeholer image can be downloaded
- if (strongSelf.placeholdSrc) {
- NSString *newURL = [strongSelf.placeholdSrc copy];
- WX_REWRITE_URL([strongSelf placeholdSrc], WXResourceTypeImage, strongSelf.weexInstance)
+ if (choosedPlaceholder) {
+ NSString *newURL = [choosedPlaceholder copy];
+ WX_REWRITE_URL(choosedPlaceholder, WXResourceTypeImage, strongSelf.weexInstance)
[[strongSelf imageLoader] setImageViewWithURL:(UIImageView*)strongSelf.view
url:[NSURL URLWithString:newURL]
placeholderImage:nil
@@ -513,7 +518,7 @@
[strongSelf updatePlaceHolderWithFailedBlock:downloadFailed];
[strongSelf updateContentImageWithFailedBlock:downloadFailed];
- if (!strongSelf.imageSrc && !strongSelf.placeholdSrc) {
+ if (!choosedSrc && !choosedPlaceholder) {
dispatch_async(dispatch_get_main_queue(), ^{
strongSelf.layer.contents = nil;
strongSelf.imageDownloadFinish = YES;
@@ -526,15 +531,16 @@
- (void)updatePlaceHolderWithFailedBlock:(void(^)(NSString *, NSError *))downloadFailedBlock
{
- NSString *placeholderSrc = self.placeholdSrc;
+ BOOL isDarkMode = [self.weexInstance isDarkTheme];
+ NSString* choosedPlaceholder = isDarkMode ? (self.darkThemePlaceholderSrc ?: self.placeholdSrc) : self.placeholdSrc;
- if ([WXUtility isBlankString:placeholderSrc]) {
+ if ([WXUtility isBlankString:choosedPlaceholder]) {
return;
}
- WXLogDebug(@"Updating image, component:%@, placeholder:%@ ", self.ref, placeholderSrc);
- NSString *newURL = [self.placeholdSrc copy];
- WX_REWRITE_URL(self.placeholdSrc, WXResourceTypeImage, self.weexInstance)
+ WXLogDebug(@"Updating image, component:%@, placeholder:%@ ", self.ref, choosedPlaceholder);
+ NSString *newURL = [choosedPlaceholder copy];
+ WX_REWRITE_URL(choosedPlaceholder, WXResourceTypeImage, self.weexInstance)
__weak typeof(self) weakSelf = self;
self.placeholderOperation = [[self imageLoader] downloadImageWithURL:newURL imageFrame:self.calculatedFrame
@@ -543,16 +549,21 @@
{
dispatch_async(dispatch_get_main_queue(), ^{
__strong typeof(self) strongSelf = weakSelf;
+ if (strongSelf == nil) {
+ return;
+ }
UIImage *viewImage = ((UIImageView *)strongSelf.view).image;
if (error) {
- downloadFailedBlock(placeholderSrc,error);
+ downloadFailedBlock(choosedPlaceholder, error);
if ([strongSelf isViewLoaded] && !viewImage) {
((UIImageView *)(strongSelf.view)).image = nil;
[strongSelf readyToRender];
}
return;
}
- if (![placeholderSrc isEqualToString:strongSelf.placeholdSrc]) {
+
+ NSString* currentPlaceholder = [strongSelf.weexInstance isDarkTheme] ? (strongSelf.darkThemePlaceholderSrc ?: strongSelf.placeholdSrc) : strongSelf.placeholdSrc;
+ if (![choosedPlaceholder isEqualToString:currentPlaceholder]) {
return;
}
@@ -570,20 +581,25 @@
- (void)updateContentImageWithFailedBlock:(void(^)(NSString *, NSError *))downloadFailedBlock
{
- NSString *imageSrc = [NSString stringWithFormat:@"%@", self.imageSrc?:@""];
- if ([WXUtility isBlankString:imageSrc]) {
+ BOOL isDarkMode = [self.weexInstance isDarkTheme];
+ NSString* choosedSrc = isDarkMode ? (self.darkThemeSrc ?: self.src) : self.src;
+
+ if ([WXUtility isBlankString:choosedSrc]) {
WXLogError(@"image src is empty");
return;
}
- WXLogDebug(@"Updating image:%@, component:%@", self.imageSrc, self.ref);
+ WXLogDebug(@"Updating image:%@, component:%@", choosedSrc, self.ref);
NSDictionary *userInfo = @{@"imageQuality":@(self.imageQuality), @"imageSharp":@(self.imageSharp), @"blurRadius":@(self.blurRadius), @"instanceId":[self _safeInstanceId], @"pageURL": self.weexInstance.scriptURL ?: @""};
- NSString * newURL = [imageSrc copy];
- WX_REWRITE_URL(imageSrc, WXResourceTypeImage, self.weexInstance)
+ NSString * newURL = [choosedSrc copy];
+ WX_REWRITE_URL(choosedSrc, WXResourceTypeImage, self.weexInstance)
__weak typeof(self) weakSelf = self;
weakSelf.imageOperation = [[weakSelf imageLoader] downloadImageWithURL:newURL imageFrame:weakSelf.calculatedFrame userInfo:userInfo completed:^(UIImage *image, NSError *error, BOOL finished) {
dispatch_async(dispatch_get_main_queue(), ^{
__strong typeof(self) strongSelf = weakSelf;
+ if (strongSelf == nil) {
+ return;
+ }
if (strongSelf.imageLoadEvent) {
NSMutableDictionary *sizeDict = [NSMutableDictionary new];
@@ -598,12 +614,13 @@
[strongSelf fireEvent:@"load" params:@{ @"success": error? @false : @true,@"size":sizeDict}];
}
if (error) {
- downloadFailedBlock(imageSrc, error);
+ downloadFailedBlock(choosedSrc, error);
[strongSelf readyToRender];
return ;
}
- if (![imageSrc isEqualToString:strongSelf.imageSrc]) {
+ NSString* currentSrc = [strongSelf.weexInstance isDarkTheme] ? (strongSelf.darkThemeSrc ?: strongSelf.src) : strongSelf.src;
+ if (![choosedSrc isEqualToString:currentSrc]) {
return ;
}
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXRichText.mm b/ios/sdk/WeexSDK/Sources/Component/WXRichText.mm
index 8ffeaf3..8e30d76 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXRichText.mm
+++ b/ios/sdk/WeexSDK/Sources/Component/WXRichText.mm
@@ -27,6 +27,7 @@
#import "WXImgLoaderProtocol.h"
#import "WXComponentManager.h"
#import "WXLog.h"
+#import "WXDarkThemeProtocol.h"
#include <pthread/pthread.h>
@interface WXRichNode : NSObject
@@ -35,7 +36,9 @@
@property (nonatomic, strong) NSString *ref;
@property (nonatomic, strong) NSString *text;
@property (nonatomic, strong) UIColor *color;
+@property (nonatomic, strong) UIColor *darkThemeColor;
@property (nonatomic, strong) UIColor *backgroundColor;
+@property (nonatomic, strong) UIColor *darkThemeBackgroundColor;
@property (nonatomic, strong) NSString *fontFamily;
@property (nonatomic, assign) CGFloat fontSize;
@property (nonatomic, assign) CGFloat fontWeight;
@@ -91,7 +94,9 @@
id value = styles[@#key]; \
if (value) { \
node.key = [WXConvert type:value];\
- } else if (!([@#key isEqualToString:@"backgroundColor"] || [@#key isEqualToString:@"textDecoration"]) && superNode.key ) { \
+ } else if (!([@#key isEqualToString:@"backgroundColor"] || \
+ [@#key isEqualToString:@"darkThemeBackgroundColor"] || \
+ [@#key isEqualToString:@"textDecoration"]) && superNode.key ) { \
node.key = superNode.key; \
} \
} while(0);
@@ -190,7 +195,9 @@
- (void)fillCSSStyles:(NSDictionary *)styles toNode:(WXRichNode *)node superNode:(WXRichNode *)superNode
{
WX_STYLE_FILL_RICHTEXT(color, UIColor)
+ WX_STYLE_FILL_RICHTEXT(darkThemeColor, UIColor)
WX_STYLE_FILL_RICHTEXT(backgroundColor, UIColor)
+ WX_STYLE_FILL_RICHTEXT(darkThemeBackgroundColor, UIColor)
WX_STYLE_FILL_RICHTEXT(fontFamily, NSString)
WX_STYLE_FILL_RICHTEXT_PIXEL(fontSize)
WX_STYLE_FILL_RICHTEXT(fontWeight, WXTextWeight)
@@ -421,6 +428,15 @@
};
}
+- (void)themeDidChange:(NSString*)theme
+{
+ [super themeDidChange:theme];
+ if ([self isViewLoaded]) {
+ // Force inner layout
+ [self innerLayout];
+ }
+}
+
#pragma mark Text Building
- (NSMutableAttributedString *)buildAttributeString
@@ -434,6 +450,16 @@
NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] init];
NSUInteger location;
+ BOOL invert = self.invertForDarkTheme;
+
+ // Invert default background color.
+ UIColor* defaultTextColor = [UIColor blackColor];
+ UIColor* defaultBackgroundColor = _backgroundColor;
+ if (invert && [self.weexInstance isDarkTheme]) {
+ defaultTextColor = [[WXSDKInstance darkThemeColorHandler] getInvertedColorFor:[UIColor blackColor] ofScene:[self colorSceneType] withDefault:[UIColor blackColor]];
+ defaultBackgroundColor = [[WXSDKInstance darkThemeColorHandler] getInvertedColorFor:_backgroundColor ofScene:[self colorSceneType] withDefault:_backgroundColor];
+ }
+
__weak typeof(self) weakSelf = self;
for (WXRichNode *node in array) {
location = attrStr.length;
@@ -444,8 +470,10 @@
[attrStr.mutableString appendString:text];
NSRange range = NSMakeRange(location, text.length);
- [attrStr addAttribute:NSForegroundColorAttributeName value:node.color ?: [UIColor blackColor] range:range];
- [attrStr addAttribute:NSBackgroundColorAttributeName value:node.backgroundColor ?: _backgroundColor range:range];
+ UIColor* textColor = [self.weexInstance chooseColor:node.color darkThemeColor:node.darkThemeColor invert:invert scene:[self colorSceneType]];
+ UIColor* bgColor = [self.weexInstance chooseColor:node.backgroundColor darkThemeColor:node.darkThemeBackgroundColor invert:invert scene:[self colorSceneType]];
+ [attrStr addAttribute:NSForegroundColorAttributeName value:textColor ?: defaultTextColor range:range];
+ [attrStr addAttribute:NSBackgroundColorAttributeName value:bgColor ?: defaultBackgroundColor range:range];
UIFont *font = [WXUtility fontWithSize:node.fontSize textWeight:node.fontWeight textStyle:WXTextStyleNormal fontFamily:node.fontFamily scaleFactor:self.weexInstance.pixelScaleFactor];
if (font) {
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.mm
index 3a95fac..3bb7948 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.mm
+++ b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.mm
@@ -120,6 +120,8 @@
@interface WXTextComponent()
@property (atomic, strong) NSString *fontFamily;
+@property (atomic, strong) UIColor *textColor;
+@property (atomic, strong) UIColor *darkThemeTextColor;
@end
@implementation WXTextComponent
@@ -128,7 +130,6 @@
UIEdgeInsets _padding;
NSTextStorage *_textStorage;
float _textStorageWidth;
- float _color[4];
float _fontSize;
float _fontWeight;
WXTextStyle _fontStyle;
@@ -174,8 +175,6 @@
} else {
_useCoreText = YES;
}
-
- _color[0] = -1.0;
[self fillCSSStyles:styles];
[self fillAttributes:attributes];
@@ -261,25 +260,41 @@
WX_STYLE_FILL_TEXT_PIXEL(letterSpacing, letterSpacing, YES) //!OCLint
WX_STYLE_FILL_TEXT(wordWrap, wordWrap, NSString, YES); //!OCLint
- UIColor* color = nil;
- id value = styles[@"color"];
- if (value) {
- if([WXUtility isBlankString:value]){
- color = [UIColor blackColor];
- } else {
- color = [WXConvert UIColor:value];
+ do {
+ UIColor* color = nil;
+ id value = styles[@"color"];
+ if (value) {
+ if([WXUtility isBlankString:value]){
+ color = [UIColor blackColor];
+ } else {
+ color = [WXConvert UIColor:value];
+ }
+ if (color) {
+ self.textColor = color;
+ [self setNeedsRepaint];
+ }
}
- if (color) {
- [self setNeedsRepaint];
- CGFloat red, green, blue, alpha;
- [color getRed:&red green:&green blue:&blue alpha:&alpha];
- _color[0] = red;
- _color[1] = green;
- _color[2] = blue;
- _color[3] = alpha;
+ if (self.textColor == nil) {
+ self.textColor = [UIColor blackColor];
}
- }
+ } while (0);
+ do {
+ UIColor* color = nil;
+ id value = styles[@"darkThemeColor"];
+ if (value) {
+ if([WXUtility isBlankString:value]){
+ color = [UIColor blackColor];
+ } else {
+ color = [WXConvert UIColor:value];
+ }
+ if (color) {
+ self.darkThemeTextColor = color;
+ [self setNeedsRepaint];
+ }
+ }
+ } while (0);
+
if (self.fontFamily && !_observerIconfont) {
// notification received when custom icon font file download finish
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(repaintText:) name:WX_ICONFONT_DOWNLOAD_NOTIFICATION object:nil];
@@ -371,6 +386,20 @@
[self setNeedsRepaint];
}
+- (void)themeDidChange:(NSString*)theme
+{
+ [self setNeedsRepaint];
+ [super themeDidChange:theme];
+ if (_view) {
+ [self setNeedsDisplay];
+ }
+}
+
+- (WXColorScene)colorSceneType
+{
+ return WXColorSceneText;
+}
+
- (BOOL)needsDrawRect
{
return YES;
@@ -497,8 +526,9 @@
string = @"";
}
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString: string];
- if (_color[0] >= 0) {
- [attributedString addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithRed:_color[0] green:_color[1] blue:_color[2] alpha:_color[3]] range:NSMakeRange(0, string.length)];
+ UIColor* textColor = [self.weexInstance chooseColor:self.textColor darkThemeColor:self.darkThemeTextColor invert:self.invertForDarkTheme scene:[self colorSceneType]];
+ if (textColor) {
+ [attributedString addAttribute:NSForegroundColorAttributeName value:textColor range:NSMakeRange(0, string.length)];
}
// set font
@@ -585,8 +615,9 @@
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string];
// set textColor
- if (_color[0] >= 0) {
- [attributedString addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithRed:_color[0] green:_color[1] blue:_color[2] alpha:_color[3]] range:NSMakeRange(0, string.length)];
+ UIColor* textColor = [self.weexInstance chooseColor:self.textColor darkThemeColor:self.darkThemeTextColor invert:self.invertForDarkTheme scene:[self colorSceneType]];
+ if (textColor) {
+ [attributedString addAttribute:NSForegroundColorAttributeName value:textColor range:NSMakeRange(0, string.length)];
}
// set font
@@ -1112,10 +1143,11 @@
{
[super _resetCSSNodeStyles:styles];
if ([styles containsObject:@"color"]) {
- _color[0] = 0;
- _color[1] = 0;
- _color[2] = 0;
- _color[3] = 1.0;
+ self.textColor = [UIColor blackColor];
+ [self setNeedsRepaint];
+ }
+ if ([styles containsObject:@"darkThemeColor"]) {
+ self.darkThemeTextColor = nil;
[self setNeedsRepaint];
}
if ([styles containsObject:@"fontSize"]) {
diff --git a/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m b/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m
index d209068..7b513e8 100644
--- a/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m
+++ b/ios/sdk/WeexSDK/Sources/Display/WXComponent+Display.m
@@ -28,6 +28,8 @@
#import "UIBezierPath+Weex.h"
#import "WXRoundedRect.h"
#import "WXSDKInstance.h"
+#import "WXDarkThemeProtocol.h"
+#import "WXHandlerFactory.h"
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
@@ -101,6 +103,29 @@
WXAssertMainThread();
}
+- (void)themeDidChange:(NSString*)theme
+{
+ WXAssertMainThread();
+ if (_view) {
+ if (![self _needsDrawBorder]) {
+ _layer.borderColor = [self.weexInstance chooseColor:_borderTopColor darkThemeColor:_darkThemeBorderTopColor invert:self.invertForDarkTheme scene:[self colorSceneType]].CGColor;
+ _layer.backgroundColor = [self.weexInstance chooseColor:self.styleBackgroundColor darkThemeColor:self.darkThemeBackgroundColor invert:self.invertForDarkTheme scene:[self colorSceneType]].CGColor;
+ }
+ else {
+ [self setNeedsDisplay];
+ }
+
+ if (_backgroundImage) {
+ [self setGradientLayer];
+ }
+ }
+}
+
+- (WXColorScene)colorSceneType
+{
+ return WXColorSceneUnknown;
+}
+
#pragma mark Private
- (WXDisplayBlock)_displayBlock
@@ -289,7 +314,7 @@
- (void)_collectCompositingDisplayBlocks:(NSMutableArray *)displayBlocks context:(CGContextRef)context isCancelled:(BOOL(^)(void))isCancelled
{
// TODO: compositingChild has no chance to applyPropertiesToView, need update here?
- UIColor *backgroundColor = self.styleBackgroundColor;
+ UIColor *backgroundColor = [self.weexInstance chooseColor:self.styleBackgroundColor darkThemeColor:self.darkThemeBackgroundColor invert:self.invertForDarkTheme scene:[self colorSceneType]];
BOOL clipsToBounds = _clipToBounds;
CGRect frame = self.calculatedFrame;
CGRect bounds = CGRectMake(0, 0, frame.size.width, frame.size.height);
@@ -349,8 +374,9 @@
CGContextSetAlpha(context, _opacity);
// fill background color
- if (self.styleBackgroundColor && CGColorGetAlpha(self.styleBackgroundColor.CGColor) > 0) {
- CGContextSetFillColorWithColor(context, self.styleBackgroundColor.CGColor);
+ UIColor* bgColor = [self.weexInstance chooseColor:self.styleBackgroundColor darkThemeColor:self.darkThemeBackgroundColor invert:self.invertForDarkTheme scene:[self colorSceneType]];
+ if (bgColor && CGColorGetAlpha(bgColor.CGColor) > 0) {
+ CGContextSetFillColorWithColor(context, bgColor.CGColor);
UIBezierPath *bezierPath = [UIBezierPath wx_bezierPathWithRoundedRect:rect topLeft:topLeft topRight:topRight bottomLeft:bottomLeft bottomRight:bottomRight];
[bezierPath fill];
WXPerformBlockOnMainThread(^{
@@ -367,7 +393,8 @@
CGContextSetLineDash(context, 0, 0, 0);
}
CGContextSetLineWidth(context, _borderTopWidth);
- CGContextSetStrokeColorWithColor(context, _borderTopColor.CGColor);
+ CGContextSetStrokeColorWithColor(context,
+ [self.weexInstance chooseColor:_borderTopColor darkThemeColor:_darkThemeBorderTopColor invert:self.invertForDarkTheme scene:[self colorSceneType]].CGColor);
CGContextAddArc(context, size.width-topRight, topRight, topRight-_borderTopWidth/2, -M_PI_4+(_borderRightWidth>0?0:M_PI_4), -M_PI_2, 1);
CGContextMoveToPoint(context, size.width-topRight, _borderTopWidth/2);
CGContextAddLineToPoint(context, topLeft, _borderTopWidth/2);
@@ -388,7 +415,8 @@
CGContextSetLineDash(context, 0, 0, 0);
}
CGContextSetLineWidth(context, _borderLeftWidth);
- CGContextSetStrokeColorWithColor(context, _borderLeftColor.CGColor);
+ CGContextSetStrokeColorWithColor(context,
+ [self.weexInstance chooseColor:_borderLeftColor darkThemeColor:_darkThemeBorderLeftColor invert:self.invertForDarkTheme scene:[self colorSceneType]].CGColor);
CGContextAddArc(context, topLeft, topLeft, topLeft-_borderLeftWidth/2, -M_PI, -M_PI_2-M_PI_4+(_borderTopWidth > 0?0:M_PI_4), 0);
CGContextMoveToPoint(context, _borderLeftWidth/2, topLeft);
CGContextAddLineToPoint(context, _borderLeftWidth/2, size.height-bottomLeft);
@@ -409,7 +437,8 @@
CGContextSetLineDash(context, 0, 0, 0);
}
CGContextSetLineWidth(context, _borderBottomWidth);
- CGContextSetStrokeColorWithColor(context, _borderBottomColor.CGColor);
+ CGContextSetStrokeColorWithColor(context,
+ [self.weexInstance chooseColor:_borderBottomColor darkThemeColor:_darkThemeBorderBottomColor invert:self.invertForDarkTheme scene:[self colorSceneType]].CGColor);
CGContextAddArc(context, bottomLeft, size.height-bottomLeft, bottomLeft-_borderBottomWidth/2, M_PI-M_PI_4+(_borderLeftWidth>0?0:M_PI_4), M_PI_2, 1);
CGContextMoveToPoint(context, bottomLeft, size.height-_borderBottomWidth/2);
CGContextAddLineToPoint(context, size.width-bottomRight, size.height-_borderBottomWidth/2);
@@ -430,7 +459,8 @@
CGContextSetLineDash(context, 0, 0, 0);
}
CGContextSetLineWidth(context, _borderRightWidth);
- CGContextSetStrokeColorWithColor(context, _borderRightColor.CGColor);
+ CGContextSetStrokeColorWithColor(context,
+ [self.weexInstance chooseColor:_borderRightColor darkThemeColor:_darkThemeBorderRightColor invert:self.invertForDarkTheme scene:[self colorSceneType]].CGColor);
CGContextAddArc(context, size.width-bottomRight, size.height-bottomRight, bottomRight-_borderRightWidth/2, M_PI_4+(_borderBottomWidth>0?0:M_PI_4), 0, 1);
CGContextMoveToPoint(context, size.width-_borderRightWidth/2, size.height-bottomRight);
CGContextAddLineToPoint(context, size.width-_borderRightWidth/2, topRight);
@@ -486,7 +516,17 @@
if (!radiusEqual) {
return YES;
}
- BOOL colorEqual = [_borderTopColor isEqual:_borderRightColor] && [_borderRightColor isEqual:_borderBottomColor] && [_borderBottomColor isEqual:_borderLeftColor];
+
+ BOOL invert = self.invertForDarkTheme;
+ WXColorScene scene = [self colorSceneType];
+ UIColor* usingBorderTopColor = [self.weexInstance chooseColor:_borderTopColor darkThemeColor:_darkThemeBorderTopColor invert:invert scene:scene];
+ UIColor* usingBorderRightColor = [self.weexInstance chooseColor:_borderRightColor darkThemeColor:_darkThemeBorderRightColor invert:invert scene:scene];
+ UIColor* usingBorderBottomColor = [self.weexInstance chooseColor:_borderBottomColor darkThemeColor:_darkThemeBorderBottomColor invert:invert scene:scene];
+ UIColor* usingBorderLeftColor = [self.weexInstance chooseColor:_borderLeftColor darkThemeColor:_darkThemeBorderLeftColor invert:invert scene:scene];
+
+ BOOL colorEqual = [usingBorderTopColor isEqual:usingBorderRightColor] &&
+ [usingBorderRightColor isEqual:usingBorderBottomColor] &&
+ [usingBorderBottomColor isEqual:usingBorderLeftColor];
if (!colorEqual) {
return YES;
}
@@ -579,6 +619,32 @@
WX_CHECK_BORDER_PROP(Style, Top, Left, Bottom, Right, WXBorderStyle)
WX_CHECK_BORDER_PROP(Color, Top, Left, Bottom, Right, UIColor)
+ do {
+ BOOL needsDisplay = NO;
+ if (styles[@"darkThemeBorderColor"]) {
+ _darkThemeBorderTopColor = _darkThemeBorderLeftColor = _darkThemeBorderRightColor = _darkThemeBorderBottomColor = [WXConvert UIColor:styles[@"darkThemeBorderColor"]];
+ needsDisplay = YES;
+ }
+ if (styles[@"darkThemeBorderTopColor"]) {
+ _darkThemeBorderTopColor = [WXConvert UIColor:styles[@"darkThemeBorderTopColor"]];
+ needsDisplay = YES;
+ }
+ if (styles[@"darkThemeBorderLeftColor"]) {
+ _darkThemeBorderLeftColor = [WXConvert UIColor:styles[@"darkThemeBorderLeftColor"]];
+ needsDisplay = YES;
+ }
+ if (styles[@"darkThemeBorderRightColor"]) {
+ _darkThemeBorderRightColor = [WXConvert UIColor:styles[@"darkThemeBorderRightColor"]];
+ needsDisplay = YES;
+ }
+ if (styles[@"darkThemeBorderBottomColor"]) {
+ _darkThemeBorderBottomColor = [WXConvert UIColor:styles[@"darkThemeBorderBottomColor"]];
+ needsDisplay = YES;
+ }
+ if (needsDisplay && updating) {
+ [self setNeedsDisplay];
+ }
+ } while (0);
WX_CHECK_BORDER_PROP_PIXEL(Width, Top, Left, Bottom, Right)
WX_CHECK_BORDER_PROP_PIXEL(Radius, TopLeft, TopRight, BottomLeft, BottomRight)
@@ -592,9 +658,9 @@
} else if (!nowNeedsDrawBorder) {
[self _resetNativeBorderRadius];
_layer.borderWidth = _borderTopWidth;
- _layer.borderColor = _borderTopColor.CGColor;
+ _layer.borderColor = [self.weexInstance chooseColor:_borderTopColor darkThemeColor:_darkThemeBorderTopColor invert:self.invertForDarkTheme scene:[self colorSceneType]].CGColor;
if ((_transition.transitionOptions & WXTransitionOptionsBackgroundColor) != WXTransitionOptionsBackgroundColor ) {
- _layer.backgroundColor = self.styleBackgroundColor.CGColor;
+ _layer.backgroundColor = [self.weexInstance chooseColor:self.styleBackgroundColor darkThemeColor:self.darkThemeBackgroundColor invert:self.invertForDarkTheme scene:[self colorSceneType]].CGColor;
}
}
}
@@ -606,7 +672,8 @@
WXRoundedRect *borderRect = [[WXRoundedRect alloc] initWithRect:rect topLeft:_borderTopLeftRadius topRight:_borderTopRightRadius bottomLeft:_borderBottomLeftRadius bottomRight:_borderBottomRightRadius];
WXRadii *radii = borderRect.radii;
BOOL hasBorderRadius = [radii hasBorderRadius];
- return (!hasBorderRadius) && _opacity == 1.0 && CGColorGetAlpha(self.styleBackgroundColor.CGColor) == 1.0 && [self _needsDrawBorder];
+ UIColor* currentBgColor = [self.weexInstance chooseColor:self.styleBackgroundColor darkThemeColor:self.darkThemeBackgroundColor invert:self.invertForDarkTheme scene:[self colorSceneType]];
+ return (!hasBorderRadius) && _opacity == 1.0 && CGColorGetAlpha(currentBgColor.CGColor) == 1.0 && [self _needsDrawBorder];
}
- (CAShapeLayer *)drawBorderRadiusMaskLayer:(CGRect)rect
diff --git a/ios/sdk/WeexSDK/Sources/Engine/WXSDKEngine.m b/ios/sdk/WeexSDK/Sources/Engine/WXSDKEngine.m
index ad181ac..f630f02 100644
--- a/ios/sdk/WeexSDK/Sources/Engine/WXSDKEngine.m
+++ b/ios/sdk/WeexSDK/Sources/Engine/WXSDKEngine.m
@@ -29,6 +29,7 @@
#import "WXNavigationDefaultImpl.h"
#import "WXURLRewriteDefaultImpl.h"
#import "WXJSFrameworkLoadDefaultImpl.h"
+#import "WXDarkThemeDefaultImpl.h"
#import "WXSDKManager.h"
#import "WXSDKError.h"
@@ -205,6 +206,7 @@
[self registerHandler:[WXNavigationDefaultImpl new] withProtocol:@protocol(WXNavigationProtocol)];
[self registerHandler:[WXURLRewriteDefaultImpl new] withProtocol:@protocol(WXURLRewriteProtocol)];
[self registerHandler:[WXJSFrameworkLoadDefaultImpl new] withProtocol:@protocol(WXJSFrameworkLoadProtocol)];
+ [self registerHandler:[WXDarkThemeDefaultImpl new] withProtocol:@protocol(WXDarkThemeProtocol)];
}
+ (void)registerHandler:(id)handler withProtocol:(Protocol *)protocol
diff --git a/ios/sdk/WeexSDK/Sources/Protocol/WXDestroyProtocol.h b/ios/sdk/WeexSDK/Sources/Handler/WXDarkThemeDefaultImpl.h
similarity index 71%
rename from ios/sdk/WeexSDK/Sources/Protocol/WXDestroyProtocol.h
rename to ios/sdk/WeexSDK/Sources/Handler/WXDarkThemeDefaultImpl.h
index 1875e79..cba2971 100644
--- a/ios/sdk/WeexSDK/Sources/Protocol/WXDestroyProtocol.h
+++ b/ios/sdk/WeexSDK/Sources/Handler/WXDarkThemeDefaultImpl.h
@@ -18,12 +18,15 @@
*/
#import <Foundation/Foundation.h>
+#import <WeexSDK/WXDarkThemeProtocol.h>
-@protocol WXDestroyProtocol <NSObject>
+NS_ASSUME_NONNULL_BEGIN
-/**
- * @abstract execute unload function before dealloc
+/* By default, this implementation class do basic invert of UIColor of RGBA color space.
+ You should implementation your own handler to better handle dark theme in your application.
*/
-- (void)unload;
+@interface WXDarkThemeDefaultImpl : NSObject <WXDarkThemeProtocol>
@end
+
+NS_ASSUME_NONNULL_END
diff --git a/ios/sdk/WeexSDK/Sources/Protocol/WXDestroyProtocol.h b/ios/sdk/WeexSDK/Sources/Handler/WXDarkThemeDefaultImpl.m
similarity index 60%
copy from ios/sdk/WeexSDK/Sources/Protocol/WXDestroyProtocol.h
copy to ios/sdk/WeexSDK/Sources/Handler/WXDarkThemeDefaultImpl.m
index 1875e79..0717392 100644
--- a/ios/sdk/WeexSDK/Sources/Protocol/WXDestroyProtocol.h
+++ b/ios/sdk/WeexSDK/Sources/Handler/WXDarkThemeDefaultImpl.m
@@ -17,13 +17,22 @@
* under the License.
*/
-#import <Foundation/Foundation.h>
+#import "WXDarkThemeDefaultImpl.h"
-@protocol WXDestroyProtocol <NSObject>
+@implementation WXDarkThemeDefaultImpl
-/**
- * @abstract execute unload function before dealloc
- */
-- (void)unload;
+- (void)configureView:(UIView *)view ofComponent:(WXComponent *)component
+{
+ // Nothing
+}
+
+- (UIColor *_Nullable)getInvertedColorFor:(UIColor *_Nonnull)color ofScene:(WXColorScene)scene withDefault:(UIColor *_Nullable)defaultColor
+{
+ CGFloat red, blue, green, alpha;
+ if ([color getRed:&red green:&green blue:&blue alpha:&alpha]) {
+ return [UIColor colorWithRed:1 - red green:1 - green blue:1 - blue alpha:alpha];
+ }
+ return color;
+}
@end
diff --git a/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.mm b/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.mm
index f4b29a8..b8f205c 100644
--- a/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.mm
+++ b/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.mm
@@ -260,6 +260,7 @@
WXAssert(_rootComponent == nil, @"Create body is invoked twice.");
_rootComponent = [self _buildComponent:ref type:type supercomponent:nil styles:styles attributes:attributes events:events renderObject:renderObject];
+ _rootComponent.invertForDarkTheme = YES;
CGSize size = _weexInstance.frame.size;
[WXCoreBridge setDefaultDimensionIntoRoot:_weexInstance.instanceId
@@ -327,6 +328,11 @@
}
}
+ // Not explicitly declare "invertForDarkTheme", inherit
+ if (attributes[@"invertForDarkTheme"] == nil) {
+ component.invertForDarkTheme = supercomponent.invertForDarkTheme;
+ }
+
#ifdef DEBUG
WXLogDebug(@"flexLayout -> _recursivelyAddComponent : super:(%@,%@):[%f,%f] ,child:(%@,%@):[%f,%f],childClass:%@",
supercomponent.type,
diff --git a/ios/sdk/WeexSDK/Sources/Model/WXComponent.h b/ios/sdk/WeexSDK/Sources/Model/WXComponent.h
index 0528b1d..4c31a95 100644
--- a/ios/sdk/WeexSDK/Sources/Model/WXComponent.h
+++ b/ios/sdk/WeexSDK/Sources/Model/WXComponent.h
@@ -32,6 +32,12 @@
WXComponentUpdateStylesCallback
} WXComponentCallbackType;
+typedef enum : NSUInteger {
+ WXColorSceneBackground,
+ WXColorSceneText,
+ WXColorSceneUnknown,
+} WXColorScene;
+
/**
* @abstract the component callback , result can be string or dictionary.
* @discussion callback data to js, the id of callback function will be removed to save memory.
@@ -364,6 +370,8 @@
/// @name Display
///--------------------------------------
+@property (nonatomic, assign) BOOL invertForDarkTheme;
+
@property (nonatomic, assign) WXDisplayType displayType;
/**
@@ -379,6 +387,16 @@
- (BOOL)needsDrawRect;
/**
+ * @abstract Fired on instance theme did changed.
+ */
+- (void)themeDidChange:(NSString*)theme;
+
+/**
+ * @abstract Hint used for better do color invert in dark mode.
+ */
+- (WXColorScene)colorSceneType;
+
+/**
* @abstract Draws the component’s image within the passed-in rectangle.
* @parameter rect The rectangle which is the entire visible bounds of your component.
* @return A UIImage containing the contents of the current bitmap graphics context.
diff --git a/ios/sdk/WeexSDK/Sources/Model/WXComponent.mm b/ios/sdk/WeexSDK/Sources/Model/WXComponent.mm
index 3235dbd..16de3c6 100644
--- a/ios/sdk/WeexSDK/Sources/Model/WXComponent.mm
+++ b/ios/sdk/WeexSDK/Sources/Model/WXComponent.mm
@@ -42,6 +42,7 @@
#import "WXSDKInstance_performance.h"
#import "WXComponent_performance.h"
#import "WXCoreBridge.h"
+#import "WXDarkThemeProtocol.h"
#pragma clang diagnostic ignored "-Wincomplete-implementation"
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
@@ -71,6 +72,7 @@
@synthesize transform = _transform;
@synthesize styleBackgroundColor = _styleBackgroundColor;
+@synthesize darkThemeBackgroundColor = _darkThemeBackgroundColor;
#pragma mark Life Cycle
@@ -104,6 +106,7 @@
_eventPenetrationEnabled = NO;
_accessibilityHintContent = nil;
_cancelsTouchesInView = YES;
+ _invertForDarkTheme = NO;
_async = NO;
@@ -122,6 +125,10 @@
}
}
+ if (attributes[@"invertForDarkTheme"]) {
+ _invertForDarkTheme = [WXConvert BOOL:attributes[@"invertForDarkTheme"]];
+ }
+
if (attributes[@"userInteractionEnabled"]) {
_userInteractionEnabled = [WXConvert BOOL:attributes[@"userInteractionEnabled"]];
}
@@ -380,6 +387,10 @@
[self viewWillLoad];
_view = [self loadView];
+
+ // Provide a chance for dark theme handler to process the view
+ [[WXSDKInstance darkThemeColorHandler] configureView:_view ofComponent:self];
+
#ifdef DEBUG
WXLogDebug(@"flexLayout -> loadView:addr-(%p),componentRef-(%@)",_view,self.ref);
#endif
@@ -388,11 +399,19 @@
_view.hidden = _visibility == WXVisibilityShow ? NO : YES;
_view.clipsToBounds = _clipToBounds;
if (![self _needsDrawBorder]) {
- _layer.borderColor = _borderTopColor.CGColor;
+ _layer.borderColor = [self.weexInstance chooseColor:_borderTopColor darkThemeColor:_darkThemeBorderTopColor invert:_invertForDarkTheme scene:[self colorSceneType]].CGColor;
_layer.borderWidth = _borderTopWidth;
[self _resetNativeBorderRadius];
_layer.opacity = _opacity;
- _view.backgroundColor = self.styleBackgroundColor;
+
+ /* Also set background color to view to fix that problem that system may
+ set dynamic color to UITableView. Without these codes, event if we set
+ clear color to layer, the table view could not be transparent. */
+ UIColor* choosedColor = [self.weexInstance chooseColor:self.styleBackgroundColor darkThemeColor:self.darkThemeBackgroundColor invert:_invertForDarkTheme scene:[self colorSceneType]];
+ if (choosedColor == [UIColor clearColor]) {
+ _view.backgroundColor = choosedColor;
+ }
+ _layer.backgroundColor = choosedColor.CGColor;
}
if (_backgroundImage) {
@@ -845,7 +864,11 @@
if (CGRectEqualToRect(self.view.frame, CGRectZero)) {
return;
}
- NSDictionary * linearGradient = [WXUtility linearGradientWithBackgroundImage:_backgroundImage];
+
+ BOOL isDark = [self.weexInstance isDarkTheme];
+ NSString* styleValue = isDark ? (_darkThemeBackgroundImage ?: _backgroundImage) : _backgroundImage;
+
+ NSDictionary * linearGradient = [WXUtility linearGradientWithBackgroundImage:styleValue];
if (!linearGradient) {
return ;
}
@@ -854,6 +877,7 @@
dispatch_async(dispatch_get_main_queue(), ^{
__strong typeof(self) strongSelf = weakSelf;
if(strongSelf) {
+ // No need to auto-invert linear-gradient colors. We only allows using 'dark-theme-background-image' style.
UIColor * startColor = (UIColor*)linearGradient[@"startColor"];
UIColor * endColor = (UIColor*)linearGradient[@"endColor"];
CAGradientLayer * gradientLayer = [WXUtility gradientLayerFromColors:@[startColor, endColor] locations:nil frame:strongSelf.view.bounds gradientType:(WXGradientType)[linearGradient[@"gradientType"] integerValue]];
diff --git a/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.h b/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.h
index c6cf521..e2df9fb 100644
--- a/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.h
+++ b/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.h
@@ -26,6 +26,8 @@
#import <WeexSDK/WXApmForInstance.h>
#import <WeexSDK/WXComponentManager.h>
+@protocol WXDarkThemeProtocol;
+
NS_ASSUME_NONNULL_BEGIN
extern NSString *const bundleUrlOptionKey;
@@ -447,6 +449,32 @@
*/
+ (NSDictionary*)lastPageInfo;
+#pragma mark - Theme Support
+
+/**
+ Handler for handling color invert.
+
+ @return Handler instance.
+ */
++ (id<WXDarkThemeProtocol>)darkThemeColorHandler;
+
+/**
+ Return true if current is dark theme for this instance.
+ */
+- (BOOL)isDarkTheme;
+
+/**
+ Get/set interface style of current instance.
+ */
+- (NSString*)currentThemeName;
+- (void)setCurrentThemeName:(NSString*)name;
+
+/**
+ Choose final color between original color and dark-mode one.
+ Also considering invert.
+ */
+- (UIColor*)chooseColor:(UIColor*)originalColor darkThemeColor:(UIColor*)darkColor invert:(BOOL)invert scene:(WXColorScene)scene;
+
/**
* Deprecated
*/
diff --git a/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m b/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m
index 7a22748..a0af7d7 100644
--- a/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m
+++ b/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m
@@ -51,7 +51,8 @@
#import "WXPageEventNotifyEvent.h"
#import "WXConvertUtility.h"
#import "WXCoreBridge.h"
-#import <WeexSDK/WXDataRenderHandler.h>
+#import "WXDataRenderHandler.h"
+#import "WXDarkThemeProtocol.h"
#define WEEX_LITE_URL_SUFFIX @"wlasm"
#define WEEX_RENDER_TYPE_PLATFORM @"platform"
@@ -102,6 +103,7 @@
{
self = [super init];
if (self) {
+ self.themeName = [WXUtility isSystemInDarkTheme] ? @"dark" : @"light";
_renderType = renderType;
_appearState = YES;
@@ -538,9 +540,6 @@
if (!self.userInfo[@"jsMainBundleStringContentLength"]) {
self.userInfo[@"jsMainBundleStringContentLength"] = @([mainBundleString length]);
}
- if (!self.userInfo[@"jsMainBundleStringContentLength"]) {
- self.userInfo[@"jsMainBundleStringContentMd5"] = [WXUtility md5:mainBundleString];
- }
id<WXPageEventNotifyEventProtocol> pageEvent = [WXSDKEngine handlerForProtocol:@protocol(WXPageEventNotifyEventProtocol)];
if ([pageEvent respondsToSelector:@selector(pageStart:)]) {
@@ -1172,6 +1171,84 @@
return result;
}
++ (id<WXDarkThemeProtocol>)darkThemeColorHandler
+{
+ static id<WXDarkThemeProtocol> colorHandler;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ colorHandler = [WXHandlerFactory handlerForProtocol:@protocol(WXDarkThemeProtocol)];
+ });
+ return colorHandler;
+}
+
+- (NSString*)currentThemeName
+{
+ return self.themeName;
+}
+
+- (BOOL)isDarkTheme
+{
+ return [self.themeName isEqualToString:@"dark"];
+}
+
+- (void)setCurrentThemeName:(NSString*)name
+{
+ if (name && ![name isEqualToString:self.themeName]) {
+ self.themeName = name;
+
+ // Recursively visit all components and notify that theme had changed.
+ __weak WXSDKInstance* weakSelf = self;
+ WXPerformBlockOnComponentThread(^{
+ __strong WXSDKInstance* strongSelf = weakSelf;
+ if (strongSelf == nil) {
+ return;
+ }
+
+ if (!strongSelf->_componentManager.isValid) {
+ return;
+ }
+
+ [strongSelf->_componentManager enumerateComponentsUsingBlock:^(WXComponent * _Nonnull component, BOOL * _Nonnull stop) {
+ __weak WXComponent* wcomp = component;
+ WXPerformBlockOnMainThread(^{
+ __strong WXComponent* scomp = wcomp;
+ if (scomp) {
+ [scomp themeDidChange:name];
+ }
+ });
+ }];
+ });
+
+ [[WXSDKManager bridgeMgr] fireEvent:_instanceId
+ ref:WX_SDK_ROOT_REF
+ type:@"themechanged"
+ params:@{@"theme": self.themeName?:@"light"}
+ domChanges:nil];
+ }
+}
+
+- (UIColor*)chooseColor:(UIColor*)originalColor darkThemeColor:(UIColor*)darkColor invert:(BOOL)invert scene:(WXColorScene)scene
+{
+ if ([self isDarkTheme]) {
+ if (darkColor) {
+ return darkColor;
+ }
+ else if (invert) {
+ // Invert originalColor
+ if (originalColor == [UIColor clearColor]) {
+ return originalColor;
+ }
+ return [[WXSDKInstance darkThemeColorHandler] getInvertedColorFor:originalColor ofScene:scene withDefault:originalColor];
+ }
+ else {
+ return originalColor;
+ }
+ }
+ else {
+ return originalColor;
+ }
+}
+
@end
@implementation WXSDKInstance (Deprecated)
diff --git a/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance_private.h b/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance_private.h
index 25680da..852275d 100644
--- a/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance_private.h
+++ b/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance_private.h
@@ -39,6 +39,8 @@
@property (nonatomic, strong) NSString *createInstanceContextResult;
@property (nonatomic, strong) NSString *executeRaxApiResult;
+@property (atomic, strong) NSString* themeName;
+
- (void)addModuleEventObservers:(NSString*)event callback:(NSString*)callbackId option:(NSDictionary*)option moduleClassName:(NSString*)moduleClassName;
- (void)_addModuleEventObserversWithModuleMethod:(WXModuleMethod*)method;
- (void)removeModuleEventObserver:(NSString*)event moduleClassName:(NSString*)moduleClassName;
diff --git a/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.m b/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.m
index 8ee1591..f75728a 100644
--- a/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.m
+++ b/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.m
@@ -26,6 +26,7 @@
#import "WXLength.h"
#import "WXTransition.h"
#import "WXComponent+Layout.h"
+#import "WXDarkThemeProtocol.h"
@interface WXAnimationInfo : NSObject<NSCopying>
@@ -207,7 +208,30 @@
}
CAMediaTimingFunction *timingFunction = [WXConvert CAMediaTimingFunction:args[@"timingFunction"]];
NSDictionary *styles = args[@"styles"];
+ NSDictionary* componentRawStyles = target.styles;
+
+ BOOL isDarkTheme = [target.weexInstance isDarkTheme];
+ BOOL updatingDarkThemeBackgroundColor = styles[@"darkThemeBackgroundColor"] != nil;
+
for (NSString *property in styles) {
+ if ([property isEqualToString:@"backgroundColor"]) {
+ if (isDarkTheme && (updatingDarkThemeBackgroundColor ||
+ componentRawStyles[@"darkThemeBackgroundColor"] != nil)) {
+ /* Updating "darkThemeBackgroundColor" in dark mode,
+ or this component has dark bg color explicitly defined in styels.
+ We ignore transition animation for "backgroundColor" */
+ continue;
+ }
+ }
+ else if ([property isEqualToString:@"darkThemeBackgroundColor"]) {
+ if (!isDarkTheme || componentRawStyles[@"darkThemeBackgroundColor"] == nil) {
+ /* Do not do animation for "darkThemeBackgroundColor" in light mode.
+ Or there is no dark bg color explicitly defined in styles.
+ */
+ continue;
+ }
+ }
+
WXAnimationInfo *info = [WXAnimationInfo new];
info.duration = duration;
info.delay = delay;
@@ -287,10 +311,17 @@
[infos addObject:newInfo];
}
target.transform = wxTransform;
- } else if ([property isEqualToString:@"backgroundColor"]) {
+ } else if ([property isEqualToString:@"backgroundColor"] ||
+ [property isEqualToString:@"darkThemeBackgroundColor"]) {
info.propertyName = @"backgroundColor";
info.fromValue = (__bridge id)(layer.backgroundColor);
- info.toValue = (__bridge id)[WXConvert CGColor:value];
+ UIColor* toColor = [WXConvert UIColor:value];
+ if ([target.weexInstance isDarkTheme] && target.invertForDarkTheme &&
+ [property isEqualToString:@"backgroundColor"]) {
+ // Invert color
+ toColor = [[WXSDKInstance darkThemeColorHandler] getInvertedColorFor:toColor ofScene:[target colorSceneType] withDefault:toColor];
+ }
+ info.toValue = (__bridge id)([toColor CGColor]);
[infos addObject:info];
} else if ([property isEqualToString:@"opacity"]) {
info.propertyName = @"opacity";
diff --git a/ios/sdk/WeexSDK/Sources/Module/WXTransition.mm b/ios/sdk/WeexSDK/Sources/Module/WXTransition.mm
index 8bb616d..b597232 100644
--- a/ios/sdk/WeexSDK/Sources/Module/WXTransition.mm
+++ b/ios/sdk/WeexSDK/Sources/Module/WXTransition.mm
@@ -30,6 +30,7 @@
#import "WXAssert.h"
#import "WXSDKInstance_private.h"
#import "WXLength.h"
+#import "WXDarkThemeProtocol.h"
@implementation WXTransitionInfo
@end
@@ -82,6 +83,7 @@
@"bottom": @(WXTransitionOptionsBottom),
@"top": @(WXTransitionOptionsTop),
@"backgroundColor": @(WXTransitionOptionsBackgroundColor),
+ @"darkThemeBackgroundColor": @(WXTransitionOptionsBackgroundColor),
@"transform": @(WXTransitionOptionsTransform),
@"opacity": @(WXTransitionOptionsOpacity)
};
@@ -106,24 +108,55 @@
_filterStyles = _filterStyles ?:[NSMutableDictionary new];
_oldFilterStyles = _oldFilterStyles ?: [NSMutableDictionary new];
NSMutableDictionary *futileStyles = [NSMutableDictionary new];
+ NSDictionary* componentRawStyles = targetComponent.styles;
+
+ BOOL isDarkTheme = [targetComponent.weexInstance isDarkTheme];
+ BOOL updatingDarkThemeBackgroundColor = styles[@"darkThemeBackgroundColor"] != nil;
for (NSString *key in styles) {
if (self.transitionOptions & [self transitionOptionsFromString:key]) {
+ if ([key isEqualToString:@"backgroundColor"]) {
+ if (isDarkTheme && (updatingDarkThemeBackgroundColor ||
+ componentRawStyles[@"darkThemeBackgroundColor"] != nil)) {
+ /* Updating "darkThemeBackgroundColor" in dark mode,
+ or this component has dark bg color explicitly defined in styels.
+ We ignore transition animation for "backgroundColor" */
+ [futileStyles setObject:styles[key] forKey:key];
+ continue;
+ }
+ }
+ else if ([key isEqualToString:@"darkThemeBackgroundColor"]) {
+ if (!isDarkTheme || componentRawStyles[@"darkThemeBackgroundColor"] == nil) {
+ /* Do not do animation for "darkThemeBackgroundColor" in light mode.
+ Or there is no dark bg color explicitly defined in styles.
+ */
+ [futileStyles setObject:styles[key] forKey:key];
+ continue;
+ }
+ }
+
[_filterStyles setObject:styles[key] forKey:key];
if (![key isEqualToString:@"transform"]) {
if (!isRunning) {
- /* style value may not be in component.styles, so we must get
- value from layout and convert it to style value. */
- id styleValue = targetComponent.styles[key];
- if (styleValue == nil) {
+ // Get animation 'from' value from raw styles.
+ id styleValue = componentRawStyles[key];
+ if ([key isEqualToString:@"backgroundColor"] ||
+ [key isEqualToString:@"darkThemeBackgroundColor"]) {
+ if (styleValue == nil) {
+ // background color is transparent by default.
+ styleValue = @"transparent";
+ }
+ }
+ else if (styleValue == nil) {
+ /* Flex styles may not be in component.styles, so we must get
+ value from layout and convert it to style value. */
styleValue = [targetComponent convertLayoutValueToStyleValue:key];
}
[_oldFilterStyles setObject:styleValue forKey:key];
}
}
}
- else
- {
+ else {
[futileStyles setObject:styles[key] forKey:key];
}
}
@@ -131,7 +164,7 @@
_targetComponent = targetComponent;
NSMutableDictionary *componentStyles = [NSMutableDictionary dictionaryWithDictionary:styles];
- [componentStyles addEntriesFromDictionary:targetComponent.styles];
+ [componentStyles addEntriesFromDictionary:componentRawStyles];
_transitionDuration = componentStyles[kWXTransitionDuration] ? [WXConvert CGFloat:componentStyles[kWXTransitionDuration]] : 0;
_transitionDelay = componentStyles[kWXTransitionDelay] ? [WXConvert CGFloat:componentStyles[kWXTransitionDelay]] : 0;
@@ -200,10 +233,21 @@
if (!_propertyArray) {
_propertyArray = [NSMutableArray new];
}
- if ([singleProperty isEqualToString:@"backgroundColor"]) {
+ if ([singleProperty isEqualToString:@"backgroundColor"] ||
+ [singleProperty isEqualToString:@"darkThemeBackgroundColor"]) {
+ UIColor* fromColor = [WXConvert UIColor:_oldFilterStyles[singleProperty]];
+ UIColor* toColor = [WXConvert UIColor:_filterStyles[singleProperty]];
+ if ([_targetComponent.weexInstance isDarkTheme] &&
+ _targetComponent.invertForDarkTheme &&
+ [singleProperty isEqualToString:@"backgroundColor"]) {
+ // Invert color
+ fromColor = [[WXSDKInstance darkThemeColorHandler] getInvertedColorFor:fromColor ofScene:[_targetComponent colorSceneType] withDefault:fromColor];
+ toColor = [[WXSDKInstance darkThemeColorHandler] getInvertedColorFor:toColor ofScene:[_targetComponent colorSceneType] withDefault:toColor];
+ }
+
WXTransitionInfo *info = [WXTransitionInfo new];
- info.fromValue = [self _dealWithColor:[WXConvert UIColor:_oldFilterStyles[singleProperty]]];
- info.toValue = [self _dealWithColor:[WXConvert UIColor:_filterStyles[singleProperty]]];
+ info.fromValue = [self _dealWithColor:fromColor];
+ info.toValue = [self _dealWithColor:toColor];
info.perValue = [self _calculatePerColorRGB1:info.toValue RGB2:info.fromValue];
info.propertyName = singleProperty;
[_propertyArray addObject:info];
@@ -337,6 +381,7 @@
@([info.fromValue[3] floatValue] + [info.perValue[3] floatValue] * per)];
UIColor *color = [UIColor colorWithRed:[array[0] floatValue] green:[array[1] floatValue] blue:[array[2] floatValue] alpha:[array[3] floatValue]];
WXPerformBlockOnMainThread(^{
+ // Here we do not need to consider about dark mode.
_targetComponent.view.backgroundColor = color;
[_targetComponent.view setNeedsDisplay];
});
diff --git a/ios/sdk/WeexSDK/Sources/Protocol/WXDarkThemeProtocol.h b/ios/sdk/WeexSDK/Sources/Protocol/WXDarkThemeProtocol.h
new file mode 100644
index 0000000..c443879
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Protocol/WXDarkThemeProtocol.h
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#import <WeexSDK/WXModuleProtocol.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@protocol WXDarkThemeProtocol <WXModuleProtocol>
+
+/**
+After any view of Weex component is created. Callback dark theme handler to provide a
+ chance to configure the view.
+*/
+- (void)configureView:(UIView*_Nonnull)view ofComponent:(WXComponent*_Nonnull)component;
+
+/**
+ Get inverted color in dark mode for input color with scene hint.
+
+ @param color Input color.
+ @param scene Scene indicating the color usage.
+ @param defaultColor If no inverted one matches, return the default color.
+ @return Inverted color.
+ */
+- (UIColor *_Nullable)getInvertedColorFor:(UIColor *_Nonnull)color ofScene:(WXColorScene)scene withDefault:(UIColor *_Nullable)defaultColor;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h b/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h
index 61eeccd..4addab0 100644
--- a/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h
+++ b/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h
@@ -131,6 +131,14 @@
+ (void)performBlock:(void (^_Nonnull)(void))block onThread:(NSThread *_Nonnull)thread;
/**
+ * @abstract Check if system is in dark mode.
+ *
+ * @return Boolean
+ *
+ */
++ (BOOL)isSystemInDarkTheme;
+
+/**
* @abstract Returns the environment of current application, you can get some necessary properties such as appVersion、sdkVersion、appName etc.
*
* @return A dictionary object which contains these properties.
diff --git a/ios/sdk/WeexSDK/Sources/Utility/WXUtility.m b/ios/sdk/WeexSDK/Sources/Utility/WXUtility.m
index 6ec4223..6d7136b 100644
--- a/ios/sdk/WeexSDK/Sources/Utility/WXUtility.m
+++ b/ios/sdk/WeexSDK/Sources/Utility/WXUtility.m
@@ -163,6 +163,22 @@
return [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:UISemanticContentAttributeUnspecified] == UIUserInterfaceLayoutDirectionRightToLeft ? WXLayoutDirectionRTL : WXLayoutDirectionLTR;
}
++ (BOOL)isSystemInDarkTheme
+{
+ if (@available(iOS 13.0, *)) {
+ __block BOOL result = NO;
+ WXPerformBlockSyncOnMainThread(^{
+#ifdef __IPHONE_13_0
+ if ([UITraitCollection currentTraitCollection].userInterfaceStyle == UIUserInterfaceStyleDark) {
+ result = YES;
+ }
+#endif
+ });
+ return result;
+ }
+ return NO;
+}
+
+ (NSDictionary *)getEnvironment
{
NSString *platform = @"iOS";
@@ -187,7 +203,8 @@
@"deviceWidth":@(deviceWidth * scale),
@"deviceHeight":@(deviceHeight * scale),
@"scale":@(scale),
- @"layoutDirection": [self getEnvLayoutDirection] == WXLayoutDirectionRTL ? @"rtl" : @"ltr"
+ @"layoutDirection": [self getEnvLayoutDirection] == WXLayoutDirectionRTL ? @"rtl" : @"ltr",
+ @"theme": [self isSystemInDarkTheme] ? @"dark" : @"light"
}];
if ([[[UIDevice currentDevice] systemVersion] integerValue] >= 11) {
diff --git a/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.mm b/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.mm
index ce252d0..accb21a 100644
--- a/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.mm
+++ b/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.mm
@@ -67,6 +67,14 @@
}\
} while(0);
+#define WX_BOARD_RADIUS_DARK_THEME_COLOR_RESET_ALL(key)\
+do {\
+ if (styles && [styles containsObject:@#key]) {\
+ _darkThemeBorderTopColor = _darkThemeBorderLeftColor = _darkThemeBorderRightColor = _darkThemeBorderBottomColor = [UIColor blackColor];\
+ [self setNeedsDisplay];\
+ }\
+} while(0);
+
#define WX_BOARD_COLOR_RESET(key)\
do {\
if (styles && [styles containsObject:@#key]) {\
@@ -175,7 +183,11 @@
- (void)_initViewPropertyWithStyles:(NSDictionary *)styles
{
self.styleBackgroundColor = styles[@"backgroundColor"] ? [WXConvert UIColor:styles[@"backgroundColor"]] : [UIColor clearColor];
+ if (styles[@"darkThemeBackgroundColor"]) {
+ self.darkThemeBackgroundColor = [WXConvert UIColor:styles[@"darkThemeBackgroundColor"]];
+ }
_backgroundImage = styles[@"backgroundImage"] ? [WXConvert NSString:styles[@"backgroundImage"]]: nil;
+ _darkThemeBackgroundImage = styles[@"darkThemeBackgroundImage"] ? [WXConvert NSString:styles[@"darkThemeBackgroundImage"]] : nil;
_opacity = styles[@"opacity"] ? [WXConvert CGFloat:styles[@"opacity"]] : 1.0;
_clipToBounds = styles[@"overflow"] ? [WXConvert WXClipType:styles[@"overflow"]] : NO;
_visibility = styles[@"visibility"] ? [WXConvert WXVisibility:styles[@"visibility"]] : WXVisibilityShow;
@@ -195,6 +207,9 @@
if (styles[@"backgroundColor"]) {
self.styleBackgroundColor = [WXConvert UIColor:styles[@"backgroundColor"]];
}
+ if (styles[@"darkThemeBackgroundColor"]) {
+ self.darkThemeBackgroundColor = [WXConvert UIColor:styles[@"darkThemeBackgroundColor"]];
+ }
if (styles[@"opacity"]) {
_opacity = [WXConvert CGFloat:styles[@"opacity"]];
}
@@ -215,9 +230,21 @@
[self setNeedsDisplay];
}
+ if (styles[@"darkThemeBackgroundColor"]) {
+ self.darkThemeBackgroundColor = [WXConvert UIColor:styles[@"darkThemeBackgroundColor"]];
+ [self setNeedsDisplay];
+ }
+
if (styles[@"backgroundImage"]) {
- _backgroundImage = styles[@"backgroundImage"] ? [WXConvert NSString:styles[@"backgroundImage"]]: nil;
- if (_backgroundImage) {
+ _backgroundImage = [WXConvert NSString:styles[@"backgroundImage"]];
+ }
+
+ if (styles[@"darkThemeBackgroundImage"]) {
+ _darkThemeBackgroundImage = [WXConvert NSString:styles[@"darkThemeBackgroundImage"]];
+ }
+
+ if (styles[@"backgroundImage"] || styles[@"darkThemeBackgroundImage"]) {
+ if (_backgroundImage || _darkThemeBackgroundImage) {
[self setGradientLayer];
}
}
@@ -302,6 +329,11 @@
WX_BOARD_COLOR_RESET(borderLeftColor);
WX_BOARD_COLOR_RESET(borderRightColor);
WX_BOARD_COLOR_RESET(borderBottomColor);
+ WX_BOARD_RADIUS_DARK_THEME_COLOR_RESET_ALL(darkThemeBorderColor);
+ WX_BOARD_COLOR_RESET(darkThemeBorderTopColor);
+ WX_BOARD_COLOR_RESET(darkThemeBorderLeftColor);
+ WX_BOARD_COLOR_RESET(darkThemeBorderRightColor);
+ WX_BOARD_COLOR_RESET(darkThemeBorderBottomColor);
}
- (void)_resetStyles:(NSArray *)styles
@@ -310,6 +342,11 @@
self.styleBackgroundColor = [UIColor clearColor];
[self setNeedsDisplay];
}
+ if (styles && [styles containsObject:@"darkThemeBackgroundColor"]) {
+ self.darkThemeBackgroundColor = nil;
+ [self setNeedsDisplay];
+ }
+
if (styles && [styles containsObject:@"boxShadow"]) {
_lastBoxShadow = _boxShadow;
_boxShadow = nil;
@@ -317,6 +354,11 @@
}
if (styles && [styles containsObject:@"backgroundImage"]) {
_backgroundImage = nil;
+ }
+ if (styles && [styles containsObject:@"darkThemeBackgroundImage"]) {
+ _darkThemeBackgroundImage = nil;
+ }
+ if (styles && ([styles containsObject:@"backgroundImage"] || [styles containsObject:@"darkThemeBackgroundImage"])) {
[self setGradientLayer];
}
diff --git a/ios/sdk/WeexSDK/Sources/View/WXRootView.m b/ios/sdk/WeexSDK/Sources/View/WXRootView.m
index 3cc8a36..8b2414b 100644
--- a/ios/sdk/WeexSDK/Sources/View/WXRootView.m
+++ b/ios/sdk/WeexSDK/Sources/View/WXRootView.m
@@ -23,6 +23,10 @@
#import "WXSDKEngine.h"
@interface WXRootView()
+{
+ BOOL _hasFirstTraitCollectionChange;
+ BOOL _allowFirstTraitCollectionChange;
+}
@property (nonatomic, assign) BOOL mHasEvent;
@@ -59,4 +63,36 @@
return _mHasEvent;
}
+- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
+{
+ [super traitCollectionDidChange:previousTraitCollection];
+
+ if (@available(iOS 13.0, *)) {
+ // When entering background system may call back change twice.. We ignore the first one.
+ UIUserInterfaceStyle currentStyle = self.traitCollection.userInterfaceStyle;
+ if (currentStyle != previousTraitCollection.userInterfaceStyle) {
+ if (_hasFirstTraitCollectionChange) {
+ _allowFirstTraitCollectionChange = NO;
+ [self.instance setCurrentThemeName:currentStyle == UIUserInterfaceStyleDark ? @"dark" : @"light"];
+ }
+ else {
+ __weak WXRootView* weakSelf = self;
+ _hasFirstTraitCollectionChange = YES;
+ _allowFirstTraitCollectionChange = YES;
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ __strong WXRootView* strongSelf = weakSelf;
+ if (strongSelf) {
+ if (strongSelf->_allowFirstTraitCollectionChange) {
+ if (strongSelf.instance) {
+ [strongSelf.instance setCurrentThemeName:currentStyle == UIUserInterfaceStyleDark ? @"dark" : @"light"];
+ }
+ }
+ strongSelf->_hasFirstTraitCollectionChange = NO;
+ }
+ });
+ }
+ }
+ }
+}
+
@end
diff --git a/ios/sdk/WeexSDK/Sources/WeexSDK.h b/ios/sdk/WeexSDK/Sources/WeexSDK.h
index 8e5f6f4..05dbe21 100644
--- a/ios/sdk/WeexSDK/Sources/WeexSDK.h
+++ b/ios/sdk/WeexSDK/Sources/WeexSDK.h
@@ -73,6 +73,7 @@
#import <WeexSDK/WXDefine.h>
#import <WeexSDK/WXDebugTool.h>
#import <WeexSDK/WXDataRenderHandler.h>
+#import <WeexSDK/WXDarkThemeProtocol.h>
#import <WeexSDK/WXConvertUtility.h>
#import <WeexSDK/WXConvert.h>
#import <WeexSDK/WXConfigCenterProtocol.h>