公司的開發的項目要求在狀態列上邊加入程式下載的進度條,之前寫的程式,由於是根據ipad的朝向來設定自訂的狀態列的frame,以及子視圖的 frame和transform,出現一些不太容易解決的bug。這兩天正好項目不太緊,就好好學習一下這方面的知識,以下是我所總結的一點經驗:
這裡說明一下,Apple沒有開放的狀態列的API,在ios 的官方文檔沒有提到修改Window Level的方式;
先看一下Window Level的可用的值包括:
1: typedef CGFloat UIWindowLevel;
2: const UIWindowLevel UIWindowLevelNormal; // 0.0
3: const UIWindowLevel UIWindowLevelAlert; // 2000.0
4: const UIWindowLevel UIWindowLevelStatusBar; // 1000.0
預設我們的UIView layer都是在UIWindowLevelNormal上,這也就是為什麼系統彈出來的對話方塊在我們的視圖之上,因為它的Window Level層級更高。
根據WindowLevel的原理我們也就知道,如果想在系統的狀態列上,添加自訂的狀態列,就需要比UIWindowLevelStatusBar的層級更高,接下來,用代碼說明一下:
首先,先建一個Single View Application,名字自訂就可以了,
然後,建立一個類命名為: StatusBarOverlay 繼承自UIWindow類,代碼:
StatusBarOverlay.h檔案
1: #import
2:
3: @interface StatusBarOverlay : UIWindow{
4: UIView *contentView;
5: UILabel *textLabel;
6: }
7:
8: @property (nonatomic, retain) UIView *contentView;
9:
10: @property (nonatomic, retain) UILabel *textLabel;
11:
12: @end
StatusBarOverlay.m檔案
1: //
2: // StatusBarOverlay.m
3: // StatusBarDemo
4: //
5: // Created by jordy wang on 12-8-7.
6: // Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
7: //
8:
9: #import "StatusBarOverlay.h"
10:
11: #define STATUS_BAR_ORIENTATION [UIApplication sharedApplication].statusBarOrientation
12: #define ROTATION_ANIMATION_DURATION [UIApplication sharedApplication].statusBarOrientationAnimationDuration
13:
14:
15: @interface StatusBarOverlay()
16:
17: - (void)initializeToDefaultState;
18: - (void)rotateStatusBarWithFrame:(NSValue *)frameValue;
19: - (void)setSubViewHFrame;
20: - (void)setSubViewVFrame;
21: @end
22:
23:
24: @implementation StatusBarOverlay
25: @synthesize contentView;
26: @synthesize textLabel;
27:
28: //重寫init方法
29: - (id)init
30: {
31: self = [super initWithFrame:CGRectZero];
32: if (self) {
33: self.windowLevel = UIWindowLevelStatusBar + 1;
34: self.frame = [UIApplication sharedApplication].statusBarFrame;
35: [self setBackgroundColor:[UIColor orangeColor]];
36: [self setHidden:NO];
37:
38: //內容視圖
39: UIView *_contentView = [[UIView alloc] initWithFrame:self.bounds];
40: self.contentView = _contentView;
41: [self.contentView setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
42: [self.contentView setBackgroundColor:[UIColor cyanColor]];
43: [self addSubview:self.contentView];
44: [_contentView release];
45:
46:
47: //添加textLabel
48: UILabel *_textLabel = [[UILabel alloc] initWithFrame:CGRectMake(30, 0, CGRectGetWidth(self.frame)-60, CGRectGetHeight(self.frame))];
49: self.textLabel = _textLabel;
50: [self.textLabel setBackgroundColor:[UIColor blueColor]];
51: [self.textLabel setFont:[UIFont systemFontOfSize:12]];
52: [self.textLabel setTextAlignment:UITextAlignmentCenter];
53: [self.textLabel setTextColor:[UIColor blackColor]];
54: [self.textLabel setText:@"自訂的狀態列 author by jordy"];
55: [self.contentView addSubview:self.textLabel];
56: [_textLabel release];
57:
58: //註冊監聽---當螢幕將要轉動時,所出發的事件(用於操作本視圖改變其frame)
59: [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willRotateScreenEvent:)
name:UIApplicationWillChangeStatusBarFrameNotification object:nil];
60: //初始化
61: [self initializeToDefaultState];
62: }
63:
64: return self;
65: }
66:
67:
68:
69:
70: //初始化為預設狀態
71: - (void)initializeToDefaultState
72: {
73: //擷取當前的狀態列位置
74: CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
75: //設定當前視圖的旋轉, 根據當前裝置的朝向
76: [self rotateStatusBarWithFrame:[NSValue valueWithCGRect:statusBarFrame]];
77:
78:
79:
80: }
81:
82:
83: //旋轉螢幕
84: - (void)rotateStatusBarWithFrame:(NSValue *)frameValue
85: {
86: CGRect frame = [frameValue CGRectValue];
87: UIInterfaceOrientation orientation = STATUS_BAR_ORIENTATION;
88:
89: if (orientation == UIDeviceOrientationPortrait) {
90: self.transform = CGAffineTransformIdentity; //螢幕不旋轉
91: [self setSubViewVFrame];
92: }else if (orientation == UIDeviceOrientationPortraitUpsideDown) {
93: self.transform = CGAffineTransformMakeRotation(M_PI); //旋轉螢幕180度
94: [self setSubViewVFrame];
95: }else if (orientation == UIDeviceOrientationLandscapeRight) {
96: self.transform = CGAffineTransformMakeRotation((M_PI * (-90.0f) / 180.0f)); //旋轉螢幕-90度
97: [self setSubViewHFrame];
98: }else if (orientation == UIDeviceOrientationLandscapeLeft){
99: self.transform = CGAffineTransformMakeRotation(M_PI * 90.0f / 180.0f); //旋轉螢幕90度
100: [self setSubViewHFrame];
101: }
102:
103: self.frame = frame;
104: [self.contentView setFrame:self.bounds];
105: }
106:
107: //設定橫屏的子視圖的frame
108: - (void)setSubViewHFrame
109: {
110: self.textLabel.frame = CGRectMake(30, 0, 1024-60, 20);
111: }
112: //設定豎屏的子視圖的frame
113: - (void)setSubViewVFrame
114: {
115: self.textLabel.frame = CGRectMake(30, 0, 748-60, 20);
116: }
117:
118: #pragma mark -
119: #pragma mark 響應螢幕即將旋轉時的事件響應
120: - (void)willRotateScreenEvent:(NSNotification *)notification
121: {
122: NSValue *frameValue = [notification.userInfo valueForKey:UIApplicationStatusBarFrameUserInfoKey];
123: [self rotateStatusBarAnimatedWithFrame:frameValue];
124: }
125:
126: - (void)rotateStatusBarAnimatedWithFrame:(NSValue *)frameValue {
127: [UIView animateWithDuration:ROTATION_ANIMATION_DURATION animations:^{
128: self.alpha = 0;
129: } completion:^(BOOL finished) {
130: [self rotateStatusBarWithFrame:frameValue];
131: [UIView animateWithDuration:ROTATION_ANIMATION_DURATION animations:^{
132: self.alpha = 1;
133: }];
134: }];
135: }
136:
137: - (void)dealloc
138: {
139: [[NSNotificationCenter defaultCenter] removeObserver:self];
140: [textLabel release];
141: textLabel = nil;
142:
143: [contentView release];
144: contentView = nil;
145:
146: [super dealloc];
147: }
148:
149: @end
由於代碼比較簡單,並且我在上述代碼裡有相應的注釋,這裡需要說明一點的是,預設我們繼承自UIWindow的StatusBarOverlay類是hidden狀態,需要在初始化的時候設定它的hidden屬性為NO,
在旋轉螢幕過程中,自訂的狀態列與UIViewController之間的旋轉是分離的,所以我們需要做一個隱藏的動畫,在旋轉過程前先隱藏自訂的狀態列,旋轉結果後設定顯示狀態。
如果需要做一種動畫,比方從底部下移顯示一條資訊,隔N秒後又自動收回的動畫,直接設定自訂的視圖的y座標就可以了,預設y座標設定是0。
最後, 使用它的方式也比較簡單,只需要初始化,代碼:
StatusBarOverlay *statusBarOverlay = [[StatusBarOverlay alloc] init];
由於我公司的需求是開機自動下載的功能,所以我在初始化的時候,是放在了AppDelegate中。