iOS:状态栏提示控件的实现原理

移动开发 iOS
现在很多流行的软件都加入了状态栏提示的功能,比如手机qq,微信等,今天我们就一起来看看状态栏提示控件的原理与实现。

现在很多流行的软件都加入了状态栏提示的功能,比如手机qq,微信等,今天我们就一起来看看状态栏提示控件的原理与实现。

[[113965]]

一、状态栏提示的实现原理

不知道大家看到状态栏提示控件,***感觉它是怎么实现的呢?

我们知道即使平时写的view是充满全屏的,也始终不会显示到 statusBar的上层的。也就是说statusBar应该是一个特殊的view,始终位于程序的topLevel,这就容易联想到UIKit中一个特 殊的view-----UIWindow。UIWindow有一个windowLevel的属性刚好能实现一直保持在上层的功能,于是方向就比较明确了。 我较早写的两篇博客UIWindowLevel详解以及关于UIWindow的一点儿思考中对windowLevel有过详细的介绍和验证。

确定了使用UIWindow来实现状态栏提示控件,好像问题就全部解决了,真的是这样吗?

如果你的程序仅仅支持portrait方向的话,那么最主要的功能就结束了,剩下的事情就是文本框布局和简单动画的实现了。但如果你的控件要支持其他三个方向的话,就还需要处理window的旋转。

iOS中完整的旋转流程如下: 设备检测到方向旋转->UIApplication收到旋转事件->通知window设备发生了旋转->window通知它的 rootViewController进行旋转->viewController会调整自身view的transform。观察发现自始至终 window本身的位置和方向是没有发生变化的,也就是说如果自己创建一个window用于展示提示,我们需要自己处理该window的旋转,根据不同的 方向调整window的位置和transform。

综上要实现状态栏提示控件我们需要做以下两件事:

1、创建一个UIWindow,指定它的frame为statusBar的frame,并且设置该window的windowLevel级别略高于statusBar的windowLevel。

2、注册系统的旋转通知,监测设备方向变化,根据当前设备的方向做出相应的调整。

整个过程中主要用到了UIWindow和transfrom的知识,这两部分知识我前面写的博客都有涉及,难点主要集中在自己旋转window这一块。

二、Window的旋转

UIKit通过UIWindow和UIViewContoller为我们提供了一套旋转支持的框架,在方向变化以后viewController中view的坐标系统就已经被转到正确的方向了,我们只需要简单的重新布局就可以了。我们现在是直接通过UIWindow实现状态栏提示控件,因此需要自己完成对该window进行旋转的操作。

我们知道对当前view设置的transform是针对它的父view的,window本身就是一种特殊的view。你可能会疑问window不就是***层的view,它还有父view吗?

答案是YES,不信的话你可以打印一下 window的superView看看。window默认方向是portrait方向,向下y坐标增加,向右x坐标增加。因此Portrait方向我们只 需要向普通的view那样布局就可以了,其它几个方向我们就需要用到transform和设置位置来搞定了。

下面我们看一看从Portrait方向转到landscapeRight方向的图示:

上图中从左到右展示了如何将初始位置(Portrait方向),旋转到目标位置(landscapeRight方向)的过程。

1、原始window位置位于屏幕最上方(与statusBar的位置一样)。

2、首先我们对这个window做顺时针90°旋转,变化后到达绿色绘制位置。

3、接着我们再修改window的center到屏幕最右边并且上下居中,达到红色虚线所示的位置。

4、***对该window的bound进行设置,使该window充满屏幕最右边的区域。注意这个时候由于window的transform已经转了90°,所以设置时width代表着高度,height代表这宽度。

下面是完整的处理旋转到四个方向的代码:

  1. - (void)updateOrientation:(NSNotification*)noti 
  2.     UIInterfaceOrientation newOrientation = [[noti.userInfo valueForKey:UIApplicationStatusBarOrientationUserInfoKey] integerValue]; 
  3.     NSLog(@"new orientation: %d", newOrientation); 
  4.      
  5.     switch (newOrientation) { 
  6.         case UIInterfaceOrientationPortrait: 
  7.         { 
  8.             self.transform = CGAffineTransformIdentity; 
  9.             self.frame = CGRectMake(0, 0, SCREEN_WIDTH, HEIGHT); 
  10.              
  11.             break
  12.         } 
  13.         case UIInterfaceOrientationPortraitUpsideDown: 
  14.         { 
  15.             // 先转矩阵,坐标系统落在屏幕有右下角,朝上是y,朝左是x 
  16.             self.transform = CGAffineTransformMakeRotation(M_PI); 
  17.             self.center = CGPointMake(SCREEN_WIDTH / 2, SCREEN_HEIGHT - HEIGHT / 2); 
  18.             self.bounds = CGRectMake(0, 0, SCREEN_WIDTH, HEIGHT); 
  19.              
  20.             break
  21.         } 
  22.         case UIInterfaceOrientationLandscapeLeft: 
  23.         { 
  24.             self.transform = CGAffineTransformMakeRotation(-M_PI_2); 
  25.             // 这个时候坐标轴已经转了90°,调整x相当于调节竖向调节,y相当于横向调节 
  26.             self.center = CGPointMake(HEIGHT / 2, [UIScreen mainScreen].bounds.size.height / 2); 
  27.             self.bounds = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.height, HEIGHT); 
  28.              
  29.             break
  30.         } 
  31.         case UIInterfaceOrientationLandscapeRight: 
  32.         { 
  33.             // 先设置transform,在设置位置和大小 
  34.             self.transform = CGAffineTransformMakeRotation(M_PI_2); 
  35.             self.center = CGPointMake(SCREEN_WIDTH - HEIGHT / 2, SCREEN_HEIGHT / 2); 
  36.             self.bounds = CGRectMake(0, 0, SCREEN_HEIGHT, HEIGHT); 
  37.              
  38.             break
  39.         } 
  40.         default
  41.             break
  42.     } 

三、状态栏提示控件源码

讲了那么多,说好的控件呢?

下面是完整的控件代码:

View Code SvStatusBarTipsWindow.h

  1. - (void)updateOrientation:(NSNotification*)noti 
  2.     UIInterfaceOrientation newOrientation = [[noti.userInfo valueForKey:UIApplicationStatusBarOrientationUserInfoKey] integerValue]; 
  3.     NSLog(@"new orientation: %d", newOrientation); 
  4.      
  5.     switch (newOrientation) { 
  6.         case UIInterfaceOrientationPortrait: 
  7.         { 
  8.             self.transform = CGAffineTransformIdentity; 
  9.             self.frame = CGRectMake(0, 0, SCREEN_WIDTH, HEIGHT); 
  10.              
  11.             break
  12.         } 
  13.         case UIInterfaceOrientationPortraitUpsideDown: 
  14.         { 
  15.             // 先转矩阵,坐标系统落在屏幕有右下角,朝上是y,朝左是x 
  16.             self.transform = CGAffineTransformMakeRotation(M_PI); 
  17.             self.center = CGPointMake(SCREEN_WIDTH / 2, SCREEN_HEIGHT - HEIGHT / 2); 
  18.             self.bounds = CGRectMake(0, 0, SCREEN_WIDTH, HEIGHT); 
  19.              
  20.             break
  21.         } 
  22.         case UIInterfaceOrientationLandscapeLeft: 
  23.         { 
  24.             self.transform = CGAffineTransformMakeRotation(-M_PI_2); 
  25.             // 这个时候坐标轴已经转了90°,调整x相当于调节竖向调节,y相当于横向调节 
  26.             self.center = CGPointMake(HEIGHT / 2, [UIScreen mainScreen].bounds.size.height / 2); 
  27.             self.bounds = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.height, HEIGHT); 
  28.              
  29.             break
  30.         } 
  31.         case UIInterfaceOrientationLandscapeRight: 
  32.         { 
  33.             // 先设置transform,在设置位置和大小 
  34.             self.transform = CGAffineTransformMakeRotation(M_PI_2); 
  35.             self.center = CGPointMake(SCREEN_WIDTH - HEIGHT / 2, SCREEN_HEIGHT / 2); 
  36.             self.bounds = CGRectMake(0, 0, SCREEN_HEIGHT, HEIGHT); 
  37.              
  38.             break
  39.         } 
  40.         default
  41.             break
  42.     } 
  43.  
  44. 复制代码 
  45.  
  46.    
  47.  
  48. 三、状态栏提示控件源码 
  49.  
  50.   
  51.  
  52. 讲了那么多,说好的控件呢? 
  53.  
  54.   下面是完整的控件代码: 
  55. 复制代码 
  56.  
  57. // 
  58. //  SvStatusBarTipsWindow.h 
  59. //  SvStatusBarTips 
  60. // 
  61. //  Created by  maple on 4/21/13. 
  62. //  Copyright (c) 2013 maple. All rights reserved. 
  63. // 
  64.  
  65. #import <UIKit/UIKit.h> 
  66.  
  67. @interface SvStatusBarTipsWindow : UIWindow 
  68.  
  69. /* 
  70.  * @brief get the singleton tips window 
  71.  */ 
  72. + (SvStatusBarTipsWindow*)shareTipsWindow; 
  73.  
  74. /* 
  75.  * @brief show tips message on statusBar 
  76.  */ 
  77. - (void)showTips:(NSString*)tips; 
  78.  
  79. /* 
  80.  * @brief show tips message on statusBar 
  81.  */ 
  82. - (void)showTips:(NSString*)tips hideAfterDelay:(NSInteger)seconds; 
  83.  
  84. /* 
  85.  * @brief show tips icon and message on statusBar 
  86.  */ 
  87. - (void)showTipsWithImage:(UIImage*)tipsIcon message:(NSString*)message; 
  88.  
  89. /* 
  90.  * @brief show tips icon and message on statusBar 
  91.  */ 
  92. - (void)showTipsWithImage:(UIImage*)tipsIcon message:(NSString*)message hideAfterDelay:(NSInteger)seconds; 
  93.  
  94.  
  95. /* 
  96.  * @brief hide tips window 
  97.  */ 
  98. - (void)hideTips; 
  99.  
  100. @end 

SvStatusBarTipsWindow.m

  1. // 
  2. //  SvStatusBarTipsWindow.m 
  3. //  SvStatusBarTips 
  4. // 
  5. //  Created by  maple on 4/21/13. 
  6. //  Copyright (c) 2013 maple. All rights reserved. 
  7. // 
  8.  
  9. #import "SvStatusBarTipsWindow.h" 
  10.  
  11. #define SCREEN_WIDTH  ([UIScreen mainScreen].bounds.size.width) 
  12. #define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height) 
  13.  
  14. #define HEIGHT   20 
  15.  
  16. #define ICON_WIDTH 20 
  17.  
  18. #define TIPMESSAGE_RIGHT_MARGIN 20 
  19. #define ICON_RIGHT_MARGIN       5 
  20.  
  21.  
  22. @interface SvStatusBarTipsWindow () { 
  23.     UILabel     *_tipsLbl; 
  24.     UIImageView *_tipsIcon; 
  25.      
  26.     NSTimer     *_hideTimer; 
  27.  
  28. @property (nonatomic, copy)     NSString *tipsMessage; 
  29.  
  30. @end 
  31.  
  32.  
  33.  
  34. @implementation SvStatusBarTipsWindow 
  35.  
  36. @synthesize tipsMessage; 
  37.  
  38.  
  39. static SvStatusBarTipsWindow *tipsWindow = nil; 
  40.  
  41. + (SvStatusBarTipsWindow*)shareTipsWindow 
  42.     if (!tipsWindow) { 
  43.         static dispatch_once_t onceToken; 
  44.         dispatch_once(&onceToken, ^{ 
  45.             tipsWindow = [[super allocWithZone:NULL] init]; 
  46.         }); 
  47.     } 
  48.      
  49.     return tipsWindow; 
  50.  
  51. + (id)copyWithZone:(NSZone *)zone 
  52.     return [[self shareTipsWindow] retain]; 
  53.  
  54. + (id)allocWithZone:(NSZone *)zone 
  55.     return [[self shareTipsWindow] retain]; 
  56.  
  57. - (id)retain 
  58.     return tipsWindow; 
  59.  
  60. - (oneway void)release 
  61.     return
  62.  
  63. - (id)autorelease 
  64.     return tipsWindow; 
  65.  
  66. - (id)init 
  67.     CGRect frame = [UIApplication sharedApplication].statusBarFrame; 
  68.     self = [super initWithFrame:frame]; 
  69.     if (self) { 
  70.          
  71.         self.autoresizingMask = UIViewAutoresizingFlexibleWidth; 
  72.         self.windowLevel = UIWindowLevelStatusBar + 10; 
  73.         self.backgroundColor = [UIColor clearColor]; 
  74.          
  75.         _tipsIcon = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, ICON_WIDTH, ICON_WIDTH)]; 
  76.         _tipsIcon.autoresizingMask = UIViewAutoresizingFlexibleHeight; 
  77.         _tipsIcon.backgroundColor = [UIColor redColor]; 
  78.         [self addSubview:_tipsIcon]; 
  79.         [_tipsIcon release]; 
  80.          
  81.         _tipsLbl = [[UILabel alloc] initWithFrame:self.bounds]; 
  82.         #ifdef NSTextAlignmentRight 
  83.             _tipsLbl.textAlignment = NSTextAlignmentLeft; 
  84.             _tipsLbl.lineBreakMode = NSLineBreakByTruncatingTail; 
  85.         #else 
  86.             _tipsLbl.textAlignment = 0; // means UITextAlignmentLeft 
  87.             _tipsLbl.lineBreakMode = 4; //UILineBreakModeTailTruncation; 
  88.         #endif 
  89.         _tipsLbl.textColor = [UIColor whiteColor]; 
  90.         _tipsLbl.font = [UIFont systemFontOfSize:12]; 
  91.         _tipsLbl.backgroundColor = [UIColor blackColor]; 
  92.         _tipsLbl.autoresizingMask = UIViewAutoresizingFlexibleHeight; 
  93.         [self addSubview:_tipsLbl]; 
  94.         [_tipsLbl release]; 
  95.          
  96.         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateOrientation:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil]; 
  97.     } 
  98.      
  99.     return self; 
  100.  
  101. - (void)dealloc 
  102.     [[NSNotificationCenter defaultCenter] removeObserver:self]; 
  103.  
  104.     [tipsMessage release]; 
  105.      
  106.     [super dealloc]; 
  107.  
  108. #pragma mark - 
  109. #pragma mark Notification Handle 
  110.  
  111. - (void)updateOrientation:(NSNotification*)noti 
  112.     UIInterfaceOrientation newOrientation = [[noti.userInfo valueForKey:UIApplicationStatusBarOrientationUserInfoKey] integerValue]; 
  113.     NSLog(@"new orientation: %d", newOrientation); 
  114.      
  115.     switch (newOrientation) { 
  116.         case UIInterfaceOrientationPortrait: 
  117.         { 
  118.             self.transform = CGAffineTransformIdentity; 
  119.             self.frame = CGRectMake(0, 0, SCREEN_WIDTH, HEIGHT); 
  120.              
  121.             break
  122.         } 
  123.         case UIInterfaceOrientationPortraitUpsideDown: 
  124.         { 
  125.             // 先转矩阵,坐标系统落在屏幕有右下角,朝上是y,朝左是x 
  126.             self.transform = CGAffineTransformMakeRotation(M_PI); 
  127.             self.center = CGPointMake(SCREEN_WIDTH / 2, SCREEN_HEIGHT - HEIGHT / 2); 
  128.             self.bounds = CGRectMake(0, 0, SCREEN_WIDTH, HEIGHT); 
  129.              
  130.             break
  131.         } 
  132.         case UIInterfaceOrientationLandscapeLeft: 
  133.         { 
  134.             self.transform = CGAffineTransformMakeRotation(-M_PI_2); 
  135.             // 这个时候坐标轴已经转了90°,调整x相当于调节竖向调节,y相当于横向调节 
  136.             self.center = CGPointMake(HEIGHT / 2, [UIScreen mainScreen].bounds.size.height / 2); 
  137.             self.bounds = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.height, HEIGHT); 
  138.              
  139.             break
  140.         } 
  141.         case UIInterfaceOrientationLandscapeRight: 
  142.         { 
  143.             // 先设置transform,在设置位置和大小 
  144.             self.transform = CGAffineTransformMakeRotation(M_PI_2); 
  145.             self.center = CGPointMake(SCREEN_WIDTH - HEIGHT / 2, SCREEN_HEIGHT / 2); 
  146.             self.bounds = CGRectMake(0, 0, SCREEN_HEIGHT, HEIGHT); 
  147.              
  148.             break
  149.         } 
  150.         default
  151.             break
  152.     } 
  153.  
  154. #pragma mark - 
  155. #pragma mark Tips Method 
  156.  
  157. /* 
  158.  * @brief show tips message on statusBar 
  159.  */ 
  160. - (void)showTips:(NSString*)tips 
  161.     if (_hideTimer) { 
  162.         [_hideTimer invalidate]; 
  163.         [_hideTimer release]; 
  164.     } 
  165.      
  166.     _tipsIcon.image = nil; 
  167.     _tipsIcon.hidden = YES; 
  168.      
  169.     CGSize size = [tips sizeWithFont:_tipsLbl.font constrainedToSize:CGSizeMake(320, 30)]; 
  170.     size.width += TIPMESSAGE_RIGHT_MARGIN; 
  171.     if (size.width > self.bounds.size.width - ICON_WIDTH) { 
  172.         size.width = self.bounds.size.width - ICON_WIDTH; 
  173.     } 
  174.      
  175.     _tipsLbl.frame = CGRectMake(self.bounds.size.width - size.width, 0, size.width, self.bounds.size.height); 
  176.     _tipsLbl.text = tips; 
  177.      
  178.     [self makeKeyAndVisible]; 
  179.  
  180. - (void)showTips:(NSString*)tips hideAfterDelay:(NSInteger)seconds 
  181.     [self showTips:tips]; 
  182.      
  183.     _hideTimer = [NSTimer scheduledTimerWithTimeInterval:seconds target:self selector:@selector(hideTips) userInfo:nil repeats:NO]; 
  184.  
  185. /* 
  186.  * @brief show tips icon and message on statusBar 
  187.  */ 
  188. - (void)showTipsWithImage:(UIImage*)tipsIconImage message:(NSString*)message 
  189.     if (_hideTimer) { 
  190.         [_hideTimer invalidate]; 
  191.         [_hideTimer release]; 
  192.     } 
  193.      
  194.     CGSize size = [message sizeWithFont:_tipsLbl.font constrainedToSize:self.bounds.size]; 
  195.     size.width += TIPMESSAGE_RIGHT_MARGIN; 
  196.     if (size.width > self.bounds.size.width - ICON_WIDTH) { 
  197.         size.width = self.bounds.size.width - ICON_WIDTH; 
  198.     } 
  199.      
  200.     _tipsLbl.frame = CGRectMake(self.bounds.size.width - size.width, 0, size.width, self.bounds.size.height); 
  201.     _tipsLbl.text = message; 
  202.      
  203.     _tipsIcon.center = CGPointMake(self.bounds.size.width - _tipsLbl.bounds.size.width - ICON_WIDTH / 2 - ICON_RIGHT_MARGIN, self.bounds.size.height / 2); 
  204.     _tipsIcon.image = tipsIconImage; 
  205.     _tipsIcon.hidden = NO; 
  206.      
  207.     [self makeKeyAndVisible]; 
  208.  
  209. - (void)showTipsWithImage:(UIImage*)tipsIconImage message:(NSString*)message hideAfterDelay:(NSInteger)seconds 
  210.     [self showTipsWithImage:tipsIconImage message:message]; 
  211.      
  212.     _hideTimer = [NSTimer scheduledTimerWithTimeInterval:seconds target:self selector:@selector(hideTips) userInfo:nil repeats:NO]; 
  213.  
  214. /* 
  215.  * @brief hide tips window 
  216.  */ 
  217. - (void)hideTips 
  218.     self.hidden = YES; 
  219.  
  220. @end 

该状态栏提示控件实现了添加提示图标和提示文字,以及自动隐藏等功能。显示和隐藏动画实 现起来比较简单,控件中就没有实现,大家可以根据需要随意发挥。该控件使用单例模式,接口非常简单,使用起来很方便。上面代码相信大家都能看得懂,这里就不展开讲了,有什么问题欢迎讨论。

责任编辑:闫佳明 来源: cnblogs
相关推荐

2015-02-12 14:49:36

CGToast状态栏提示Status

2013-07-18 16:09:10

自定义iOS状态栏iOS开发iOS学习

2017-02-17 11:00:57

状态栏Android

2012-12-24 14:42:48

iOS自定义状态栏

2013-11-20 15:08:32

iOS开发状态栏

2021-08-09 20:29:27

Android沉浸式状态栏

2023-07-12 23:27:24

Powerline编辑器

2016-11-29 11:20:08

Android

2017-12-05 12:44:57

Android沉浸式状态栏APP

2022-11-23 14:47:29

北向开发鸿蒙

2021-06-15 15:04:38

Android 12安卓通话

2021-06-15 15:28:31

谷歌Android开发

2011-06-16 11:15:04

2018-03-09 12:12:25

macOSiOS苹果

2018-05-16 09:11:19

Android状态栏移动系统

2013-04-09 10:03:29

iOS6.0旋转兼容

2022-09-29 10:26:59

iOSScaffoldflutter

2009-10-16 14:31:48

VB.NET Noti

2014-07-15 09:26:13

swiftiosMPGNotifica

2018-10-25 15:13:23

APP脱壳工具
点赞
收藏

51CTO技术栈公众号