Initial Commit ...
This commit is contained in:
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
NOTE: plugman/cordova cli should have already installed this,
|
||||
but you need the value UIViewControllerBasedStatusBarAppearance
|
||||
in your Info.plist as well to set the styles in iOS 7
|
||||
*/
|
||||
|
||||
#import "CDVStatusBar.h"
|
||||
#import <objc/runtime.h>
|
||||
#import <Cordova/CDVViewController.h>
|
||||
|
||||
static const void *kHideStatusBar = &kHideStatusBar;
|
||||
static const void *kStatusBarStyle = &kStatusBarStyle;
|
||||
|
||||
@interface CDVViewController (StatusBar)
|
||||
|
||||
@property (nonatomic, retain) id sb_hideStatusBar;
|
||||
@property (nonatomic, retain) id sb_statusBarStyle;
|
||||
|
||||
@end
|
||||
|
||||
@implementation CDVViewController (StatusBar)
|
||||
|
||||
@dynamic sb_hideStatusBar;
|
||||
@dynamic sb_statusBarStyle;
|
||||
|
||||
- (id)sb_hideStatusBar {
|
||||
return objc_getAssociatedObject(self, kHideStatusBar);
|
||||
}
|
||||
|
||||
- (void)setSb_hideStatusBar:(id)newHideStatusBar {
|
||||
objc_setAssociatedObject(self, kHideStatusBar, newHideStatusBar, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
|
||||
- (id)sb_statusBarStyle {
|
||||
return objc_getAssociatedObject(self, kStatusBarStyle);
|
||||
}
|
||||
|
||||
- (void)setSb_statusBarStyle:(id)newStatusBarStyle {
|
||||
objc_setAssociatedObject(self, kStatusBarStyle, newStatusBarStyle, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
|
||||
- (BOOL) prefersStatusBarHidden {
|
||||
return [self.sb_hideStatusBar boolValue];
|
||||
}
|
||||
|
||||
- (UIStatusBarStyle)preferredStatusBarStyle
|
||||
{
|
||||
return (UIStatusBarStyle)[self.sb_statusBarStyle intValue];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface CDVStatusBar () <UIScrollViewDelegate>
|
||||
- (void)fireTappedEvent;
|
||||
- (void)updateIsVisible:(BOOL)visible;
|
||||
@end
|
||||
|
||||
@implementation CDVStatusBar
|
||||
|
||||
- (id)settingForKey:(NSString*)key
|
||||
{
|
||||
return [self.commandDelegate.settings objectForKey:[key lowercaseString]];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context
|
||||
{
|
||||
if ([keyPath isEqual:@"statusBarHidden"]) {
|
||||
NSNumber* newValue = [change objectForKey:NSKeyValueChangeNewKey];
|
||||
[self updateIsVisible:![newValue boolValue]];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)cordovaViewWillAppear:(NSNotification*)notification
|
||||
{
|
||||
[self resizeWebView];
|
||||
}
|
||||
|
||||
-(void)statusBarDidChangeFrame:(NSNotification*)notification
|
||||
{
|
||||
//add a small delay ( 0.1 seconds ) or statusbar size will be wrong
|
||||
__weak CDVStatusBar* weakSelf = self;
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
|
||||
[weakSelf resizeStatusBarBackgroundView];
|
||||
[weakSelf resizeWebView];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)pluginInitialize
|
||||
{
|
||||
// init
|
||||
NSNumber* uiviewControllerBasedStatusBarAppearance = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIViewControllerBasedStatusBarAppearance"];
|
||||
_uiviewControllerBasedStatusBarAppearance = (uiviewControllerBasedStatusBarAppearance == nil || [uiviewControllerBasedStatusBarAppearance boolValue]);
|
||||
|
||||
// observe the statusBarHidden property
|
||||
[[UIApplication sharedApplication] addObserver:self forKeyPath:@"statusBarHidden" options:NSKeyValueObservingOptionNew context:NULL];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarDidChangeFrame:) name: UIApplicationDidChangeStatusBarFrameNotification object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cordovaViewWillAppear:) name: @"CDVViewWillAppearNotification" object:nil];
|
||||
|
||||
_statusBarOverlaysWebView = YES; // default
|
||||
|
||||
[self initializeStatusBarBackgroundView];
|
||||
|
||||
self.viewController.view.autoresizesSubviews = YES;
|
||||
|
||||
NSString* setting;
|
||||
|
||||
setting = @"StatusBarBackgroundColor";
|
||||
if ([self settingForKey:setting]) {
|
||||
[self _backgroundColorByHexString:[self settingForKey:setting]];
|
||||
}
|
||||
|
||||
setting = @"StatusBarStyle";
|
||||
if ([self settingForKey:setting]) {
|
||||
[self setStatusBarStyle:[self settingForKey:setting]];
|
||||
}
|
||||
|
||||
setting = @"StatusBarDefaultScrollToTop";
|
||||
if ([self settingForKey:setting]) {
|
||||
self.webView.scrollView.scrollsToTop = [(NSNumber*)[self settingForKey:setting] boolValue];
|
||||
} else {
|
||||
self.webView.scrollView.scrollsToTop = NO;
|
||||
}
|
||||
|
||||
// blank scroll view to intercept status bar taps
|
||||
UIScrollView *fakeScrollView = [[UIScrollView alloc] initWithFrame:UIScreen.mainScreen.bounds];
|
||||
fakeScrollView.delegate = self;
|
||||
fakeScrollView.scrollsToTop = YES;
|
||||
[self.viewController.view addSubview:fakeScrollView]; // Add scrollview to the view heirarchy so that it will begin accepting status bar taps
|
||||
[self.viewController.view sendSubviewToBack:fakeScrollView]; // Send it to the very back of the view heirarchy
|
||||
fakeScrollView.contentSize = CGSizeMake(UIScreen.mainScreen.bounds.size.width, UIScreen.mainScreen.bounds.size.height * 2.0f); // Make the scroll view longer than the screen itself
|
||||
fakeScrollView.contentOffset = CGPointMake(0.0f, UIScreen.mainScreen.bounds.size.height); // Scroll down so a tap will take scroll view back to the top
|
||||
|
||||
_statusBarVisible = ![UIApplication sharedApplication].isStatusBarHidden;
|
||||
}
|
||||
|
||||
- (void)onReset {
|
||||
_eventsCallbackId = nil;
|
||||
}
|
||||
|
||||
- (void)fireTappedEvent {
|
||||
if (_eventsCallbackId == nil) {
|
||||
return;
|
||||
}
|
||||
NSDictionary* payload = @{@"type": @"tap"};
|
||||
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:payload];
|
||||
[result setKeepCallbackAsBool:YES];
|
||||
[self.commandDelegate sendPluginResult:result callbackId:_eventsCallbackId];
|
||||
}
|
||||
|
||||
- (void)updateIsVisible:(BOOL)visible {
|
||||
if (_eventsCallbackId == nil) {
|
||||
return;
|
||||
}
|
||||
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:visible];
|
||||
[result setKeepCallbackAsBool:YES];
|
||||
[self.commandDelegate sendPluginResult:result callbackId:_eventsCallbackId];
|
||||
}
|
||||
|
||||
- (void) _ready:(CDVInvokedUrlCommand*)command
|
||||
{
|
||||
_eventsCallbackId = command.callbackId;
|
||||
[self updateIsVisible:![UIApplication sharedApplication].statusBarHidden];
|
||||
NSString* setting = @"StatusBarOverlaysWebView";
|
||||
if ([self settingForKey:setting]) {
|
||||
self.statusBarOverlaysWebView = [(NSNumber*)[self settingForKey:setting] boolValue];
|
||||
if (self.statusBarOverlaysWebView) {
|
||||
[self resizeWebView];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) initializeStatusBarBackgroundView
|
||||
{
|
||||
CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
|
||||
|
||||
if ([[UIApplication sharedApplication]statusBarOrientation] == UIInterfaceOrientationPortraitUpsideDown &&
|
||||
statusBarFrame.size.height + statusBarFrame.origin.y == [self.viewController.view.window bounds].size.height) {
|
||||
|
||||
// When started in upside-down orientation on iOS 7, status bar will be bound to lower edge of the
|
||||
// screen (statusBarFrame.origin.y will be somewhere around screen height). In this case we need to
|
||||
// correct frame's coordinates
|
||||
statusBarFrame.origin.y = 0;
|
||||
}
|
||||
|
||||
_statusBarBackgroundView = [[UIView alloc] initWithFrame:statusBarFrame];
|
||||
_statusBarBackgroundView.backgroundColor = _statusBarBackgroundColor;
|
||||
_statusBarBackgroundView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin);
|
||||
_statusBarBackgroundView.autoresizesSubviews = YES;
|
||||
}
|
||||
|
||||
- (void) setStatusBarOverlaysWebView:(BOOL)statusBarOverlaysWebView
|
||||
{
|
||||
// we only care about the latest iOS version or a change in setting
|
||||
if (statusBarOverlaysWebView == _statusBarOverlaysWebView) {
|
||||
return;
|
||||
}
|
||||
|
||||
_statusBarOverlaysWebView = statusBarOverlaysWebView;
|
||||
|
||||
[self resizeWebView];
|
||||
|
||||
if (statusBarOverlaysWebView) {
|
||||
|
||||
[_statusBarBackgroundView removeFromSuperview];
|
||||
|
||||
} else {
|
||||
|
||||
[self initializeStatusBarBackgroundView];
|
||||
[self.webView.superview addSubview:_statusBarBackgroundView];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (BOOL) statusBarOverlaysWebView
|
||||
{
|
||||
return _statusBarOverlaysWebView;
|
||||
}
|
||||
|
||||
- (void) overlaysWebView:(CDVInvokedUrlCommand*)command
|
||||
{
|
||||
id value = [command argumentAtIndex:0];
|
||||
if (!([value isKindOfClass:[NSNumber class]])) {
|
||||
value = [NSNumber numberWithBool:YES];
|
||||
}
|
||||
|
||||
self.statusBarOverlaysWebView = [value boolValue];
|
||||
}
|
||||
|
||||
- (void) refreshStatusBarAppearance
|
||||
{
|
||||
SEL sel = NSSelectorFromString(@"setNeedsStatusBarAppearanceUpdate");
|
||||
if ([self.viewController respondsToSelector:sel]) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
||||
[self.viewController performSelector:sel withObject:nil];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
|
||||
- (void) setStyleForStatusBar:(UIStatusBarStyle)style
|
||||
{
|
||||
if (_uiviewControllerBasedStatusBarAppearance) {
|
||||
CDVViewController* vc = (CDVViewController*)self.viewController;
|
||||
vc.sb_statusBarStyle = [NSNumber numberWithInt:style];
|
||||
[self refreshStatusBarAppearance];
|
||||
|
||||
} else {
|
||||
[[UIApplication sharedApplication] setStatusBarStyle:style];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) setStatusBarStyle:(NSString*)statusBarStyle
|
||||
{
|
||||
// default, lightContent, blackTranslucent, blackOpaque
|
||||
NSString* lcStatusBarStyle = [statusBarStyle lowercaseString];
|
||||
|
||||
if ([lcStatusBarStyle isEqualToString:@"default"]) {
|
||||
[self styleDefault:nil];
|
||||
} else if ([lcStatusBarStyle isEqualToString:@"lightcontent"]) {
|
||||
[self styleLightContent:nil];
|
||||
} else if ([lcStatusBarStyle isEqualToString:@"blacktranslucent"]) {
|
||||
[self styleBlackTranslucent:nil];
|
||||
} else if ([lcStatusBarStyle isEqualToString:@"blackopaque"]) {
|
||||
[self styleBlackOpaque:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) styleDefault:(CDVInvokedUrlCommand*)command
|
||||
{
|
||||
[self setStyleForStatusBar:UIStatusBarStyleDefault];
|
||||
}
|
||||
|
||||
- (void) styleLightContent:(CDVInvokedUrlCommand*)command
|
||||
{
|
||||
[self setStyleForStatusBar:UIStatusBarStyleLightContent];
|
||||
}
|
||||
|
||||
- (void) styleBlackTranslucent:(CDVInvokedUrlCommand*)command
|
||||
{
|
||||
[self setStyleForStatusBar:UIStatusBarStyleLightContent];
|
||||
}
|
||||
|
||||
- (void) styleBlackOpaque:(CDVInvokedUrlCommand*)command
|
||||
{
|
||||
[self setStyleForStatusBar:UIStatusBarStyleLightContent];
|
||||
}
|
||||
|
||||
- (void) backgroundColorByName:(CDVInvokedUrlCommand*)command
|
||||
{
|
||||
id value = [command argumentAtIndex:0];
|
||||
if (!([value isKindOfClass:[NSString class]])) {
|
||||
value = @"black";
|
||||
}
|
||||
|
||||
SEL selector = NSSelectorFromString([value stringByAppendingString:@"Color"]);
|
||||
if ([UIColor respondsToSelector:selector]) {
|
||||
_statusBarBackgroundView.backgroundColor = [UIColor performSelector:selector];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) _backgroundColorByHexString:(NSString*)hexString
|
||||
{
|
||||
unsigned int rgbValue = 0;
|
||||
NSScanner* scanner = [NSScanner scannerWithString:hexString];
|
||||
[scanner setScanLocation:1];
|
||||
[scanner scanHexInt:&rgbValue];
|
||||
|
||||
_statusBarBackgroundColor = [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 green:((rgbValue & 0xFF00) >> 8)/255.0 blue:(rgbValue & 0xFF)/255.0 alpha:1.0];
|
||||
_statusBarBackgroundView.backgroundColor = _statusBarBackgroundColor;
|
||||
}
|
||||
|
||||
- (void) backgroundColorByHexString:(CDVInvokedUrlCommand*)command
|
||||
{
|
||||
NSString* value = [command argumentAtIndex:0];
|
||||
if (!([value isKindOfClass:[NSString class]])) {
|
||||
value = @"#000000";
|
||||
}
|
||||
|
||||
if (![value hasPrefix:@"#"] || [value length] < 7) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self _backgroundColorByHexString:value];
|
||||
}
|
||||
|
||||
- (void) hideStatusBar
|
||||
{
|
||||
if (_uiviewControllerBasedStatusBarAppearance) {
|
||||
CDVViewController* vc = (CDVViewController*)self.viewController;
|
||||
vc.sb_hideStatusBar = [NSNumber numberWithBool:YES];
|
||||
[self refreshStatusBarAppearance];
|
||||
|
||||
} else {
|
||||
UIApplication* app = [UIApplication sharedApplication];
|
||||
[app setStatusBarHidden:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) hide:(CDVInvokedUrlCommand*)command
|
||||
{
|
||||
_statusBarVisible = NO;
|
||||
UIApplication* app = [UIApplication sharedApplication];
|
||||
|
||||
if (!app.isStatusBarHidden)
|
||||
{
|
||||
|
||||
[self hideStatusBar];
|
||||
|
||||
[_statusBarBackgroundView removeFromSuperview];
|
||||
|
||||
[self resizeWebView];
|
||||
|
||||
_statusBarBackgroundView.hidden = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) showStatusBar
|
||||
{
|
||||
if (_uiviewControllerBasedStatusBarAppearance) {
|
||||
CDVViewController* vc = (CDVViewController*)self.viewController;
|
||||
vc.sb_hideStatusBar = [NSNumber numberWithBool:NO];
|
||||
[self refreshStatusBarAppearance];
|
||||
|
||||
} else {
|
||||
UIApplication* app = [UIApplication sharedApplication];
|
||||
[app setStatusBarHidden:NO];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) show:(CDVInvokedUrlCommand*)command
|
||||
{
|
||||
_statusBarVisible = YES;
|
||||
UIApplication* app = [UIApplication sharedApplication];
|
||||
|
||||
if (app.isStatusBarHidden)
|
||||
{
|
||||
[self showStatusBar];
|
||||
[self resizeWebView];
|
||||
|
||||
if (!self.statusBarOverlaysWebView) {
|
||||
|
||||
// there is a possibility that when the statusbar was hidden, it was in a different orientation
|
||||
// from the current one. Therefore we need to expand the statusBarBackgroundView as well to the
|
||||
// statusBar's current size
|
||||
[self resizeStatusBarBackgroundView];
|
||||
[self.webView.superview addSubview:_statusBarBackgroundView];
|
||||
|
||||
}
|
||||
|
||||
_statusBarBackgroundView.hidden = NO;
|
||||
}
|
||||
}
|
||||
|
||||
-(void)resizeStatusBarBackgroundView {
|
||||
CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
|
||||
CGRect sbBgFrame = _statusBarBackgroundView.frame;
|
||||
sbBgFrame.size = statusBarFrame.size;
|
||||
_statusBarBackgroundView.frame = sbBgFrame;
|
||||
}
|
||||
|
||||
-(void)resizeWebView
|
||||
{
|
||||
BOOL isIOS11 = (IsAtLeastiOSVersion(@"11.0"));
|
||||
|
||||
CGRect bounds = [self.viewController.view.window bounds];
|
||||
if (CGRectEqualToRect(bounds, CGRectZero)) {
|
||||
bounds = [[UIScreen mainScreen] bounds];
|
||||
}
|
||||
|
||||
self.viewController.view.frame = bounds;
|
||||
|
||||
self.webView.frame = bounds;
|
||||
|
||||
CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
|
||||
CGRect frame = self.webView.frame;
|
||||
CGFloat height = statusBarFrame.size.height;
|
||||
|
||||
if (!self.statusBarOverlaysWebView) {
|
||||
frame.origin.y = height;
|
||||
} else {
|
||||
frame.origin.y = height >= 20 ? height - 20 : 0;
|
||||
if (isIOS11) {
|
||||
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
|
||||
if (@available(iOS 11.0, *)) {
|
||||
float safeAreaTop = self.webView.safeAreaInsets.top;
|
||||
if (height >= safeAreaTop && safeAreaTop >0) {
|
||||
// Sometimes when in-call/recording/hotspot larger status bar is present, the safeAreaTop is 40 but we want frame.origin.y to be 20
|
||||
frame.origin.y = safeAreaTop == 40 ? 20 : height - safeAreaTop;
|
||||
} else {
|
||||
frame.origin.y = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
frame.size.height -= frame.origin.y;
|
||||
self.webView.frame = frame;
|
||||
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[[UIApplication sharedApplication] removeObserver:self forKeyPath:@"statusBarHidden"];
|
||||
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - UIScrollViewDelegate
|
||||
|
||||
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView
|
||||
{
|
||||
[self fireTappedEvent];
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user