JDStatusBarNotification.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. //
  2. // JDStatusBarNotification.m
  3. //
  4. // Based on KGStatusBar by Kevin Gibbon
  5. //
  6. // Created by Markus Emrich on 10/28/13.
  7. // Copyright 2013 Markus Emrich. All rights reserved.
  8. //
  9. #import <QuartzCore/QuartzCore.h>
  10. #import "JDStatusBarNotification.h"
  11. @interface JDStatusBarStyle (Hidden)
  12. + (NSArray*)allDefaultStyleIdentifier;
  13. + (JDStatusBarStyle*)defaultStyleWithName:(NSString*)styleName;
  14. @end
  15. @interface JDStatusBarNotificationViewController : UIViewController
  16. @end
  17. @interface UIApplication (mainWindow)
  18. - (UIWindow*)mainApplicationWindowIgnoringWindow:(UIWindow*)ignoringWindow;
  19. @end
  20. @interface JDStatusBarNotification () <CAAnimationDelegate>
  21. @property (nonatomic, strong, readonly) UIWindow *overlayWindow;
  22. @property (nonatomic, strong, readonly) UIView *progressView;
  23. @property (nonatomic, strong, readonly) JDStatusBarView *topBar;
  24. @property (nonatomic, strong) NSTimer *dismissTimer;
  25. @property (nonatomic, assign) CGFloat progress;
  26. @property (nonatomic, weak) JDStatusBarStyle *activeStyle;
  27. @property (nonatomic, strong) JDStatusBarStyle *defaultStyle;
  28. @property (nonatomic, strong) NSMutableDictionary *userStyles;
  29. @end
  30. @implementation JDStatusBarNotification
  31. @synthesize overlayWindow = _overlayWindow;
  32. @synthesize progressView = _progressView;
  33. @synthesize topBar = _topBar;
  34. #pragma mark Class methods
  35. + (JDStatusBarNotification*)sharedInstance {
  36. static dispatch_once_t once;
  37. static JDStatusBarNotification *sharedInstance;
  38. dispatch_once(&once, ^ {
  39. sharedInstance = [[self alloc] init];
  40. });
  41. return sharedInstance;
  42. }
  43. + (UIView*)showWithStatus:(NSString *)status;
  44. {
  45. return [[self sharedInstance] showWithStatus:status
  46. styleName:nil];
  47. }
  48. + (UIView*)showWithStatus:(NSString *)status
  49. styleName:(NSString*)styleName;
  50. {
  51. return [[self sharedInstance] showWithStatus:status
  52. styleName:styleName];
  53. }
  54. + (UIView*)showWithStatus:(NSString *)status
  55. dismissAfter:(NSTimeInterval)timeInterval;
  56. {
  57. UIView *view = [[self sharedInstance] showWithStatus:status
  58. styleName:nil];
  59. [self dismissAfter:timeInterval];
  60. return view;
  61. }
  62. + (UIView*)showWithStatus:(NSString *)status
  63. dismissAfter:(NSTimeInterval)timeInterval
  64. styleName:(NSString*)styleName;
  65. {
  66. UIView *view = [[self sharedInstance] showWithStatus:status
  67. styleName:styleName];
  68. [self dismissAfter:timeInterval];
  69. return view;
  70. }
  71. + (void)dismiss;
  72. {
  73. [self dismissAnimated:YES];
  74. }
  75. + (void)dismissAnimated:(BOOL)animated;
  76. {
  77. [[self sharedInstance] dismissAnimated:animated];
  78. }
  79. + (void)dismissAfter:(NSTimeInterval)delay;
  80. {
  81. [[self sharedInstance] setDismissTimerWithInterval:delay];
  82. }
  83. + (void)setDefaultStyle:(JDPrepareStyleBlock)prepareBlock;
  84. {
  85. NSAssert(prepareBlock != nil, @"No prepareBlock provided");
  86. JDStatusBarStyle *style = [[self sharedInstance].defaultStyle copy];
  87. [self sharedInstance].defaultStyle = prepareBlock(style);
  88. }
  89. + (NSString*)addStyleNamed:(NSString*)identifier
  90. prepare:(JDPrepareStyleBlock)prepareBlock;
  91. {
  92. return [[self sharedInstance] addStyleNamed:identifier
  93. prepare:prepareBlock];
  94. }
  95. + (void)showProgress:(CGFloat)progress;
  96. {
  97. [[self sharedInstance] setProgress:progress];
  98. }
  99. + (void)showActivityIndicator:(BOOL)show indicatorStyle:(UIActivityIndicatorViewStyle)style;
  100. {
  101. [[self sharedInstance] showActivityIndicator:show indicatorStyle:style];
  102. }
  103. + (BOOL)isVisible;
  104. {
  105. return [[self sharedInstance] isVisible];
  106. }
  107. #pragma mark Implementation
  108. - (id)init
  109. {
  110. if ((self = [super init]))
  111. {
  112. // set defaults
  113. [self setupDefaultStyles];
  114. // register for orientation changes
  115. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willChangeStatusBarFrame:)
  116. name:UIApplicationWillChangeStatusBarFrameNotification object:nil];
  117. }
  118. return self;
  119. }
  120. - (void)dealloc
  121. {
  122. [[NSNotificationCenter defaultCenter] removeObserver:self];
  123. }
  124. #pragma mark Custom styles
  125. - (void)setupDefaultStyles;
  126. {
  127. self.defaultStyle = [JDStatusBarStyle defaultStyleWithName:JDStatusBarStyleDefault];
  128. self.userStyles = [NSMutableDictionary dictionary];
  129. for (NSString *styleName in [JDStatusBarStyle allDefaultStyleIdentifier]) {
  130. [self.userStyles setObject:[JDStatusBarStyle defaultStyleWithName:styleName] forKey:styleName];
  131. }
  132. }
  133. - (NSString*)addStyleNamed:(NSString*)identifier
  134. prepare:(JDPrepareStyleBlock)prepareBlock;
  135. {
  136. NSAssert(identifier != nil, @"No identifier provided");
  137. NSAssert(prepareBlock != nil, @"No prepareBlock provided");
  138. JDStatusBarStyle *style = [self.defaultStyle copy];
  139. [self.userStyles setObject:prepareBlock(style) forKey:identifier];
  140. return identifier;
  141. }
  142. #pragma mark Presentation
  143. - (UIView*)showWithStatus:(NSString *)status
  144. styleName:(NSString*)styleName;
  145. {
  146. JDStatusBarStyle *style = nil;
  147. if (styleName != nil) {
  148. style = self.userStyles[styleName];
  149. }
  150. if (style == nil) style = self.defaultStyle;
  151. return [self showWithStatus:status style:style];
  152. }
  153. - (UIView*)showWithStatus:(NSString *)status
  154. style:(JDStatusBarStyle*)style;
  155. {
  156. // first, check if status bar is visible at all
  157. if ([UIApplication sharedApplication].statusBarHidden) return nil;
  158. // prepare for new style
  159. if (style != self.activeStyle) {
  160. self.activeStyle = style;
  161. if (self.activeStyle.animationType == JDStatusBarAnimationTypeFade) {
  162. self.topBar.alpha = 0.0;
  163. self.topBar.transform = CGAffineTransformIdentity;
  164. } else {
  165. self.topBar.alpha = 1.0;
  166. self.topBar.transform = CGAffineTransformMakeTranslation(0, -self.topBar.frame.size.height);
  167. }
  168. }
  169. // cancel previous dismissing & remove animations
  170. [[NSRunLoop currentRunLoop] cancelPerformSelector:@selector(dismiss) target:self argument:nil];
  171. [self.topBar.layer removeAllAnimations];
  172. // create & show window
  173. [self.overlayWindow setHidden:NO];
  174. // update style
  175. self.topBar.backgroundColor = style.barColor;
  176. self.topBar.textVerticalPositionAdjustment = style.textVerticalPositionAdjustment;
  177. UILabel *textLabel = self.topBar.textLabel;
  178. textLabel.textColor = style.textColor;
  179. textLabel.font = style.font;
  180. textLabel.accessibilityLabel = status;
  181. textLabel.text = status;
  182. if (style.textShadow) {
  183. textLabel.shadowColor = style.textShadow.shadowColor;
  184. textLabel.shadowOffset = style.textShadow.shadowOffset;
  185. } else {
  186. textLabel.shadowColor = nil;
  187. textLabel.shadowOffset = CGSizeZero;
  188. }
  189. // reset progress & activity
  190. self.progress = 0.0;
  191. [self showActivityIndicator:NO indicatorStyle:0];
  192. // animate in
  193. BOOL animationsEnabled = (style.animationType != JDStatusBarAnimationTypeNone);
  194. if (animationsEnabled && style.animationType == JDStatusBarAnimationTypeBounce) {
  195. [self animateInWithBounceAnimation];
  196. } else {
  197. [UIView animateWithDuration:(animationsEnabled ? 0.4 : 0.0) animations:^{
  198. self.topBar.alpha = 1.0;
  199. self.topBar.transform = CGAffineTransformIdentity;
  200. }];
  201. }
  202. return self.topBar;
  203. }
  204. #pragma mark Dismissal
  205. - (void)setDismissTimerWithInterval:(NSTimeInterval)interval;
  206. {
  207. [self.dismissTimer invalidate];
  208. self.dismissTimer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:interval]
  209. interval:0 target:self selector:@selector(dismiss:) userInfo:nil repeats:NO];
  210. [[NSRunLoop currentRunLoop] addTimer:self.dismissTimer forMode:NSRunLoopCommonModes];
  211. }
  212. - (void)dismiss:(NSTimer*)timer;
  213. {
  214. [self dismissAnimated:YES];
  215. }
  216. - (void)dismissAnimated:(BOOL)animated;
  217. {
  218. [self.dismissTimer invalidate];
  219. self.dismissTimer = nil;
  220. // check animation type
  221. BOOL animationsEnabled = (self.activeStyle.animationType != JDStatusBarAnimationTypeNone);
  222. animated &= animationsEnabled;
  223. dispatch_block_t animation = ^{
  224. if (self.activeStyle.animationType == JDStatusBarAnimationTypeFade) {
  225. self.topBar.alpha = 0.0;
  226. } else {
  227. self.topBar.transform = CGAffineTransformMakeTranslation(0, -self.topBar.frame.size.height);
  228. }
  229. };
  230. void(^complete)(BOOL) = ^(BOOL finished) {
  231. [self.overlayWindow removeFromSuperview];
  232. [self.overlayWindow setHidden:YES];
  233. _overlayWindow.rootViewController = nil;
  234. _overlayWindow = nil;
  235. _progressView = nil;
  236. _topBar = nil;
  237. };
  238. if (animated) {
  239. // animate out
  240. [UIView animateWithDuration:0.4 animations:animation completion:complete];
  241. } else {
  242. animation();
  243. complete(YES);
  244. }
  245. }
  246. #pragma mark Bounce Animation
  247. - (void)animateInWithBounceAnimation;
  248. {
  249. //don't animate in, if topBar is already fully visible
  250. if (self.topBar.frame.origin.y >= 0) {
  251. return;
  252. }
  253. // easing function (based on github.com/robb/RBBAnimation)
  254. CGFloat(^RBBEasingFunctionEaseOutBounce)(CGFloat) = ^CGFloat(CGFloat t) {
  255. if (t < 4.0 / 11.0) return pow(11.0 / 4.0, 2) * pow(t, 2);
  256. if (t < 8.0 / 11.0) return 3.0 / 4.0 + pow(11.0 / 4.0, 2) * pow(t - 6.0 / 11.0, 2);
  257. if (t < 10.0 / 11.0) return 15.0 /16.0 + pow(11.0 / 4.0, 2) * pow(t - 9.0 / 11.0, 2);
  258. return 63.0 / 64.0 + pow(11.0 / 4.0, 2) * pow(t - 21.0 / 22.0, 2);
  259. };
  260. // create values
  261. int fromCenterY=-20, toCenterY=0, animationSteps=100;
  262. NSMutableArray *values = [NSMutableArray arrayWithCapacity:animationSteps];
  263. for (int t = 1; t<=animationSteps; t++) {
  264. float easedTime = RBBEasingFunctionEaseOutBounce((t*1.0)/animationSteps);
  265. float easedValue = fromCenterY + easedTime * (toCenterY-fromCenterY);
  266. [values addObject:[NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0, easedValue, 0)]];
  267. }
  268. // build animation
  269. CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
  270. animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
  271. animation.duration = 0.66;
  272. animation.values = values;
  273. animation.removedOnCompletion = NO;
  274. animation.fillMode = kCAFillModeForwards;
  275. animation.delegate = self;
  276. [self.topBar.layer setValue:@(toCenterY) forKeyPath:animation.keyPath];
  277. [self.topBar.layer addAnimation:animation forKey:@"JDBounceAnimation"];
  278. }
  279. - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;
  280. {
  281. self.topBar.transform = CGAffineTransformIdentity;
  282. [self.topBar.layer removeAllAnimations];
  283. }
  284. #pragma mark Progress & Activity
  285. - (void)setProgress:(CGFloat)progress;
  286. {
  287. if (_topBar == nil) return;
  288. // trim progress
  289. _progress = MIN(1.0, MAX(0.0,progress));
  290. if (_progress == 0.0) {
  291. _progressView.frame = CGRectZero;
  292. return;
  293. }
  294. // update superview
  295. if (self.activeStyle.progressBarPosition == JDStatusBarProgressBarPositionBelow ||
  296. self.activeStyle.progressBarPosition == JDStatusBarProgressBarPositionNavBar) {
  297. [self.topBar.superview addSubview:self.progressView];
  298. } else {
  299. [self.topBar insertSubview:self.progressView belowSubview:self.topBar.textLabel];
  300. }
  301. // calculate progressView frame
  302. CGRect frame = self.topBar.bounds;
  303. CGFloat height = MIN(frame.size.height,MAX(0.5, self.activeStyle.progressBarHeight));
  304. if (height == 20.0 && frame.size.height > height) height = frame.size.height;
  305. frame.size.height = height;
  306. frame.size.width = round((frame.size.width - 2 * self.activeStyle.progressBarHorizontalInsets) * progress);
  307. frame.origin.x = self.activeStyle.progressBarHorizontalInsets;
  308. // apply y-position from active style
  309. CGFloat barHeight = self.topBar.bounds.size.height;
  310. if (self.activeStyle.progressBarPosition == JDStatusBarProgressBarPositionBottom) {
  311. frame.origin.y = barHeight - height;
  312. } else if(self.activeStyle.progressBarPosition == JDStatusBarProgressBarPositionCenter) {
  313. frame.origin.y = round((barHeight - height)/2.0);
  314. } else if(self.activeStyle.progressBarPosition == JDStatusBarProgressBarPositionTop) {
  315. frame.origin.y = 0.0;
  316. } else if(self.activeStyle.progressBarPosition == JDStatusBarProgressBarPositionBelow) {
  317. frame.origin.y = barHeight;
  318. } else if(self.activeStyle.progressBarPosition == JDStatusBarProgressBarPositionNavBar) {
  319. CGFloat navBarHeight = 44.0;
  320. if (([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) &&
  321. UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
  322. navBarHeight = 32.0;
  323. }
  324. frame.origin.y = barHeight + navBarHeight;
  325. }
  326. // apply color from active style
  327. self.progressView.backgroundColor = self.activeStyle.progressBarColor;
  328. // apply corner radius
  329. self.progressView.layer.cornerRadius = self.activeStyle.progressBarCornerRadius;
  330. // update progressView frame
  331. BOOL animated = !CGRectEqualToRect(self.progressView.frame, CGRectZero);
  332. [UIView animateWithDuration:animated ? 0.05 : 0.0 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
  333. self.progressView.frame = frame;
  334. } completion:nil];
  335. }
  336. - (void)showActivityIndicator:(BOOL)show
  337. indicatorStyle:(UIActivityIndicatorViewStyle)style;
  338. {
  339. if (_topBar == nil) return;
  340. if (show) {
  341. [self.topBar.activityIndicatorView startAnimating];
  342. self.topBar.activityIndicatorView.activityIndicatorViewStyle = style;
  343. } else {
  344. [self.topBar.activityIndicatorView stopAnimating];
  345. }
  346. }
  347. #pragma mark State
  348. - (BOOL)isVisible;
  349. {
  350. return (_topBar != nil);
  351. }
  352. #pragma mark Lazy views
  353. - (UIWindow *)overlayWindow;
  354. {
  355. if(_overlayWindow == nil) {
  356. _overlayWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  357. _overlayWindow.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  358. _overlayWindow.backgroundColor = [UIColor clearColor];
  359. _overlayWindow.userInteractionEnabled = NO;
  360. _overlayWindow.windowLevel = UIWindowLevelStatusBar;
  361. _overlayWindow.rootViewController = [[JDStatusBarNotificationViewController alloc] init];
  362. _overlayWindow.rootViewController.view.backgroundColor = [UIColor clearColor];
  363. #if __IPHONE_OS_VERSION_MIN_REQUIRED < 70000 // only when deployment target is < ios7
  364. _overlayWindow.rootViewController.wantsFullScreenLayout = YES;
  365. #endif
  366. [self updateWindowTransform];
  367. [self updateTopBarFrameWithStatusBarFrame:[[UIApplication sharedApplication] statusBarFrame]];
  368. }
  369. return _overlayWindow;
  370. }
  371. - (JDStatusBarView*)topBar;
  372. {
  373. if(_topBar == nil) {
  374. _topBar = [[JDStatusBarView alloc] init];
  375. [self.overlayWindow.rootViewController.view addSubview:_topBar];
  376. JDStatusBarStyle *style = self.activeStyle ?: self.defaultStyle;
  377. if (style.animationType != JDStatusBarAnimationTypeFade) {
  378. self.topBar.transform = CGAffineTransformMakeTranslation(0, -self.topBar.frame.size.height);
  379. } else {
  380. self.topBar.alpha = 0.0;
  381. }
  382. }
  383. return _topBar;
  384. }
  385. - (UIView *)progressView;
  386. {
  387. if (_progressView == nil) {
  388. _progressView = [[UIView alloc] initWithFrame:CGRectZero];
  389. }
  390. return _progressView;
  391. }
  392. #pragma mark Rotation
  393. - (void)updateWindowTransform;
  394. {
  395. UIWindow *window = [[UIApplication sharedApplication]
  396. mainApplicationWindowIgnoringWindow:self.overlayWindow];
  397. _overlayWindow.transform = window.transform;
  398. _overlayWindow.frame = window.frame;
  399. }
  400. - (void)updateTopBarFrameWithStatusBarFrame:(CGRect)rect;
  401. {
  402. CGFloat width = MAX(rect.size.width, rect.size.height);
  403. CGFloat height = MIN(rect.size.width, rect.size.height);
  404. // on ios7 fix position, if statusBar has double height
  405. CGFloat yPos = 0;
  406. if ([JDStatusBarView isIphoneX]) {
  407. height = 64;
  408. } else {
  409. if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0 && height > 20.0) {
  410. yPos = -height/2.0;
  411. }
  412. }
  413. _topBar.frame = CGRectMake(0, yPos, width, height);
  414. }
  415. - (void)willChangeStatusBarFrame:(NSNotification*)notification;
  416. {
  417. CGRect newBarFrame = [notification.userInfo[UIApplicationStatusBarFrameUserInfoKey] CGRectValue];
  418. NSTimeInterval duration = [[UIApplication sharedApplication] statusBarOrientationAnimationDuration];
  419. // update window & statusbar
  420. void(^updateBlock)() = ^{
  421. [self updateWindowTransform];
  422. [self updateTopBarFrameWithStatusBarFrame:newBarFrame];
  423. self.progress = self.progress; // // relayout progress bar
  424. };
  425. [UIView animateWithDuration:duration animations:^{
  426. updateBlock();
  427. } completion:^(BOOL finished) {
  428. // this hack fixes a broken frame after the rotation (#35)
  429. // but rotation animation is still broken
  430. updateBlock();
  431. }];
  432. }
  433. @end
  434. // A custom view controller, so the statusBarStyle & rotation behaviour is correct
  435. @implementation JDStatusBarNotificationViewController
  436. // rotation
  437. - (UIViewController*)mainController
  438. {
  439. UIWindow *mainAppWindow = [[UIApplication sharedApplication] mainApplicationWindowIgnoringWindow:self.view.window];
  440. UIViewController *topController = mainAppWindow.rootViewController;
  441. while(topController.presentedViewController) {
  442. topController = topController.presentedViewController;
  443. }
  444. return topController;
  445. }
  446. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
  447. return [[self mainController] shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
  448. }
  449. - (BOOL)shouldAutorotate {
  450. return [[self mainController] shouldAutorotate];
  451. }
  452. #if __IPHONE_OS_VERSION_MAX_ALLOWED < 90000
  453. - (NSUInteger)supportedInterfaceOrientations {
  454. #else
  455. - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
  456. #endif
  457. return [[self mainController] supportedInterfaceOrientations];
  458. }
  459. - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
  460. return [[self mainController] preferredInterfaceOrientationForPresentation];
  461. }
  462. // statusbar
  463. static BOOL JDUIViewControllerBasedStatusBarAppearanceEnabled() {
  464. static BOOL enabled = NO;
  465. static dispatch_once_t onceToken;
  466. dispatch_once(&onceToken, ^{
  467. enabled = [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"UIViewControllerBasedStatusBarAppearance"] boolValue];
  468. });
  469. return enabled;
  470. }
  471. - (UIStatusBarStyle)preferredStatusBarStyle {
  472. if(JDUIViewControllerBasedStatusBarAppearanceEnabled()) {
  473. return [[self mainController] preferredStatusBarStyle];
  474. }
  475. return [[UIApplication sharedApplication] statusBarStyle];
  476. }
  477. - (BOOL)prefersStatusBarHidden {
  478. return NO;
  479. }
  480. - (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
  481. if(JDUIViewControllerBasedStatusBarAppearanceEnabled()) {
  482. return [[self mainController] preferredStatusBarUpdateAnimation];
  483. }
  484. return [super preferredStatusBarUpdateAnimation];
  485. }
  486. @end
  487. @implementation UIApplication (mainWindow)
  488. // we don't want the keyWindow, since it could be our own window
  489. - (UIWindow*)mainApplicationWindowIgnoringWindow:(UIWindow *)ignoringWindow {
  490. for (UIWindow *window in [[UIApplication sharedApplication] windows]) {
  491. if (!window.hidden && window != ignoringWindow) {
  492. return window;
  493. }
  494. }
  495. return nil;
  496. }
  497. @end