This is an automated email from the ASF dual-hosted git repository. erisu pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cordova-ios.git
The following commit(s) were added to refs/heads/master by this push: new 0ab311fa feat(statusbar): Simple built-in status bar support (#1523) 0ab311fa is described below commit 0ab311fafd274047fe98c3e36f95c6c35ce6b6e8 Author: Darryl Pogue <dar...@dpogue.ca> AuthorDate: Sun Apr 6 21:17:20 2025 -0700 feat(statusbar): Simple built-in status bar support (#1523) * fix: Prevent recursion in scrollView monkeypatch If you call scrollView on a UIView that does not have its own scrollView method, this hijack method will keep recursively calling itself until you run out of stack space. Try to guard against that by making sure that it's not getting called on the same instance multiple times. TODO: Think about thread safety, but in theory because this is view-related stuff it's only safe to run on the main thread. * feat(statusbar): Simple built-in status bar background This adds a background behind the status bar automatically when a page does not request to extend itself behind the status bar using a meta tag with `viewport-fit=cover`. It automatically shows and hides as the value of the viewport-fit option changes. On iOS 16.4 and newer, it will be coloured according to the meta `theme-color` tag, and a default colour can be set in the storyboard for a CDVViewController. We expose a very basic JS API hanging off the existing `window.statusbar` property that allows controlling the visibility (of the status bar contents entirely, not just of the background view) and overriding the background colour. The colour is determined as follows: 1. Any colour set explicitly with the JS API (if any) 2. Any colour pulled from the meta tag (iOS 16.4+, if any) 3. The default colour specified in the storyboard (if any) 4. The background colour specified in the storyboard (if any) 5. The default system background colour Efforts were made to ensure that this does not interfere with the existing CDVStatusBar plugin implementation (although maybe this can replace it for most common use cases), by making the display of this built-in conditional on not having the CDVStatusBar plugin installed. * feat(statusbar): Hook up StatusBarBackgroundColor --- ...VLaunchScreen.m => CDVViewController+Private.h} | 14 +-- .../Plugins/CDVLaunchScreen/CDVLaunchScreen.m | 1 + .../CDVStatusBarInternal.h} | 16 +-- .../CDVStatusBarInternal/CDVStatusBarInternal.m | 46 +++++++++ .../Plugins/CDVWebViewEngine/CDVWebViewEngine.m | 14 +++ CordovaLib/Classes/Public/CDVPlugin.m | 10 +- CordovaLib/Classes/Public/CDVViewController.m | 112 ++++++++++++++++++++- CordovaLib/CordovaLib.xcodeproj/project.pbxproj | 26 +++++ CordovaLib/include/Cordova/CDVViewController.h | 7 ++ cordova-js-src/platform.js | 4 + cordova-js-src/plugin/ios/statusbar.js | 78 ++++++++++++++ lib/prepare.js | 41 ++++++++ templates/cordova/defaults.xml | 4 + .../Contents.json | 15 +++ templates/project/App/Base.lproj/Main.storyboard | 6 ++ .../Contents.json | 15 +++ .../ios-config-xml/App/Base.lproj/Main.storyboard | 6 ++ 17 files changed, 387 insertions(+), 28 deletions(-) diff --git a/CordovaLib/Classes/Private/Plugins/CDVLaunchScreen/CDVLaunchScreen.m b/CordovaLib/Classes/Private/CDVViewController+Private.h similarity index 75% copy from CordovaLib/Classes/Private/Plugins/CDVLaunchScreen/CDVLaunchScreen.m copy to CordovaLib/Classes/Private/CDVViewController+Private.h index 8c672277..27a907f9 100644 --- a/CordovaLib/Classes/Private/Plugins/CDVLaunchScreen/CDVLaunchScreen.m +++ b/CordovaLib/Classes/Private/CDVViewController+Private.h @@ -17,18 +17,12 @@ under the License. */ -#import "CDVLaunchScreen.h" +#import <Cordova/CDVViewController.h> -@implementation CDVLaunchScreen +@interface CDVViewController (Private) -- (void)show:(CDVInvokedUrlCommand *)command -{ - [self.viewController showSplashScreen:YES]; -} +- (void)setStatusBarWebViewColor:(UIColor *)color; -- (void)hide:(CDVInvokedUrlCommand *)command -{ - [self.viewController showSplashScreen:NO]; -} +- (void)showStatusBar:(BOOL)visible; @end diff --git a/CordovaLib/Classes/Private/Plugins/CDVLaunchScreen/CDVLaunchScreen.m b/CordovaLib/Classes/Private/Plugins/CDVLaunchScreen/CDVLaunchScreen.m index 8c672277..0609d6f4 100644 --- a/CordovaLib/Classes/Private/Plugins/CDVLaunchScreen/CDVLaunchScreen.m +++ b/CordovaLib/Classes/Private/Plugins/CDVLaunchScreen/CDVLaunchScreen.m @@ -18,6 +18,7 @@ */ #import "CDVLaunchScreen.h" +#import "CDVViewController+Private.h" @implementation CDVLaunchScreen diff --git a/CordovaLib/Classes/Private/Plugins/CDVLaunchScreen/CDVLaunchScreen.m b/CordovaLib/Classes/Private/Plugins/CDVStatusBarInternal/CDVStatusBarInternal.h similarity index 75% copy from CordovaLib/Classes/Private/Plugins/CDVLaunchScreen/CDVLaunchScreen.m copy to CordovaLib/Classes/Private/Plugins/CDVStatusBarInternal/CDVStatusBarInternal.h index 8c672277..08af9c89 100644 --- a/CordovaLib/Classes/Private/Plugins/CDVLaunchScreen/CDVLaunchScreen.m +++ b/CordovaLib/Classes/Private/Plugins/CDVStatusBarInternal/CDVStatusBarInternal.h @@ -17,18 +17,12 @@ under the License. */ -#import "CDVLaunchScreen.h" +#import <Cordova/CDVPlugin.h> -@implementation CDVLaunchScreen +@interface CDVStatusBarInternal : CDVPlugin -- (void)show:(CDVInvokedUrlCommand *)command -{ - [self.viewController showSplashScreen:YES]; -} - -- (void)hide:(CDVInvokedUrlCommand *)command -{ - [self.viewController showSplashScreen:NO]; -} +- (void)setVisible:(CDVInvokedUrlCommand*)command; +- (void)setBackgroundColor:(CDVInvokedUrlCommand*)command; @end + diff --git a/CordovaLib/Classes/Private/Plugins/CDVStatusBarInternal/CDVStatusBarInternal.m b/CordovaLib/Classes/Private/Plugins/CDVStatusBarInternal/CDVStatusBarInternal.m new file mode 100644 index 00000000..fc6412bd --- /dev/null +++ b/CordovaLib/Classes/Private/Plugins/CDVStatusBarInternal/CDVStatusBarInternal.m @@ -0,0 +1,46 @@ +/* + 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 "CDVStatusBarInternal.h" +#import "CDVViewController+Private.h" + +@implementation CDVStatusBarInternal + +- (void)setVisible:(CDVInvokedUrlCommand *)command +{ + id value = [command argumentAtIndex:0]; + if (!([value isKindOfClass:[NSNumber class]])) { + value = [NSNumber numberWithBool:YES]; + } + + [self.viewController showStatusBar:[value boolValue]]; +} + +- (void)setBackgroundColor:(CDVInvokedUrlCommand *)command +{ + NSInteger valueR = [[command argumentAtIndex:0 withDefault:@0] integerValue]; + NSInteger valueG = [[command argumentAtIndex:1 withDefault:@0] integerValue]; + NSInteger valueB = [[command argumentAtIndex:2 withDefault:@0] integerValue]; + + UIColor *bgColor = [UIColor colorWithRed:valueR/255.f green:valueG/255.f blue:valueB/255.f alpha:1.f]; + [self.viewController setStatusBarBackgroundColor:bgColor]; +} + +@end + diff --git a/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m b/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m index d2314c82..7100918b 100644 --- a/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m +++ b/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m @@ -22,6 +22,7 @@ #import "CDVURLSchemeHandler.h" #import <Cordova/CDVWebViewProcessPoolFactory.h> #import <Cordova/CDVSettingsDictionary.h> +#import "CDVViewController+Private.h" #import <objc/message.h> @@ -242,6 +243,8 @@ wkWebView.customUserAgent = [settings cordovaSettingForKey:@"OverrideUserAgent"]; } + [wkWebView addObserver:self forKeyPath:@"themeColor" options:NSKeyValueObservingOptionInitial context:nil]; + self.engineWebView = wkWebView; if ([self.viewController conformsToProtocol:@protocol(WKUIDelegate)]) { @@ -477,6 +480,17 @@ return result; } +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if ([keyPath isEqualToString:@"themeColor"]) { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000 + if (@available(iOS 15.0, *)) { + [self.viewController setStatusBarWebViewColor:((WKWebView *)self.engineWebView).themeColor]; + } +#endif + } +} + #pragma mark - WKScriptMessageHandler implementation - (void)userContentController:(WKUserContentController*)userContentController didReceiveScriptMessage:(WKScriptMessage*)message diff --git a/CordovaLib/Classes/Public/CDVPlugin.m b/CordovaLib/Classes/Public/CDVPlugin.m index 3eac8209..9b84a458 100644 --- a/CordovaLib/Classes/Public/CDVPlugin.m +++ b/CordovaLib/Classes/Public/CDVPlugin.m @@ -28,9 +28,15 @@ - (UIScrollView*)scrollView { - if ([self respondsToSelector:@selector(scrollView)]) { - return [self performSelector:@selector(scrollView)]; + static UIView *caller = nil; + + if (caller != self && [self respondsToSelector:@selector(scrollView)]) { + caller = self; + UIScrollView *sv = [self performSelector:@selector(scrollView)]; + caller = nil; + return sv; } + caller = nil; return nil; } diff --git a/CordovaLib/Classes/Public/CDVViewController.m b/CordovaLib/Classes/Public/CDVViewController.m index a30671da..5932ea82 100644 --- a/CordovaLib/Classes/Public/CDVViewController.m +++ b/CordovaLib/Classes/Public/CDVViewController.m @@ -30,23 +30,27 @@ #import <Cordova/CDVTimer.h> #import "CDVCommandDelegateImpl.h" -static UIColor* defaultBackgroundColor(void) { +static UIColor *defaultBackgroundColor(void) { return UIColor.systemBackgroundColor; } -@interface CDVViewController () <CDVWebViewEngineConfigurationDelegate> { +@interface CDVViewController () <CDVWebViewEngineConfigurationDelegate, UIScrollViewDelegate> { id <CDVWebViewEngineProtocol> _webViewEngine; id <CDVCommandDelegate> _commandDelegate; NSMutableDictionary<NSString *, CDVPlugin *> *_pluginObjects; NSMutableDictionary<NSString *, NSString *> *_pluginsMap; CDVCommandQueue* _commandQueue; - UIColor* _backgroundColor; - UIColor* _splashBackgroundColor; + UIColor *_backgroundColor; + UIColor *_splashBackgroundColor; + UIColor *_statusBarBackgroundColor; + UIColor *_statusBarWebViewColor; + UIColor *_statusBarDefaultColor; CDVSettingsDictionary* _settings; } @property (nonatomic, readwrite, strong) NSMutableArray* startupPluginNames; -@property (nonatomic, readwrite, strong) UIView* launchView; +@property (nonatomic, readwrite, strong) UIView *launchView; +@property (nonatomic, readwrite, strong) UIView *statusBar; @property (readwrite, assign) BOOL initialized; @end @@ -60,6 +64,7 @@ static UIColor* defaultBackgroundColor(void) { @synthesize webViewEngine = _webViewEngine; @synthesize backgroundColor = _backgroundColor; @synthesize splashBackgroundColor = _splashBackgroundColor; +@synthesize statusBarBackgroundColor = _statusBarBackgroundColor; @synthesize settings = _settings; @dynamic webView; @dynamic enumerablePlugins; @@ -168,11 +173,49 @@ static UIColor* defaultBackgroundColor(void) { - (void)setBackgroundColor:(UIColor *)color { _backgroundColor = color ?: defaultBackgroundColor(); + + [self.webView setBackgroundColor:self.backgroundColor]; } - (void)setSplashBackgroundColor:(UIColor *)color { _splashBackgroundColor = color ?: self.backgroundColor; + + [self.launchView setBackgroundColor:self.splashBackgroundColor]; +} + +- (UIColor *)statusBarBackgroundColor +{ + // If a status bar background color has been explicitly set using the JS API, we always use that. + // Otherwise, if the webview reports a themeColor meta tag (iOS 15.4+) we use that. + // Otherwise, we use the status bar background color provided in IB (from config.xml). + // Otherwise, we use the background color. + return _statusBarBackgroundColor ?: _statusBarWebViewColor ?: _statusBarDefaultColor ?: self.backgroundColor; +} + +- (void)setStatusBarBackgroundColor:(UIColor *)color +{ + // We want the initial value from IB to set the statusBarDefaultColor and + // then all future changes to set the statusBarBackgroundColor. + // + // The reason for this is that statusBarBackgroundColor is treated like a + // forced override when it is set, and we don't want that for the initial + // value from config.xml set via IB. + + if (!_statusBarBackgroundColor && !_statusBarWebViewColor && !_statusBarDefaultColor) { + _statusBarDefaultColor = color; + } else { + _statusBarBackgroundColor = color; + } + + [self.statusBar setBackgroundColor:self.statusBarBackgroundColor]; +} + +- (void)setStatusBarWebViewColor:(UIColor *)color +{ + _statusBarWebViewColor = color; + + [self.statusBar setBackgroundColor:self.statusBarBackgroundColor]; } // Only for testing @@ -320,6 +363,11 @@ static UIColor* defaultBackgroundColor(void) { [self createGapView]; } + // Instantiate the status bar + if (!self.statusBar) { + [self createStatusBarView]; + } + // ///////////////// if ([self.startupPluginNames count] > 0) { @@ -358,6 +406,7 @@ static UIColor* defaultBackgroundColor(void) { [self.webView setBackgroundColor:self.backgroundColor]; [self.launchView setBackgroundColor:self.splashBackgroundColor]; + [self.statusBar setBackgroundColor:self.statusBarBackgroundColor]; if (self.showInitialSplashScreen) { [self.launchView setAlpha:1]; @@ -525,6 +574,11 @@ static UIColor* defaultBackgroundColor(void) { { self.webView.hidden = NO; + if ([self.webView respondsToSelector:@selector(scrollView)]) { + UIScrollView *scrollView = [self.webView performSelector:@selector(scrollView)]; + [self scrollViewDidChangeAdjustedContentInset:scrollView]; + } + if ([self.settings cordovaBoolSettingForKey:@"AutoHideSplashScreen" defaultValue:YES]) { CGFloat splashScreenDelaySetting = [self.settings cordovaFloatSettingForKey:@"SplashScreenDelay" defaultValue:0]; @@ -541,6 +595,23 @@ static UIColor* defaultBackgroundColor(void) { } } +- (void)scrollViewDidChangeAdjustedContentInset:(UIScrollView *)scrollView +{ + if (self.webView.hidden) { + self.statusBar.hidden = true; + return; + } + + self.statusBar.hidden = (scrollView.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentNever); +} + +- (BOOL)prefersStatusBarHidden +{ + // The CDVStatusBar plugin overrides this in a category extension, and + // should bypass this implementation entirely + return self.statusBar.alpha < 0.0001f; +} + #pragma mark - View Setup - (void)loadSettings @@ -667,6 +738,31 @@ static UIColor* defaultBackgroundColor(void) { [self.view addSubview:view]; [self.view sendSubviewToBack:view]; + + if ([self.webView respondsToSelector:@selector(scrollView)]) { + UIScrollView *scrollView = [self.webView performSelector:@selector(scrollView)]; + scrollView.delegate = self; + } +} + +- (void)createStatusBarView +{ + // If cordova-plugin-statusbar is loaded, we'll let it handle the status + // bar to avoid introducing conflict + if (NSClassFromString(@"CDVStatusBar") != nil) + return; + + self.statusBar = [[UIView alloc] init]; + self.statusBar.translatesAutoresizingMaskIntoConstraints = NO; + + [self.view addSubview:self.statusBar]; + + [self.statusBar.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].active = YES; + [self.statusBar.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor].active = YES; + [self.statusBar.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES; + [self.statusBar.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor].active = YES; + + self.statusBar.hidden = YES; } #pragma mark CordovaCommands @@ -783,6 +879,12 @@ static UIColor* defaultBackgroundColor(void) { }]; } +- (void)showStatusBar:(BOOL)visible +{ + [self.statusBar setAlpha:(visible ? 1 : 0)]; + [self setNeedsStatusBarAppearanceUpdate]; +} + - (void)parseSettingsWithParser:(id <NSXMLParserDelegate>)delegate { [CDVConfigParser parseConfigFile:self.configFilePath withDelegate:delegate]; diff --git a/CordovaLib/CordovaLib.xcodeproj/project.pbxproj b/CordovaLib/CordovaLib.xcodeproj/project.pbxproj index 14e66bea..3b292627 100644 --- a/CordovaLib/CordovaLib.xcodeproj/project.pbxproj +++ b/CordovaLib/CordovaLib.xcodeproj/project.pbxproj @@ -81,6 +81,12 @@ 7ED95D581AB9029B008C4574 /* NSDictionary+CordovaPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D321AB9029B008C4574 /* NSDictionary+CordovaPreferences.m */; }; 7ED95D591AB9029B008C4574 /* NSMutableArray+QueueAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D331AB9029B008C4574 /* NSMutableArray+QueueAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D5A1AB9029B008C4574 /* NSMutableArray+QueueAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D341AB9029B008C4574 /* NSMutableArray+QueueAdditions.m */; }; + 90227B4C2D499A1B005DB74E /* CDVViewController+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 90227B4B2D499A1B005DB74E /* CDVViewController+Private.h */; }; + 90227B4D2D499A1B005DB74E /* CDVViewController+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 90227B4B2D499A1B005DB74E /* CDVViewController+Private.h */; }; + 90227B512D49A042005DB74E /* CDVStatusBarInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 90227B4E2D49A042005DB74E /* CDVStatusBarInternal.h */; }; + 90227B522D49A042005DB74E /* CDVStatusBarInternal.m in Sources */ = {isa = PBXBuildFile; fileRef = 90227B4F2D49A042005DB74E /* CDVStatusBarInternal.m */; }; + 90227B532D49A042005DB74E /* CDVStatusBarInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 90227B4E2D49A042005DB74E /* CDVStatusBarInternal.h */; }; + 90227B542D49A042005DB74E /* CDVStatusBarInternal.m in Sources */ = {isa = PBXBuildFile; fileRef = 90227B4F2D49A042005DB74E /* CDVStatusBarInternal.m */; }; 902B30742C6C5A7E00C6804C /* CordovaLib.docc in Sources */ = {isa = PBXBuildFile; fileRef = 902B30732C6C5A7E00C6804C /* CordovaLib.docc */; }; 902B30752C6C5A7E00C6804C /* CordovaLib.docc in Sources */ = {isa = PBXBuildFile; fileRef = 902B30732C6C5A7E00C6804C /* CordovaLib.docc */; }; 9036843D2C6EB06500A3338C /* CDVAllowList.h in Headers */ = {isa = PBXBuildFile; fileRef = 9036843B2C6EB06500A3338C /* CDVAllowList.h */; }; @@ -194,6 +200,9 @@ 7ED95D321AB9029B008C4574 /* NSDictionary+CordovaPreferences.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+CordovaPreferences.m"; sourceTree = "<group>"; }; 7ED95D331AB9029B008C4574 /* NSMutableArray+QueueAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+QueueAdditions.h"; sourceTree = "<group>"; }; 7ED95D341AB9029B008C4574 /* NSMutableArray+QueueAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray+QueueAdditions.m"; sourceTree = "<group>"; }; + 90227B4B2D499A1B005DB74E /* CDVViewController+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CDVViewController+Private.h"; sourceTree = "<group>"; }; + 90227B4E2D49A042005DB74E /* CDVStatusBarInternal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CDVStatusBarInternal.h; sourceTree = "<group>"; }; + 90227B4F2D49A042005DB74E /* CDVStatusBarInternal.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CDVStatusBarInternal.m; sourceTree = "<group>"; }; 902B30732C6C5A7E00C6804C /* CordovaLib.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = CordovaLib.docc; sourceTree = "<group>"; }; 902D0BC12AEB64EB009C68E5 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; }; 9036843B2C6EB06500A3338C /* CDVAllowList.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CDVAllowList.h; sourceTree = "<group>"; }; @@ -300,6 +309,7 @@ 7ED95CF31AB9028C008C4574 /* CDVJSON_private.h */, 7ED95CF41AB9028C008C4574 /* CDVJSON_private.m */, 7ED95CF51AB9028C008C4574 /* CDVPlugin+Private.h */, + 90227B4B2D499A1B005DB74E /* CDVViewController+Private.h */, 7ED95CF61AB9028C008C4574 /* Plugins */, ); name = Private; @@ -309,6 +319,7 @@ 7ED95CF61AB9028C008C4574 /* Plugins */ = { isa = PBXGroup; children = ( + 90227B502D49A042005DB74E /* CDVStatusBarInternal */, 4E714D3123F5356700A321AF /* CDVLaunchScreen */, 4E23F8F423E16D30006CD852 /* CDVWebViewEngine */, 28BFF9111F355A1D00DDF01A /* CDVLogger */, @@ -349,6 +360,15 @@ path = Classes/Public; sourceTree = "<group>"; }; + 90227B502D49A042005DB74E /* CDVStatusBarInternal */ = { + isa = PBXGroup; + children = ( + 90227B4E2D49A042005DB74E /* CDVStatusBarInternal.h */, + 90227B4F2D49A042005DB74E /* CDVStatusBarInternal.m */, + ); + path = CDVStatusBarInternal; + sourceTree = "<group>"; + }; 9064EF5E26FAB74200C9D65B /* include */ = { isa = PBXGroup; children = ( @@ -401,6 +421,7 @@ buildActionMask = 2147483647; files = ( C0C01EB61E3911D50056E6CB /* Cordova.h in Headers */, + 90227B4C2D499A1B005DB74E /* CDVViewController+Private.h in Headers */, C0C01EBB1E39131A0056E6CB /* CDV.h in Headers */, C0C01EBC1E39131A0056E6CB /* CDVAppDelegate.h in Headers */, 9068B5332C6DFE2000B13532 /* CDVSettingsDictionary.h in Headers */, @@ -432,6 +453,7 @@ 9052DE8E2150D06B008E83D4 /* CDVIntentAndNavigationFilter.h in Headers */, 9052DE8F2150D06B008E83D4 /* CDVHandleOpenURL.h in Headers */, 9047732F2C7A57E900373636 /* CDVURLSchemeHandler.h in Headers */, + 90227B532D49A042005DB74E /* CDVStatusBarInternal.h in Headers */, 2FCCEA18247E7366007276A8 /* CDVLaunchScreen.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -441,6 +463,7 @@ buildActionMask = 2147483647; files = ( 7ED95D351AB9029B008C4574 /* CDV.h in Headers */, + 90227B4D2D499A1B005DB74E /* CDVViewController+Private.h in Headers */, 7ED95D361AB9029B008C4574 /* CDVAppDelegate.h in Headers */, 7ED95D381AB9029B008C4574 /* CDVAvailability.h in Headers */, 9068B5342C6DFE2000B13532 /* CDVSettingsDictionary.h in Headers */, @@ -472,6 +495,7 @@ 3093E2231B16D6A3003F381A /* CDVIntentAndNavigationFilter.h in Headers */, 7E7F69B91ABA3692007546F4 /* CDVHandleOpenURL.h in Headers */, 904773312C7A57E900373636 /* CDVURLSchemeHandler.h in Headers */, + 90227B512D49A042005DB74E /* CDVStatusBarInternal.h in Headers */, 4E714D3623F535B500A321AF /* CDVLaunchScreen.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -568,6 +592,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 90227B542D49A042005DB74E /* CDVStatusBarInternal.m in Sources */, 9052DE712150D040008E83D4 /* CDVAppDelegate.m in Sources */, 9052DE722150D040008E83D4 /* CDVCommandDelegateImpl.m in Sources */, 9052DE732150D040008E83D4 /* CDVCommandQueue.m in Sources */, @@ -600,6 +625,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 90227B522D49A042005DB74E /* CDVStatusBarInternal.m in Sources */, 7ED95D371AB9029B008C4574 /* CDVAppDelegate.m in Sources */, 7ED95D3C1AB9029B008C4574 /* CDVCommandDelegateImpl.m in Sources */, 7ED95D3E1AB9029B008C4574 /* CDVCommandQueue.m in Sources */, diff --git a/CordovaLib/include/Cordova/CDVViewController.h b/CordovaLib/include/Cordova/CDVViewController.h index 434a085f..fa934eae 100644 --- a/CordovaLib/include/Cordova/CDVViewController.h +++ b/CordovaLib/include/Cordova/CDVViewController.h @@ -187,6 +187,13 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, null_resettable, copy) IBInspectable UIColor *splashBackgroundColor; +/** + The color drawn behind the status bar. + + This can be set in the storyboard file as a view controller attribute. + */ +@property (nonatomic, null_resettable, copy) IBInspectable UIColor *statusBarBackgroundColor; + - (UIView*)newCordovaViewWithFrame:(CGRect)bounds; /** diff --git a/cordova-js-src/platform.js b/cordova-js-src/platform.js index 83868c81..b3d729f1 100644 --- a/cordova-js-src/platform.js +++ b/cordova-js-src/platform.js @@ -34,6 +34,10 @@ module.exports = { // see the file under plugin/ios/launchscreen.js require('cordova/modulemapper').clobbers('cordova/plugin/ios/launchscreen', 'navigator.splashscreen'); + // Attach the internal statusBar utility to window.statusbar + // see the file under plugin/ios/statusbar.js + require('cordova/modulemapper').clobbers('cordova/plugin/ios/statusbar', 'window.statusbar'); + require('cordova/channel').onNativeReady.fire(); } }; diff --git a/cordova-js-src/plugin/ios/statusbar.js b/cordova-js-src/plugin/ios/statusbar.js new file mode 100644 index 00000000..8fcc3f6e --- /dev/null +++ b/cordova-js-src/plugin/ios/statusbar.js @@ -0,0 +1,78 @@ +/* + * 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. + * + */ + +var exec = require('cordova/exec'); + +var statusBarVisible = true; +var statusBar = {}; + +Object.defineProperty(statusBar, 'visible', { + configurable: false, + enumerable: true, + get: function () { + if (window.StatusBar) { + // Let the CDVStatusBar plugin handle it + return window.StatusBar.isVisible; + } + + return statusBarVisible; + }, + set: function (value) { + if (window.StatusBar) { + // Let the CDVStatusBar plugin handle it + if (value) { + window.StatusBar.show(); + } else { + window.StatusBar.hide(); + } + } else { + statusBarVisible = value; + exec(null, null, 'StatusBarInternal', 'setVisible', [!!value]); + } + } +}); + +Object.defineProperty(statusBar, 'setBackgroundColor', { + configurable: false, + enumerable: false, + writable: false, + value: function (value) { + var script = document.querySelector('script[src$="cordova.js"]'); + script.style.color = value; + var rgbStr = window.getComputedStyle(script).getPropertyValue('color'); + + if (!rgbStr.match(/^rgb/)) { + return; + } + + var rgbVals = rgbStr.match(/\d+/g).map(function (v) { return parseInt(v, 10); }); + if (rgbVals.length < 3) { + return; + } + + if (window.StatusBar) { + window.StatusBar.backgroundColorByHexString('#' + rgbVals[0].toString(16).padStart(2, '0') + rgbVals[1].toString(16).padStart(2, '0') + rgbVals[2].toString(16).padStart(2, '0')); + } else { + exec(null, null, 'StatusBarInternal', 'setBackgroundColor', rgbVals); + } + } +}); + +module.exports = statusBar; diff --git a/lib/prepare.js b/lib/prepare.js index 54ca51fe..10e1e06d 100644 --- a/lib/prepare.js +++ b/lib/prepare.js @@ -614,6 +614,21 @@ function getSplashScreenBackgroundColorDir (projectRoot, platformProjDir) { } } +/** + * Returns the directory for the StatusBarBackgroundColor.colorset asset, or + * null if no xcassets exist. + * + * @param {string} projectRoot The project's root directory + * @param {string} platformProjDir The platform's project directory + */ +function getStatusBarBackgroundColorDir (projectRoot, platformProjDir) { + if (folderExists(path.join(projectRoot, platformProjDir, 'Assets.xcassets/'))) { + return path.join(platformProjDir, 'Assets.xcassets', 'StatusBarBackgroundColor.colorset'); + } else { + return null; + } +} + function colorPreferenceToComponents (pref) { if (!pref || !pref.match(/^(#[0-9A-Fa-f]{3}|(0x|#)([0-9A-Fa-f]{2})?[0-9A-Fa-f]{6})$/)) { return { @@ -668,6 +683,7 @@ function updateBackgroundColor (cordovaProject, locations) { const pref = cordovaProject.projectConfig.getPreference('BackgroundColor', 'ios') || ''; const splashPref = cordovaProject.projectConfig.getPreference('SplashScreenBackgroundColor', 'ios') || pref; + const statusBarPref = cordovaProject.projectConfig.getPreference('StatusBarBackgroundColor', 'ios') || pref; const backgroundColorDir = getBackgroundColorDir(cordovaProject.root, platformProjDir); if (backgroundColorDir) { @@ -704,6 +720,24 @@ function updateBackgroundColor (cordovaProject, locations) { fs.writeFileSync(path.join(cordovaProject.root, splashBackgroundColorDir, 'Contents.json'), JSON.stringify(contentsJSON, null, 2)); } + + const statusBarBackgroundColorDir = getStatusBarBackgroundColorDir(cordovaProject.root, platformProjDir); + if (statusBarBackgroundColorDir) { + const contentsJSON = { + colors: [{ + idiom: 'universal', + color: colorPreferenceToComponents(statusBarPref) + }], + info: { + author: 'Xcode', + version: 1 + } + }; + + events.emit('verbose', 'Updating Status Bar Background Color color set Contents.json'); + fs.writeFileSync(path.join(cordovaProject.root, statusBarBackgroundColorDir, 'Contents.json'), + JSON.stringify(contentsJSON, null, 2)); + } } /** @@ -740,6 +774,13 @@ function cleanBackgroundColor (projectRoot, projectConfig, locations) { fs.writeFileSync(path.join(projectRoot, splashBackgroundColorDir, 'Contents.json'), JSON.stringify(contentsJSON, null, 2)); } + + const statusBarBackgroundColorDir = getStatusBarBackgroundColorDir(projectRoot, platformProjDir); + if (statusBarBackgroundColorDir) { + events.emit('verbose', 'Cleaning Status Bar Background Color color set Contents.json'); + fs.writeFileSync(path.join(projectRoot, statusBarBackgroundColorDir, 'Contents.json'), + JSON.stringify(contentsJSON, null, 2)); + } } function updateFileResources (cordovaProject, locations) { diff --git a/templates/cordova/defaults.xml b/templates/cordova/defaults.xml index e7f2776d..98eafe9a 100644 --- a/templates/cordova/defaults.xml +++ b/templates/cordova/defaults.xml @@ -56,5 +56,9 @@ <param name="ios-package" value="CDVGestureHandler"/> <param name="onload" value="true"/> </feature> + <feature name="StatusBarInternal"> + <param name="ios-package" value="CDVStatusBarInternal"/> + <param name="onload" value="true"/> + </feature> </widget> diff --git a/templates/project/App/Assets.xcassets/StatusBarBackgroundColor.colorset/Contents.json b/templates/project/App/Assets.xcassets/StatusBarBackgroundColor.colorset/Contents.json new file mode 100644 index 00000000..462830f8 --- /dev/null +++ b/templates/project/App/Assets.xcassets/StatusBarBackgroundColor.colorset/Contents.json @@ -0,0 +1,15 @@ +{ + "colors" : [ + { + "color" : { + "platform" : "ios", + "reference" : "systemBackgroundColor" + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/templates/project/App/Base.lproj/Main.storyboard b/templates/project/App/Base.lproj/Main.storyboard index 0655267c..3372d5d2 100644 --- a/templates/project/App/Base.lproj/Main.storyboard +++ b/templates/project/App/Base.lproj/Main.storyboard @@ -42,6 +42,9 @@ <color key="value" name="SplashScreenBackgroundColor"/> </userDefinedRuntimeAttribute> <userDefinedRuntimeAttribute type="boolean" keyPath="showSplashScreen" value="YES"/> + <userDefinedRuntimeAttribute type="color" keyPath="statusBarBackgroundColor"> + <color key="value" name="StatusBarBackgroundColor"/> + </userDefinedRuntimeAttribute> </userDefinedRuntimeAttributes> </viewController> <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> @@ -55,6 +58,9 @@ <namedColor name="SplashScreenBackgroundColor"> <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> </namedColor> + <namedColor name="StatusBarBackgroundColor"> + <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + </namedColor> </resources> </document> diff --git a/tests/spec/unit/fixtures/ios-config-xml/App/Assets.xcassets/StatusBarBackgroundColor.colorset/Contents.json b/tests/spec/unit/fixtures/ios-config-xml/App/Assets.xcassets/StatusBarBackgroundColor.colorset/Contents.json new file mode 100644 index 00000000..462830f8 --- /dev/null +++ b/tests/spec/unit/fixtures/ios-config-xml/App/Assets.xcassets/StatusBarBackgroundColor.colorset/Contents.json @@ -0,0 +1,15 @@ +{ + "colors" : [ + { + "color" : { + "platform" : "ios", + "reference" : "systemBackgroundColor" + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/tests/spec/unit/fixtures/ios-config-xml/App/Base.lproj/Main.storyboard b/tests/spec/unit/fixtures/ios-config-xml/App/Base.lproj/Main.storyboard index 0655267c..3372d5d2 100644 --- a/tests/spec/unit/fixtures/ios-config-xml/App/Base.lproj/Main.storyboard +++ b/tests/spec/unit/fixtures/ios-config-xml/App/Base.lproj/Main.storyboard @@ -42,6 +42,9 @@ <color key="value" name="SplashScreenBackgroundColor"/> </userDefinedRuntimeAttribute> <userDefinedRuntimeAttribute type="boolean" keyPath="showSplashScreen" value="YES"/> + <userDefinedRuntimeAttribute type="color" keyPath="statusBarBackgroundColor"> + <color key="value" name="StatusBarBackgroundColor"/> + </userDefinedRuntimeAttribute> </userDefinedRuntimeAttributes> </viewController> <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> @@ -55,6 +58,9 @@ <namedColor name="SplashScreenBackgroundColor"> <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> </namedColor> + <namedColor name="StatusBarBackgroundColor"> + <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + </namedColor> </resources> </document> --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cordova.apache.org For additional commands, e-mail: commits-h...@cordova.apache.org