|
@@ -0,0 +1,600 @@
|
|
|
+//
|
|
|
+// JDStatusBarNotification.m
|
|
|
+//
|
|
|
+// Based on KGStatusBar by Kevin Gibbon
|
|
|
+//
|
|
|
+// Created by Markus Emrich on 10/28/13.
|
|
|
+// Copyright 2013 Markus Emrich. All rights reserved.
|
|
|
+//
|
|
|
+
|
|
|
+#import <QuartzCore/QuartzCore.h>
|
|
|
+
|
|
|
+#import "JDStatusBarNotification.h"
|
|
|
+
|
|
|
+@interface JDStatusBarStyle (Hidden)
|
|
|
++ (NSArray*)allDefaultStyleIdentifier;
|
|
|
++ (JDStatusBarStyle*)defaultStyleWithName:(NSString*)styleName;
|
|
|
+@end
|
|
|
+
|
|
|
+@interface JDStatusBarNotificationViewController : UIViewController
|
|
|
+@end
|
|
|
+
|
|
|
+@interface UIApplication (mainWindow)
|
|
|
+- (UIWindow*)mainApplicationWindowIgnoringWindow:(UIWindow*)ignoringWindow;
|
|
|
+@end
|
|
|
+
|
|
|
+@interface JDStatusBarNotification () <CAAnimationDelegate>
|
|
|
+@property (nonatomic, strong, readonly) UIWindow *overlayWindow;
|
|
|
+@property (nonatomic, strong, readonly) UIView *progressView;
|
|
|
+@property (nonatomic, strong, readonly) JDStatusBarView *topBar;
|
|
|
+
|
|
|
+@property (nonatomic, strong) NSTimer *dismissTimer;
|
|
|
+@property (nonatomic, assign) CGFloat progress;
|
|
|
+
|
|
|
+@property (nonatomic, weak) JDStatusBarStyle *activeStyle;
|
|
|
+@property (nonatomic, strong) JDStatusBarStyle *defaultStyle;
|
|
|
+@property (nonatomic, strong) NSMutableDictionary *userStyles;
|
|
|
+@end
|
|
|
+
|
|
|
+@implementation JDStatusBarNotification
|
|
|
+
|
|
|
+@synthesize overlayWindow = _overlayWindow;
|
|
|
+@synthesize progressView = _progressView;
|
|
|
+@synthesize topBar = _topBar;
|
|
|
+
|
|
|
+#pragma mark Class methods
|
|
|
+
|
|
|
++ (JDStatusBarNotification*)sharedInstance {
|
|
|
+ static dispatch_once_t once;
|
|
|
+ static JDStatusBarNotification *sharedInstance;
|
|
|
+ dispatch_once(&once, ^ {
|
|
|
+ sharedInstance = [[self alloc] init];
|
|
|
+ });
|
|
|
+ return sharedInstance;
|
|
|
+}
|
|
|
+
|
|
|
++ (UIView*)showWithStatus:(NSString *)status;
|
|
|
+{
|
|
|
+ return [[self sharedInstance] showWithStatus:status
|
|
|
+ styleName:nil];
|
|
|
+}
|
|
|
+
|
|
|
++ (UIView*)showWithStatus:(NSString *)status
|
|
|
+ styleName:(NSString*)styleName;
|
|
|
+{
|
|
|
+ return [[self sharedInstance] showWithStatus:status
|
|
|
+ styleName:styleName];
|
|
|
+}
|
|
|
+
|
|
|
++ (UIView*)showWithStatus:(NSString *)status
|
|
|
+ dismissAfter:(NSTimeInterval)timeInterval;
|
|
|
+{
|
|
|
+ UIView *view = [[self sharedInstance] showWithStatus:status
|
|
|
+ styleName:nil];
|
|
|
+ [self dismissAfter:timeInterval];
|
|
|
+ return view;
|
|
|
+}
|
|
|
+
|
|
|
++ (UIView*)showWithStatus:(NSString *)status
|
|
|
+ dismissAfter:(NSTimeInterval)timeInterval
|
|
|
+ styleName:(NSString*)styleName;
|
|
|
+{
|
|
|
+ UIView *view = [[self sharedInstance] showWithStatus:status
|
|
|
+ styleName:styleName];
|
|
|
+ [self dismissAfter:timeInterval];
|
|
|
+ return view;
|
|
|
+}
|
|
|
+
|
|
|
++ (void)dismiss;
|
|
|
+{
|
|
|
+ [self dismissAnimated:YES];
|
|
|
+}
|
|
|
+
|
|
|
++ (void)dismissAnimated:(BOOL)animated;
|
|
|
+{
|
|
|
+ [[self sharedInstance] dismissAnimated:animated];
|
|
|
+}
|
|
|
+
|
|
|
++ (void)dismissAfter:(NSTimeInterval)delay;
|
|
|
+{
|
|
|
+ [[self sharedInstance] setDismissTimerWithInterval:delay];
|
|
|
+}
|
|
|
+
|
|
|
++ (void)setDefaultStyle:(JDPrepareStyleBlock)prepareBlock;
|
|
|
+{
|
|
|
+ NSAssert(prepareBlock != nil, @"No prepareBlock provided");
|
|
|
+
|
|
|
+ JDStatusBarStyle *style = [[self sharedInstance].defaultStyle copy];
|
|
|
+ [self sharedInstance].defaultStyle = prepareBlock(style);
|
|
|
+}
|
|
|
+
|
|
|
++ (NSString*)addStyleNamed:(NSString*)identifier
|
|
|
+ prepare:(JDPrepareStyleBlock)prepareBlock;
|
|
|
+{
|
|
|
+ return [[self sharedInstance] addStyleNamed:identifier
|
|
|
+ prepare:prepareBlock];
|
|
|
+}
|
|
|
+
|
|
|
++ (void)showProgress:(CGFloat)progress;
|
|
|
+{
|
|
|
+ [[self sharedInstance] setProgress:progress];
|
|
|
+}
|
|
|
+
|
|
|
++ (void)showActivityIndicator:(BOOL)show indicatorStyle:(UIActivityIndicatorViewStyle)style;
|
|
|
+{
|
|
|
+ [[self sharedInstance] showActivityIndicator:show indicatorStyle:style];
|
|
|
+}
|
|
|
+
|
|
|
++ (BOOL)isVisible;
|
|
|
+{
|
|
|
+ return [[self sharedInstance] isVisible];
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark Implementation
|
|
|
+
|
|
|
+- (id)init
|
|
|
+{
|
|
|
+ if ((self = [super init]))
|
|
|
+ {
|
|
|
+ // set defaults
|
|
|
+ [self setupDefaultStyles];
|
|
|
+
|
|
|
+ // register for orientation changes
|
|
|
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willChangeStatusBarFrame:)
|
|
|
+ name:UIApplicationWillChangeStatusBarFrameNotification object:nil];
|
|
|
+ }
|
|
|
+ return self;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)dealloc
|
|
|
+{
|
|
|
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark Custom styles
|
|
|
+
|
|
|
+- (void)setupDefaultStyles;
|
|
|
+{
|
|
|
+ self.defaultStyle = [JDStatusBarStyle defaultStyleWithName:JDStatusBarStyleDefault];
|
|
|
+
|
|
|
+ self.userStyles = [NSMutableDictionary dictionary];
|
|
|
+ for (NSString *styleName in [JDStatusBarStyle allDefaultStyleIdentifier]) {
|
|
|
+ [self.userStyles setObject:[JDStatusBarStyle defaultStyleWithName:styleName] forKey:styleName];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (NSString*)addStyleNamed:(NSString*)identifier
|
|
|
+ prepare:(JDPrepareStyleBlock)prepareBlock;
|
|
|
+{
|
|
|
+ NSAssert(identifier != nil, @"No identifier provided");
|
|
|
+ NSAssert(prepareBlock != nil, @"No prepareBlock provided");
|
|
|
+
|
|
|
+ JDStatusBarStyle *style = [self.defaultStyle copy];
|
|
|
+ [self.userStyles setObject:prepareBlock(style) forKey:identifier];
|
|
|
+ return identifier;
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark Presentation
|
|
|
+
|
|
|
+- (UIView*)showWithStatus:(NSString *)status
|
|
|
+ styleName:(NSString*)styleName;
|
|
|
+{
|
|
|
+ JDStatusBarStyle *style = nil;
|
|
|
+ if (styleName != nil) {
|
|
|
+ style = self.userStyles[styleName];
|
|
|
+ }
|
|
|
+
|
|
|
+ if (style == nil) style = self.defaultStyle;
|
|
|
+ return [self showWithStatus:status style:style];
|
|
|
+}
|
|
|
+
|
|
|
+- (UIView*)showWithStatus:(NSString *)status
|
|
|
+ style:(JDStatusBarStyle*)style;
|
|
|
+{
|
|
|
+ // first, check if status bar is visible at all
|
|
|
+ if ([UIApplication sharedApplication].statusBarHidden) return nil;
|
|
|
+
|
|
|
+ // prepare for new style
|
|
|
+ if (style != self.activeStyle) {
|
|
|
+ self.activeStyle = style;
|
|
|
+ if (self.activeStyle.animationType == JDStatusBarAnimationTypeFade) {
|
|
|
+ self.topBar.alpha = 0.0;
|
|
|
+ self.topBar.transform = CGAffineTransformIdentity;
|
|
|
+ } else {
|
|
|
+ self.topBar.alpha = 1.0;
|
|
|
+ self.topBar.transform = CGAffineTransformMakeTranslation(0, -self.topBar.frame.size.height);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // cancel previous dismissing & remove animations
|
|
|
+ [[NSRunLoop currentRunLoop] cancelPerformSelector:@selector(dismiss) target:self argument:nil];
|
|
|
+ [self.topBar.layer removeAllAnimations];
|
|
|
+
|
|
|
+ // create & show window
|
|
|
+ [self.overlayWindow setHidden:NO];
|
|
|
+
|
|
|
+ // update style
|
|
|
+ self.topBar.backgroundColor = style.barColor;
|
|
|
+ self.topBar.textVerticalPositionAdjustment = style.textVerticalPositionAdjustment;
|
|
|
+ UILabel *textLabel = self.topBar.textLabel;
|
|
|
+ textLabel.textColor = style.textColor;
|
|
|
+ textLabel.font = style.font;
|
|
|
+ textLabel.accessibilityLabel = status;
|
|
|
+ textLabel.text = status;
|
|
|
+
|
|
|
+ if (style.textShadow) {
|
|
|
+ textLabel.shadowColor = style.textShadow.shadowColor;
|
|
|
+ textLabel.shadowOffset = style.textShadow.shadowOffset;
|
|
|
+ } else {
|
|
|
+ textLabel.shadowColor = nil;
|
|
|
+ textLabel.shadowOffset = CGSizeZero;
|
|
|
+ }
|
|
|
+
|
|
|
+ // reset progress & activity
|
|
|
+ self.progress = 0.0;
|
|
|
+ [self showActivityIndicator:NO indicatorStyle:0];
|
|
|
+
|
|
|
+ // animate in
|
|
|
+ BOOL animationsEnabled = (style.animationType != JDStatusBarAnimationTypeNone);
|
|
|
+ if (animationsEnabled && style.animationType == JDStatusBarAnimationTypeBounce) {
|
|
|
+ [self animateInWithBounceAnimation];
|
|
|
+ } else {
|
|
|
+ [UIView animateWithDuration:(animationsEnabled ? 0.4 : 0.0) animations:^{
|
|
|
+ self.topBar.alpha = 1.0;
|
|
|
+ self.topBar.transform = CGAffineTransformIdentity;
|
|
|
+ }];
|
|
|
+ }
|
|
|
+
|
|
|
+ return self.topBar;
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark Dismissal
|
|
|
+
|
|
|
+- (void)setDismissTimerWithInterval:(NSTimeInterval)interval;
|
|
|
+{
|
|
|
+ [self.dismissTimer invalidate];
|
|
|
+ self.dismissTimer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:interval]
|
|
|
+ interval:0 target:self selector:@selector(dismiss:) userInfo:nil repeats:NO];
|
|
|
+ [[NSRunLoop currentRunLoop] addTimer:self.dismissTimer forMode:NSRunLoopCommonModes];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)dismiss:(NSTimer*)timer;
|
|
|
+{
|
|
|
+ [self dismissAnimated:YES];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)dismissAnimated:(BOOL)animated;
|
|
|
+{
|
|
|
+ [self.dismissTimer invalidate];
|
|
|
+ self.dismissTimer = nil;
|
|
|
+
|
|
|
+ // check animation type
|
|
|
+ BOOL animationsEnabled = (self.activeStyle.animationType != JDStatusBarAnimationTypeNone);
|
|
|
+ animated &= animationsEnabled;
|
|
|
+
|
|
|
+ dispatch_block_t animation = ^{
|
|
|
+ if (self.activeStyle.animationType == JDStatusBarAnimationTypeFade) {
|
|
|
+ self.topBar.alpha = 0.0;
|
|
|
+ } else {
|
|
|
+ self.topBar.transform = CGAffineTransformMakeTranslation(0, -self.topBar.frame.size.height);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ void(^complete)(BOOL) = ^(BOOL finished) {
|
|
|
+ [self.overlayWindow removeFromSuperview];
|
|
|
+ [self.overlayWindow setHidden:YES];
|
|
|
+ _overlayWindow.rootViewController = nil;
|
|
|
+ _overlayWindow = nil;
|
|
|
+ _progressView = nil;
|
|
|
+ _topBar = nil;
|
|
|
+ };
|
|
|
+
|
|
|
+ if (animated) {
|
|
|
+ // animate out
|
|
|
+ [UIView animateWithDuration:0.4 animations:animation completion:complete];
|
|
|
+ } else {
|
|
|
+ animation();
|
|
|
+ complete(YES);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark Bounce Animation
|
|
|
+
|
|
|
+- (void)animateInWithBounceAnimation;
|
|
|
+{
|
|
|
+ //don't animate in, if topBar is already fully visible
|
|
|
+ if (self.topBar.frame.origin.y >= 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // easing function (based on github.com/robb/RBBAnimation)
|
|
|
+ CGFloat(^RBBEasingFunctionEaseOutBounce)(CGFloat) = ^CGFloat(CGFloat t) {
|
|
|
+ if (t < 4.0 / 11.0) return pow(11.0 / 4.0, 2) * pow(t, 2);
|
|
|
+ if (t < 8.0 / 11.0) return 3.0 / 4.0 + pow(11.0 / 4.0, 2) * pow(t - 6.0 / 11.0, 2);
|
|
|
+ if (t < 10.0 / 11.0) return 15.0 /16.0 + pow(11.0 / 4.0, 2) * pow(t - 9.0 / 11.0, 2);
|
|
|
+ return 63.0 / 64.0 + pow(11.0 / 4.0, 2) * pow(t - 21.0 / 22.0, 2);
|
|
|
+ };
|
|
|
+
|
|
|
+ // create values
|
|
|
+ int fromCenterY=-20, toCenterY=0, animationSteps=100;
|
|
|
+ NSMutableArray *values = [NSMutableArray arrayWithCapacity:animationSteps];
|
|
|
+ for (int t = 1; t<=animationSteps; t++) {
|
|
|
+ float easedTime = RBBEasingFunctionEaseOutBounce((t*1.0)/animationSteps);
|
|
|
+ float easedValue = fromCenterY + easedTime * (toCenterY-fromCenterY);
|
|
|
+ [values addObject:[NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0, easedValue, 0)]];
|
|
|
+ }
|
|
|
+
|
|
|
+ // build animation
|
|
|
+ CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
|
|
|
+ animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
|
|
|
+ animation.duration = 0.66;
|
|
|
+ animation.values = values;
|
|
|
+ animation.removedOnCompletion = NO;
|
|
|
+ animation.fillMode = kCAFillModeForwards;
|
|
|
+ animation.delegate = self;
|
|
|
+ [self.topBar.layer setValue:@(toCenterY) forKeyPath:animation.keyPath];
|
|
|
+ [self.topBar.layer addAnimation:animation forKey:@"JDBounceAnimation"];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;
|
|
|
+{
|
|
|
+ self.topBar.transform = CGAffineTransformIdentity;
|
|
|
+ [self.topBar.layer removeAllAnimations];
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark Progress & Activity
|
|
|
+
|
|
|
+- (void)setProgress:(CGFloat)progress;
|
|
|
+{
|
|
|
+ if (_topBar == nil) return;
|
|
|
+
|
|
|
+ // trim progress
|
|
|
+ _progress = MIN(1.0, MAX(0.0,progress));
|
|
|
+
|
|
|
+ if (_progress == 0.0) {
|
|
|
+ _progressView.frame = CGRectZero;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // update superview
|
|
|
+ if (self.activeStyle.progressBarPosition == JDStatusBarProgressBarPositionBelow ||
|
|
|
+ self.activeStyle.progressBarPosition == JDStatusBarProgressBarPositionNavBar) {
|
|
|
+ [self.topBar.superview addSubview:self.progressView];
|
|
|
+ } else {
|
|
|
+ [self.topBar insertSubview:self.progressView belowSubview:self.topBar.textLabel];
|
|
|
+ }
|
|
|
+
|
|
|
+ // calculate progressView frame
|
|
|
+ CGRect frame = self.topBar.bounds;
|
|
|
+ CGFloat height = MIN(frame.size.height,MAX(0.5, self.activeStyle.progressBarHeight));
|
|
|
+ if (height == 20.0 && frame.size.height > height) height = frame.size.height;
|
|
|
+ frame.size.height = height;
|
|
|
+ frame.size.width = round((frame.size.width - 2 * self.activeStyle.progressBarHorizontalInsets) * progress);
|
|
|
+ frame.origin.x = self.activeStyle.progressBarHorizontalInsets;
|
|
|
+
|
|
|
+ // apply y-position from active style
|
|
|
+ CGFloat barHeight = self.topBar.bounds.size.height;
|
|
|
+ if (self.activeStyle.progressBarPosition == JDStatusBarProgressBarPositionBottom) {
|
|
|
+ frame.origin.y = barHeight - height;
|
|
|
+ } else if(self.activeStyle.progressBarPosition == JDStatusBarProgressBarPositionCenter) {
|
|
|
+ frame.origin.y = round((barHeight - height)/2.0);
|
|
|
+ } else if(self.activeStyle.progressBarPosition == JDStatusBarProgressBarPositionTop) {
|
|
|
+ frame.origin.y = 0.0;
|
|
|
+ } else if(self.activeStyle.progressBarPosition == JDStatusBarProgressBarPositionBelow) {
|
|
|
+ frame.origin.y = barHeight;
|
|
|
+ } else if(self.activeStyle.progressBarPosition == JDStatusBarProgressBarPositionNavBar) {
|
|
|
+ CGFloat navBarHeight = 44.0;
|
|
|
+ if (([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) &&
|
|
|
+ UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
|
|
|
+ navBarHeight = 32.0;
|
|
|
+ }
|
|
|
+ frame.origin.y = barHeight + navBarHeight;
|
|
|
+ }
|
|
|
+
|
|
|
+ // apply color from active style
|
|
|
+ self.progressView.backgroundColor = self.activeStyle.progressBarColor;
|
|
|
+
|
|
|
+ // apply corner radius
|
|
|
+ self.progressView.layer.cornerRadius = self.activeStyle.progressBarCornerRadius;
|
|
|
+
|
|
|
+ // update progressView frame
|
|
|
+ BOOL animated = !CGRectEqualToRect(self.progressView.frame, CGRectZero);
|
|
|
+ [UIView animateWithDuration:animated ? 0.05 : 0.0 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
|
|
|
+ self.progressView.frame = frame;
|
|
|
+ } completion:nil];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)showActivityIndicator:(BOOL)show
|
|
|
+ indicatorStyle:(UIActivityIndicatorViewStyle)style;
|
|
|
+{
|
|
|
+ if (_topBar == nil) return;
|
|
|
+
|
|
|
+ if (show) {
|
|
|
+ [self.topBar.activityIndicatorView startAnimating];
|
|
|
+ self.topBar.activityIndicatorView.activityIndicatorViewStyle = style;
|
|
|
+ } else {
|
|
|
+ [self.topBar.activityIndicatorView stopAnimating];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark State
|
|
|
+
|
|
|
+- (BOOL)isVisible;
|
|
|
+{
|
|
|
+ return (_topBar != nil);
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark Lazy views
|
|
|
+
|
|
|
+- (UIWindow *)overlayWindow;
|
|
|
+{
|
|
|
+ if(_overlayWindow == nil) {
|
|
|
+ _overlayWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
|
|
+ _overlayWindow.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
|
+ _overlayWindow.backgroundColor = [UIColor clearColor];
|
|
|
+ _overlayWindow.userInteractionEnabled = NO;
|
|
|
+ _overlayWindow.windowLevel = UIWindowLevelStatusBar;
|
|
|
+ _overlayWindow.rootViewController = [[JDStatusBarNotificationViewController alloc] init];
|
|
|
+ _overlayWindow.rootViewController.view.backgroundColor = [UIColor clearColor];
|
|
|
+#if __IPHONE_OS_VERSION_MIN_REQUIRED < 70000 // only when deployment target is < ios7
|
|
|
+ _overlayWindow.rootViewController.wantsFullScreenLayout = YES;
|
|
|
+#endif
|
|
|
+ [self updateWindowTransform];
|
|
|
+ [self updateTopBarFrameWithStatusBarFrame:[[UIApplication sharedApplication] statusBarFrame]];
|
|
|
+ }
|
|
|
+ return _overlayWindow;
|
|
|
+}
|
|
|
+
|
|
|
+- (JDStatusBarView*)topBar;
|
|
|
+{
|
|
|
+ if(_topBar == nil) {
|
|
|
+ _topBar = [[JDStatusBarView alloc] init];
|
|
|
+ [self.overlayWindow.rootViewController.view addSubview:_topBar];
|
|
|
+
|
|
|
+ JDStatusBarStyle *style = self.activeStyle ?: self.defaultStyle;
|
|
|
+ if (style.animationType != JDStatusBarAnimationTypeFade) {
|
|
|
+ self.topBar.transform = CGAffineTransformMakeTranslation(0, -self.topBar.frame.size.height);
|
|
|
+ } else {
|
|
|
+ self.topBar.alpha = 0.0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return _topBar;
|
|
|
+}
|
|
|
+
|
|
|
+- (UIView *)progressView;
|
|
|
+{
|
|
|
+ if (_progressView == nil) {
|
|
|
+ _progressView = [[UIView alloc] initWithFrame:CGRectZero];
|
|
|
+ }
|
|
|
+ return _progressView;
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark Rotation
|
|
|
+
|
|
|
+- (void)updateWindowTransform;
|
|
|
+{
|
|
|
+ UIWindow *window = [[UIApplication sharedApplication]
|
|
|
+ mainApplicationWindowIgnoringWindow:self.overlayWindow];
|
|
|
+ _overlayWindow.transform = window.transform;
|
|
|
+ _overlayWindow.frame = window.frame;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)updateTopBarFrameWithStatusBarFrame:(CGRect)rect;
|
|
|
+{
|
|
|
+ CGFloat width = MAX(rect.size.width, rect.size.height);
|
|
|
+ CGFloat height = MIN(rect.size.width, rect.size.height);
|
|
|
+
|
|
|
+ // on ios7 fix position, if statusBar has double height
|
|
|
+ CGFloat yPos = 0;
|
|
|
+ if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0 && height > 20.0) {
|
|
|
+ yPos = -height/2.0;
|
|
|
+ }
|
|
|
+
|
|
|
+ _topBar.frame = CGRectMake(0, yPos, width, height);
|
|
|
+}
|
|
|
+
|
|
|
+- (void)willChangeStatusBarFrame:(NSNotification*)notification;
|
|
|
+{
|
|
|
+ CGRect newBarFrame = [notification.userInfo[UIApplicationStatusBarFrameUserInfoKey] CGRectValue];
|
|
|
+ NSTimeInterval duration = [[UIApplication sharedApplication] statusBarOrientationAnimationDuration];
|
|
|
+
|
|
|
+ // update window & statusbar
|
|
|
+ void(^updateBlock)() = ^{
|
|
|
+ [self updateWindowTransform];
|
|
|
+ [self updateTopBarFrameWithStatusBarFrame:newBarFrame];
|
|
|
+ self.progress = self.progress; // // relayout progress bar
|
|
|
+ };
|
|
|
+
|
|
|
+ [UIView animateWithDuration:duration animations:^{
|
|
|
+ updateBlock();
|
|
|
+ } completion:^(BOOL finished) {
|
|
|
+ // this hack fixes a broken frame after the rotation (#35)
|
|
|
+ // but rotation animation is still broken
|
|
|
+ updateBlock();
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+@end
|
|
|
+
|
|
|
+// A custom view controller, so the statusBarStyle & rotation behaviour is correct
|
|
|
+@implementation JDStatusBarNotificationViewController
|
|
|
+
|
|
|
+// rotation
|
|
|
+
|
|
|
+- (UIViewController*)mainController
|
|
|
+{
|
|
|
+ UIWindow *mainAppWindow = [[UIApplication sharedApplication] mainApplicationWindowIgnoringWindow:self.view.window];
|
|
|
+ UIViewController *topController = mainAppWindow.rootViewController;
|
|
|
+
|
|
|
+ while(topController.presentedViewController) {
|
|
|
+ topController = topController.presentedViewController;
|
|
|
+ }
|
|
|
+
|
|
|
+ return topController;
|
|
|
+}
|
|
|
+
|
|
|
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
|
|
|
+ return [[self mainController] shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
|
|
|
+}
|
|
|
+
|
|
|
+- (BOOL)shouldAutorotate {
|
|
|
+ return [[self mainController] shouldAutorotate];
|
|
|
+}
|
|
|
+
|
|
|
+#if __IPHONE_OS_VERSION_MAX_ALLOWED < 90000
|
|
|
+- (NSUInteger)supportedInterfaceOrientations {
|
|
|
+#else
|
|
|
+ - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
|
|
|
+#endif
|
|
|
+ return [[self mainController] supportedInterfaceOrientations];
|
|
|
+ }
|
|
|
+
|
|
|
+ - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
|
|
|
+ return [[self mainController] preferredInterfaceOrientationForPresentation];
|
|
|
+ }
|
|
|
+
|
|
|
+ // statusbar
|
|
|
+
|
|
|
+ static BOOL JDUIViewControllerBasedStatusBarAppearanceEnabled() {
|
|
|
+ static BOOL enabled = NO;
|
|
|
+ static dispatch_once_t onceToken;
|
|
|
+
|
|
|
+ dispatch_once(&onceToken, ^{
|
|
|
+ enabled = [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"UIViewControllerBasedStatusBarAppearance"] boolValue];
|
|
|
+ });
|
|
|
+
|
|
|
+ return enabled;
|
|
|
+ }
|
|
|
+
|
|
|
+ - (UIStatusBarStyle)preferredStatusBarStyle {
|
|
|
+ if(JDUIViewControllerBasedStatusBarAppearanceEnabled()) {
|
|
|
+ return [[self mainController] preferredStatusBarStyle];
|
|
|
+ }
|
|
|
+
|
|
|
+ return [[UIApplication sharedApplication] statusBarStyle];
|
|
|
+ }
|
|
|
+
|
|
|
+ - (BOOL)prefersStatusBarHidden {
|
|
|
+ return NO;
|
|
|
+ }
|
|
|
+
|
|
|
+ - (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
|
|
|
+ if(JDUIViewControllerBasedStatusBarAppearanceEnabled()) {
|
|
|
+ return [[self mainController] preferredStatusBarUpdateAnimation];
|
|
|
+ }
|
|
|
+ return [super preferredStatusBarUpdateAnimation];
|
|
|
+ }
|
|
|
+
|
|
|
+ @end
|
|
|
+
|
|
|
+ @implementation UIApplication (mainWindow)
|
|
|
+ // we don't want the keyWindow, since it could be our own window
|
|
|
+ - (UIWindow*)mainApplicationWindowIgnoringWindow:(UIWindow *)ignoringWindow {
|
|
|
+ for (UIWindow *window in [[UIApplication sharedApplication] windows]) {
|
|
|
+ if (!window.hidden && window != ignoringWindow) {
|
|
|
+ return window;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil;
|
|
|
+ }
|
|
|
+ @end
|