在之前已經介紹了iOS的學習路線圖,因為中間遇到一些Android開發問題,所以就耽擱了一段時間,那麼接下來的這段時間我們將繼續開始iOS的狂暴之路學習,按照國際慣例,第一個應用當然是我們的HelloWorld程序了。那麼本文將會通過這麼一個簡單的程序來講解一下iOS中的程序生命周期,應用中關鍵的幾個對象,項目結構,最後在手把手的創建一個空項目。
下面先用Xcode來新建一個HelloWorld程序:
點擊下一步即可:
這裡和我們在AndroidStudio中新建Android程序非常類似,不多說了,點擊下一步即可:
這樣項目就建立好了,下面我們讓程序運行起來,展示一個HelloWorld文字。這個有兩種方式,在Android中也是類似,我們這裡使用UILabel來進行操作。類似於Android中的TextView控件,一種方式是代碼寫布局,一種是采用拖的方式類似於Android中的布局文件。這裡為了方便而且以後會詳細介紹拖的那種方式,直接用代碼進行編寫了。
那麼我們在哪裡編寫代碼呢?這裡的入口是在根控制器ViewController類:
這個入口類似於Android中的Activity的onCreate方法,我們一般都是在這裡初始化一個View,這裡看到代碼邏輯不是很復雜,但是隱含的知識點很多,不過不是本文的重點,比如這裡涉及到了iOS的坐標問題,View之間的關系等,這個是在後面章節會詳細介紹View知識點在加以分析。代碼寫完了,運行程序吧,運行也很簡單:
直接點擊左上角的運行按鈕即可,同時可以選擇項目運行的目標設備。運行結果如下:
這裡看到了,在4s設備中一個簡單的HelloWorld就展示出來了。好了,下面就要開始我們今天的重要內容了,我們從這個簡單的程序入手能夠學習到哪些知識。
第一、程序的入口和生命周期
我們在開發Android程序的時候都知道其實一個應用程序的入口並不是MainActivity的onCreate方法也不是Application的onCreate方法,而是ActivityThread的main方法,與之類似,在iOS中一個程序的入口也是main方法,而這個main類在main方法中:
每個程序都有一個main.m這個類,內部有一個main方法,而這個方法我們看到和C語言中的main函數形式是一致的,入口就在這裡,那麼這裡干了一件事就是托付應用程序的代理對象AppDelegate類,也就是把整個應用程序的邏輯都托付給了AppDelegate類,在iOS中這種方式叫做代理,而在Android中我們通常叫做回調機制,然後UIApplicationMain類就會和AppDelegate類進行交互,比如應用的生命周期,事件處理等,那麼下面就來看一下AppDelegate這個類:
在這個類中我們可以看到很多時機的回調方法,這個就是和應用的生命周期方法相關聯的,下面就來一一分析這些方法的回調時機:
1、告訴代理啟動基本完成程序准備開始運行
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
2、告訴代理進程啟動但還沒進入狀態保存
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
3、當應用程序將要入非活動狀態執行,在此期間,應用程序不接收消息或事件,比如來電話了
- (void)applicationWillResignActive:(UIApplication *)application
4、當程序被推送到後台的時候調用。所以要設置後台繼續運行,則在這個函數裡面設置即可
- (void)applicationDidEnterBackground:(UIApplication *)application
5、當程序從後台將要重新回到前台時候調用,這個剛好跟上面的那個方法相反
- (void)applicationWillEnterForeground:(UIApplication *)application
6、當應用程序入活動狀態執行,這個剛好跟上面那個方法相反
- (void)applicationDidBecomeActive:(UIApplication *)application
7、當程序將要退出是被調用,通常是用來保存數據和一些退出前的清理工作
- (void)applicationWillTerminate:(UIApplication *)application
8、當應用接收到內存警告回調方法
-(void)applicationDidReceiveMemoryWarning:(UIApplication *)application
看完上面的這些生命周期的回調方法之後,大家可能會想到這個和Android中的Activity非常類似了,而不是Application類了,在Android中比較復雜一般有三個物理按鍵關系Activity的生命周期,而在iOS中只有一個Home鍵,所以這裡的生命周期很好理解的。
第二、應用的窗口
上面就介紹完了iOS程序的生命周期相關的類AppDelegate,下面還需要繼續分析,怎麼關聯上ViewController類的,把HelloWorld展示出來的?
首先我們在程序啟動完成之後的回調方法中打印一個日志,把當前應用的跟控制器打印出來:
注意:iOS中的打印日志方法和Android也很類似,使用字符串格式化打印即可,一般打印對象的占位符是%@,而如果想打印一個對象特定值的話,就需要實現這個類的description方法,這個和Java中的toString方法一樣的。這裡在多說幾句,就是一般打印基本類型的格式如下:
%d,%i 整型 (%i的老寫法)
%hd 短整型
%ld,%lld 長整型
%u 無符整型
%f 浮點型和double型
%0.2f 精度浮點數,只保留兩位小數
%x 為32位的無符號整型數(unsigned int),打印使用數字0-9的十六進制,小寫a-f;
%X 為32位的無符號整型數(unsigned int),打印使用數字0-9的十六進制,大寫A-F;
%o 八進制
%p 指針地址
%s char*類型的字符串
%c char字符類型
%C unichar類型
而這些類型打印格式不用背的,用多了就自然記住了,而且這裡用的最多的應該就是%i,%s,%p,%c,%f這幾個。
打印結果如下:
看到了當一個應用啟動了之後,有哪些生命周期的回調方法會執行,同時看到了根控制器就是我們上面添加HelloWorld控件的入口ViewController類對象。那麼這裡就引出了兩個對象:一個是self.window對象,一個是window.rootViewController對象。下面先來看看self.window這個對象了,其實這個對象是一個UIWindow類型。和Android一樣,一個程序就對應一個窗口是後續展示View的基礎。
通常一個應用就對應一個UIWindow,就是應用的窗口,但是這話也不能說的太絕對,因為後面不管是Android還是iOS都會出現多屏開發模式,到時候就不止一個Window窗口了。他是後續展示View的基石,如果沒有了這個UIWindow的話,那麼應用所有的UI都展示不出來,其實UIWindow是一個特殊的View,他繼承了UIView類的,也是可以直接添加View控件的以及你可以像使用View那樣去操作它:
而上面是說到了,一個應用只能有一個UIWindow,但是我們可以定義多個UIWindow,然後設置指定的UIWindow為主窗口,也是可以的。如果想讓哪個窗口成為主窗口並且展示的話,只需要調用makeKeyAndVisible方法即可。
第三、應用的根控制器
上面介紹完了UIWindow知識點,下面在來看一下UIViewController類,在iOS中UIViewController類就相當於Android中的Activity,用於控制View的展示功能,他是一個應用中每個頁面的載體,在Android中一個程序都有一個默認或者是主Activity,在iOS中也是一樣,有一個根控制器,而這個根控制器就是需要設置到應用的唯一窗口中,也就是UIWindow的rootViewController屬性值。關於控制器也有一些生命周期方法,用的最多也是最主要的一個就是View加載完成之後的方法:
- (void)viewDidLoad
這個方法有點類似於Activity的onCreate方法,我們如果想自己添加View的話,就是在這個方法中進行操作的。從上面的那個例子中我們可以看到在這個方法中我們定義了一個UILabel屬性,而每個控制器又有一個非常重要的屬性就是父View,我們自己需要添加的View都可以通過addSubView方法添加即可。
注意:
在iOS中添加View非常簡單和方便,因為任何一個控件包括上面講到的UIWindow都是繼承UIView類,而這些控件可以隨便添加其他控件,沒有任何限制,比如你可以在一個按鈕UIButton中添加一個標簽UILabel完全可以的,但是你要是在Android中就不可以了,Android中如果一個控件要添加子View,他必須繼承ViewGroup,比如我們常用的幾種布局類,這個看來iOS真的比Android簡單多了。
我們可以看到一個應用程序肯定會包含多個控制器的,因為一般應用都會包含多個頁面,而且每個頁面之間還可以跳轉,在Android中Activity之間的跳轉使用Intent來進行操作的,但是在iOS中沒有這種機制,而是用特定的控制器管理類,一般是用到的導航控制器和選項卡控制器來做管理,具體知識到後面接受控制器在詳細講解。
第四、總結四大對象關系
到這裡,我們就分析完了一個iOS程序中非常重要的四個對象:UIApplicationDelegate,UIWindow,UIViewController,UIView
從這張圖中,可以了解到,一個應用程序一定包含了程序應用的代理對象UIAppDelegate類,應用窗口UIWindow類。然後窗口還有一個根控制器頁面UIViewController類,每個控制器都有一個展示UI的父View,根控制器也不例外。最後是每個控制器都會關聯一個父View用於展示每個頁面的UI。
當我們知道了一個應用的結構以及關鍵對象之後,下面我們就來手把手的新建一個空項目,然後自己去新建這四個關鍵對象。因為Xcode8之後就不在支持新建一個空項目了,為了新建一個空項目,我們可以新建一個項目然後把類都刪了,就變成這樣了:
第一步:新建一個UIViewController根控制器
一定要選擇CocoaTouch Class文件:
到這裡我們其實就相當於完成了一個簡單的應用程序了,下面就需要在應用的回調方法中設置窗口信息和根控制器信息即可:
這時候運行程序可以看到,一片紅色的應用:
從這裡就可以看到,一個應用其實最基本的元素一定有UIApplicationDelegate和UIWindow和根控制器即可,那麼這個其實也是一個簡單的應用了,但是在實際開發中肯定是包含多個控制器的,並且每個根控制器都對應一個View布局文件,所以下面就開始手把手的創建對應的View信息。當然這裡可以可以在控制器的回調方法中手動的編碼添加View,但是這裡我們在介紹iOS中其實和Android一樣,每個控制器都有對應的布局文件,這裡叫做xib文件。
第二步:新建一個xib文件
選擇下一步,命名root.xib即可
新建完成之後,可以看到什麼都沒有:
我們就需要一步一步的創建了。
第三步:關聯xib和控制器
然後我們把當前的xib的File's Owner設置成我們的根控制器,這裡必須得設置xib的File' Owner,不然會報錯的。
所以從這裡可以看到,這裡的xib其實就相當於Android中的布局文件xml,每個布局文件都是和一個Activity進行關聯的,而這裡也是一樣的原理,一個xib必須依托一個控制器,所以這裡的File's Owner設置對應的控制器即可。
注意:
但是iOS中的xib文件和Android中的布局文件又有點差別,這裡xib不僅可以設置視圖布局格式,後面再會介紹一個案例,這裡可以包含一些特殊的對象,後面介紹如何把控制器,窗口等對象都搞到一個xib中,完全不用編寫代碼可以實現一個應用的創建了,而關於xib和Xcode默認創建的stroyboard有什麼區別呢?就可以簡單的理解,stroyboard更好的管理多個控制器和視圖之間的關系,比如現在我想創建一個控制器,那麼就要創建對應的xib文件了,而一個項目中如果有多個控制器頁面,那麼工程中就會有多個xib文件,這樣可能會不好管理,而stroyborad就可以把這些文件都搞到一起管理,但是從其他iOS老鳥中得知,這個其實還是要具體項目和每個人的開發習慣了,並不是強烈要求一定要用stroyborad的。
第四步:在xib中添加父View
上面設置好了xib文件和控制器的關系,下面還得設置控制器的父View屬性值,首先我們得給這個xib中添加一個View,如果不設置的話,運行程序會報錯的,添加View很簡單,我們在Xcode的最右下角有一個可以選擇各種控件和對象的界面:
搜索View控件,找到之後,直接拖到xib內容中即可。托成功之後,我們還需要把控制器的view屬性關聯到這個View控件中,不然也是會報錯的。這裡就很簡單了,對於Xcode開發工具來說,只要一拖連接即可。如下圖所示:
注意:
在iOS中有一個很方便的功能,就是給對象賦值以及後面給控件添加事件,有時候可以選擇拖這種快捷方式的,不過得有一個前提,就是這個屬性必須設置IBOutlet特性,設置之後就可以看到屬性前面有一個小圓圈了:
表示會出現在拖的列表中了,然後如果想賦值給這個屬性,就把他拖到指定對象即可,所以中這裡也可以看到iOS中的xib和Android中的布局文件有點區別。還有一個是IBAction特性,這個主要是添加控件的事件的,這個一般是添加在一個方法中的,然後這個方法就會出現在拖的列表中,比如我們在控制器中定義一個這個方法:
這裡可以看到也是有一個小圓圈的,下面在回去看xib中的File's Owner:
這裡可以看到了,當你去拖doClick方法的右邊加號圓圈到一個UIButton控件上的時候,會出現這個界面,其中列舉了多個事件,我們可以選中一個之後,就表示這裡的UIButton控件的這個事件觸發的方法是doClick了。從這個可以看到給View控件賦值和添加事件的方法還是非常方便的,而在Android中就需要findViewById方法,然後在set一些事件回調了,當然Android中現在有一些開源框架做到了這種類似功能,主要采用注解功能,無序findViewById賦值和添加事件了。不過我不怎麼喜歡使用這個框架,感覺原生最好!
第五步:加載xib文件
好了,到這裡我們就新建好了一個根控制器以及對應的xib文件了,那麼下面如果想讓這個控制器展示這個xib內容,還需要做一些工作就是要手動的加載這個xib文件,上面那個連接操作只是簡單的建立關系,加載還得自己寫代碼,就相當於Android中的setContentView方法效果一下,這裡就需要改一下控制器的初始化方法了:
控制器提供了一個initWithNibName:bundle:方法,這個方法可以顯示的加載一個xib文件,這裡需要注意兩點就是xib文件名不能攜帶後綴名,其次就是第二個參數表示xib文件的路徑,傳入nil表示從項目的根目錄中查找。
設置了控制器加載xib代碼之後,下面就可以運行程序了:
看到效果之後,發現我們不僅可以在控制器中手動編寫代碼添加View,也可以使用xib文件來進行頁面布局的。 到這裡我們就手把手的新建了一個簡單項目,在這個過程中我們將更了解了程序中的四個重要對象的關系了,下面在來總結一下: 1、UIAppDelegate類 這個類主要是用來管理應用的生命周期和一些事件的回調方法,每個應用都將有一個這個類。 2、UIWindow類 這個類主要是用來展示應用UI界面的窗口,他其實是一個特殊的UIView,可以像操作控件一樣來操作它,而每個應用都有一個唯一的主窗口,也就是UIAppDelegate類中的window屬性值。 3、UIViewController類 這個類相當於管理應用中每個頁面的功能類,他和Android中的Activity非常類似,而一個應用一般都是會包含多個控制器的,但是一定有一個根控制器,這個控制器一般要設置到UIWindow的rootViewController屬性值中。同時每個控制器如果要用代碼布局,就在viewDidLoaded方法中進行添加,如果要用xib布局文件來操作,就需要手動的進行加載操作,使用控制器的initWithNibName:bundle:方法。 4、UIView類 其實這個類是iOS中每個控件的父類,但是每個控制器都有一個父View,這個就是UIView類,一般要設置到UIViewController的view屬性值中的。