功能:在 HyponNerd 中聋丝,用戶可以在兩個視圖層次結構之間自由切換编振∽罕纾——第一個視圖層次結構用于催眠自己,第二個用于設置催眠提醒時間踪央。
要點:窗口臀玄、視圖控制器、延遲加載機制畅蹂、添加視圖健无、訪問視圖、UITabBarController 標簽頁視圖控制器液斜、UITextField 委托與文本輸入累贤、運動效果、使用調(diào)試器旗唁、本地通知畦浓、
Hypontize 截屏 | Reminder 截屏 |
---|---|
main() 與 UIApplication
用 Objective-C 語言編寫的程序的執(zhí)行入口與 C 語言相同,都是 main()
函數(shù):
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
UIApplicationMain 函數(shù):
- 1?? 創(chuàng)建一個 UIApplication 對象(每個iOS應用都有且只有一個該對象)检疫,用于維護運行循環(huán)讶请。
- 2?? UIApplicationMain 函數(shù)還會創(chuàng)建某個指定類的對象,并將其設置為 UIApplication 對象的 delegate屎媳,該對象的類是由 UIApplicationMain 函數(shù)得最后一個實參指定的夺溢,該實參的類型是 NSString 對象,代表的某個類的類名烛谊。
- 以上代碼塊中风响,UIApplicationMain 會創(chuàng)建一個 AppDelegate 對象,并將其設置為 UIApplication 對象的 delegate丹禀。
- 在應用啟動運行循環(huán)并開始接收事件前状勤,UIApplication 對象會向其委托發(fā)送一個特定的消息(
didFinishLaunchingWithOptions:
)完成相應的初始化工作,該方法只會在應用啟動完畢后調(diào)用一次双泪。
初始化方法實現(xiàn)在 AppDelegate.m 文件中:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
UIWindow
UIWindow 的常見用法示例:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 1.創(chuàng)建 UIWindow 實例持搜。
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// 2.指定 UIWindow 對象的根視圖控制器。
ViewController *viewController = [[ViewController alloc] init];
self.window.rootViewController = viewController;
// 3.設置背景色焙矛。
self.window.backgroundColor = [UIColor whiteColor];
// 4.設置 UIWindow 實例為主窗口并使其可見葫盼。
[self.window makeKeyAndVisible];
return YES;
}
-
UIWindow 對象提供了一個方法
setRootViewController
將視圖控制器的視圖層次結構加入應用窗口。當程序?qū)⒛硞€視圖控制器設置為 UIWindow 對象的rootViewControl
時村斟,UIWindow 對象會將該視圖控制器的 view 作為子視圖加入窗口贫导,同時負責維護 viewController 和 view 對應的生命周期抛猫。此外,還會自動調(diào)整 view 的大小孩灯,將其設置為與窗口的大小相同闺金。 -
rootViewControl
的view需要在應用啟動完畢之后就顯示,所以 UIWindow 對象會在設置完rootViewControl
后立刻加載其 view峰档。
UIWindow 的主要作用
- 作為 UIView 的最頂層容器掖看;包含應用顯示所需要的所有 UIVIew;
- 傳遞觸摸消息和鍵盤事件給 UIView面哥;
WindowLevel
UIKIT_EXTERN const UIWindowLevel UIWindowLevelNormal; // 層級:0
UIKIT_EXTERN const UIWindowLevel UIWindowLevelAlert; // 層級:1000
UIKIT_EXTERN const UIWindowLevel UIWindowLevelStatusBar __TVOS_PROHIBITED; // 層級:2000
視圖控制器
將視圖控制器作為子視圖添加到應用窗口(UIWindow)中。
視圖控制器是 UIViewController 類或其子類對象毅待。每個視圖控制器都負責管理一個視圖層次結構尚卫,包括視圖層次結構中的視圖并處理相關用戶事件,以及將整個視圖層次結構添加到應用窗口尸红。
UIViewController 的 view
屬性
view
屬性指向一個 UIView
對象吱涉。
UIViewController
對象可以管理一個視圖層次結構,view
就是這個視圖層次結構的根視圖外里,當程序?qū)?view
作為子視圖加入窗口時怎爵,也會加入 UIViewController
對象所管理的整個視圖層次結構。
根視圖控制器
UIWindow 對象提供了一個方法將視圖控制器的視圖層次結構加入應用窗口:setRootViewController
盅蝗,當程序?qū)⒛硞€視圖控制器設置為 UIWindow 對象的 rootViewControl 時鳖链,UIWindow 對象會將該視圖控制器的view作為子視圖加入窗口,此外墩莫,還會自動調(diào)整view的大小芙委,將其設置為與窗口的大小相同。rootViewControl
的 view 需要在應用啟動完畢之后就顯示狂秦,所以 UIWindow 對象會在設置完rootViewControl
后立刻加載其 view 灌侣。
根視圖控制器創(chuàng)建步驟:
創(chuàng)建 UIViewController 的子類
@interface myViewController : UIViewController
在 AppDelegate.h 中聲明屬性
@property (strong,nonatomic) UIViewController *viewController;
-
在 AppDelegate.m 中實現(xiàn)初始化
// 方法一: // 設置根視圖控制器 BNRMyViewController *mvc = [[BNRyViewController alloc]init]; self.window.rootViewController = mvc; // 方法二: // 獲取指向 NSbundle 對象的指針,該 NSBundle 對象代表應用程序的主程序包 // mainBundle 主程序包對應于文件系統(tǒng)中項目的根目錄 NSBundle *appBundle = [NSBundle mainBundle]; // 告訴初始化方法在 appBundle 中查找 HQLReminderViewController.xib 文件 self.viewController = [[myViewController alloc] initWithNibName:@"HQLReminderViewController" bundle: appBundle]; //將 viewController 作為 window 的根視圖控制器 self.window.rootViewController =self.viewController;
上述方法二中對【對象是通過 XIB 文件創(chuàng)建的根視圖控制器】設置同樣可使用方法一實現(xiàn)裂问,即:
HQLReminderViewController *rvc = [[HQLReminderViewController alloc] init];
self.window.rootViewController = rvc;
為什么向一個需要使用 NIB 文件的視圖控制器發(fā)送 init
消息也能成功加載對應的視圖侧啼?
-
initWithNibName:bundle:
是 UIViewController 的指定初始化方法。 - 向視圖控制器發(fā)送
init
消息會調(diào)用指定初始化方法initWithNibName:bundle:
并為兩個參數(shù)都傳入nil
堪簿。 - 即使向一個需要使用 NIB 文件的視圖控制器發(fā)送
init
消息痊乾,UIViewControl 對象仍然會在應用程序中查找和當前UIViewControl
子類同名的 XIB 文件。 - ?? 因此建議為 【UIViewController 子類】和【該子類需要載入的NIB文件】取相同的名稱戴甩,這樣當視圖控制器需要加載視圖時符喝,會自動載入正確的 XIB 文件。
lazy loading 延遲加載視圖
- 視圖控制器不會在其被創(chuàng)建出來的那一刻馬上創(chuàng)建并載入相應的視圖甜孤,只有當應用需要將某個視圖控制器的視圖顯示到屏幕上時协饲,相應的視圖控制器才會創(chuàng)建其視圖(提高內(nèi)存使用效率)蠕搜。
- 也就是說,視圖控制器剛被創(chuàng)建時党远,其
view
屬性會被初始化為 nil卿吐。之后,當應用需要將視圖控制器的視圖顯示到屏幕上時而线,如果view
屬性是 nil铭污,就會自動調(diào)用loadview
方法。 - ?? 為了實現(xiàn)視圖延遲加載膀篮,
在initWithNibName:bundle:
中不應該訪問view
或View
的任何子視圖嘹狞。凡是和view
或view
的子視圖有關的代碼,都應該在viewDidLoad
方法中實現(xiàn)誓竿,避免加載不需要在屏幕上顯示的視圖磅网。 - 視圖控制器可以通過兩種方式創(chuàng)建視圖層次結構:
- 代碼方式:覆蓋 UIViewController 中的
loadView
方法 - 文件方式:使用 interface builder 創(chuàng)建一個 NIB 文件,然后加入所需的視圖層次結構筷屡,最后視圖控制器會在運行時加載由該 NIB 文件編譯而成的 XIB 文件涧偷。
- 代碼方式:覆蓋 UIViewController 中的
- (void)loadView {
[super loadView];
//創(chuàng)建一個 HQLHypnosisView 對象
HQLHypnosisView *backgroundView = [[HQLHypnosisView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.view = backgroundView;
}
上述代碼中,將 HQLHypnosisView 對象賦值給視圖控制器的
view
屬性毙死。-
子類視圖控制器中的
view
屬性繼承自UIViewControl
對象:@property(null_resettable, nonatomic,strong) UIView *view;
如果視圖還沒有被設置燎潮,那么 getter 方法會首先調(diào)用自身的loadView
方法,如果重寫了 setter 或 getter 方法扼倘,子類必須調(diào)用父類确封。
UIView
添加視圖
frame
屬性:用于確定與視圖層次結構中其他視圖的相對位置,從而將自己的圖層與其他視圖的圖層正確組合屏幕上的圖像再菊。
bounds
屬性:用于確定繪制區(qū)域隅肥,避免將自己繪制到圖層邊界之外。
frame
屬性是從父視圖的坐標系統(tǒng)來看的袄简,而 bounds
屬性是從本身的坐標系統(tǒng)來看的.
視圖的相關結構:
Core Graphics( CG )
-
CGPoint:2D空間中的位置坐標,(x,y)
生成函數(shù):CGPointMake(x,y)
-
CGSize:某個對象的尺寸:width腥放、height
生成函數(shù):CGSizeMake(width,height)
-
CGRect:位置和尺寸:origin 和 size
生成函數(shù):CGRectMake(x,y,width,height)
//創(chuàng)建一個CGRect結構
//CGRect firstFrame = CGRectMake(160, 240, 100, 150);
CGRect firstFrame = self.window.bounds;
//創(chuàng)建HQLHypnosisView對象
HQLHypnosisView *firstView = [[HQLHypnosisView alloc] initWithFrame:firstFrame];
//設置HQLHypnosisView背景色
firstView.backgroundColor = [UIColor greenColor];
//將HQLHypnosisView對象加入UIWindow對象
[self.window addSubview:firstView];
也可以在 HQLHypnosisView.m 中覆寫 initWithFrame:
方法設置 HQLHypnosisView 背景色。
- (instancetype)initWithFrame:(CGRect)frame {
self =[super initWithFrame:frame];
if (self) {
//設置HQLHyponsisView對象的背景顏色為透明
self.backgroundColor = [UIColor clearColor];
}
return self;
}
視圖層次的嵌套
HQLHypnosisView *secondView = [[HQLHypnosisView alloc] initWithFrame:secondFrame];
secondView.backgroundColor = [UIColor blueColor];
//將 secondView 添加到 window 上
[self.window addSubview:secondView];
//視圖層次嵌套
//視圖的frame所代表的位置是相對于其父視角的
//將 secondView 添加到 firstview 上
[firstView addSubview:secondView];
訪問視圖
通常情況下绿语,在用戶看到 XIB 文件中創(chuàng)建的視圖之前需要對它們做一些額外的初始化工作秃症。但是,關于視圖的初始化代碼不能寫在視圖控制器的初始化方法中—此時視圖控制器并未加載 NIB 文件吕粹,所有指向視圖的屬性都是 nil
种柑。如果向這些屬性發(fā)送消息,雖然編譯時不會報錯匹耕,但是運行時無法對這些屬性做任何操作聚请。
兩種訪問 NIB 文件中視圖的方法:
-
-viewDidLoad:
該方法會在視圖控制器加載完視圖之后被調(diào)用,此時視圖控制器中所有視圖屬性都已經(jīng)指向了正確的視圖對象。 -
-viewWillAppear:
該方法會在視圖控制器的view
添加到應用窗口之前被調(diào)用驶赏。
區(qū)別: 如果只需要在應用啟動后設置一次視圖對象炸卑,就選擇viewDidLoad
;如果用戶每次看到視圖控制器的 view 時都需要對其進行設置煤傍,則選擇viewWillAppear
盖文。 - 相反,
viewWillDisappear:
和viewDidDisappear:
方法會在每次視圖控制器的view
從屏幕上消失時被調(diào)用蚯姆。
設置啟動頁面延時
//延時3秒
[NSThread sleepForTimeInterval:3.0];
運動效果
iOS 設備內(nèi)嵌了許多功能強大的傳感器五续,例如加速傳感器,磁場傳感器和三軸陀螺儀等龄恋。應用可以通過這些傳感器了解設備的速度疙驾、方向和角度,并實現(xiàn)有用的功能郭毕。
例如荆萤,應用可以根據(jù)設備的方向自動將界面調(diào)整為橫排模式或豎排模式。從iOS 7開始铣卡,Apple 引入了一些新 API,可以輕松為應用添加一種通過感應器實現(xiàn)的視差(parallax)效果偏竟。
在 HQLHyponViewController.m 中修改drawHyponticMessage:
方法煮落,為 UILabel 對象分別添加水平方向和垂直方向的視差效果,使 UILabel 對象的中心點坐標在每個方向上最多移動25點踊谋。
// 在屏幕隨機位置繪制20個 UILabel 對象
- (void)drawHypnoticMessage:(NSString *)message {
for (int i =0; i<20; i++) {
UILabel *messageLabel = [[UILabel alloc] init];
// 設置UILabel對象的文字和顏色
messageLabel.backgroundColor = [UIColor clearColor];
messageLabel.textColor = [UIColor whiteColor];
messageLabel.text = message;
// 根據(jù)需要顯示的文字調(diào)整 UILabel 對象的大小
[messageLabel sizeToFit];
// 獲取隨機x坐標
// 使 UILabel 對象的寬度不超出 HQLHyponsisViewController 的 view 的寬度
int width = (int)(self.view.bounds.size.width - messageLabel.bounds.size.width);
int x = arc4random() % width;
// 獲取隨機y坐標
// 使UILabel對象的高度不超出HQLHyponsisViewController的view的高度
int height = (int)(self.view.bounds.size.width - messageLabel.bounds.size.height);
int y =184 + arc4random() % height;
// 設置 UILabel 對象的 frame
CGRect frame = messageLabel.frame;
frame.origin = CGPointMake(x, y);
messageLabel.frame = frame;
// 將UILabel對象添加到HQLHyponsisViewController的 view 中
[self.view addSubview:messageLabel];
// 運行效果:水平方向和垂直方向的視差效果
// 設置 UILabel 對象的中心點坐標在每個方向上最多移動 25 點
UIInterpolatingMotionEffect *motionEffect;
motionEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
motionEffect.minimumRelativeValue = @(-25);
motionEffect.maximumRelativeValue = @(25);
[messageLabel addMotionEffect:motionEffect];
motionEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
motionEffect.minimumRelativeValue = @(-25);
motionEffect.maximumRelativeValue = @(25);
[messageLabel addMotionEffect:motionEffect];
}
LocalNotification(本地通知)
本地通知(local notification)用于向用戶提示一條消息蝉仇,即使應用沒有運行,用戶也可以收到本地通知殖蚕。
應用還可以通過后臺服務器實現(xiàn)推送通知(push notification).有關推送通知的技術細節(jié)請參考 Apple 的 Local and Push Notification Programming Guide(本地通知和推送通知編程指南)轿衔。
Demo:
實現(xiàn)本地通知方法代碼如下:
- (IBAction)addReminder:(id)sender {
NSDate *date = self.datePicker.date;
NSLog(@"Setting a reminder for %@",date);
if ([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)]){
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
}
// 創(chuàng)建 UILocalNotification 對象
UILocalNotification *note = [[UILocalNotification alloc] init];
// 設置顯示內(nèi)容
note.alertBody = @"Hypnotize me!";
// 設置提醒時間
note.fireDate = date;
// 使用 scheduleLocalNotification: 方法注冊通知
[[UIApplication sharedApplication] scheduleLocalNotification:note];
NSLog(@"addReminder run over");
}