iOS開發系列--IOS程式開發概覽

KenshinCui發表於2014-08-04

概覽

終於到了真正接觸IOS應用程式的時刻了,之前我們花了很多時間去討論C語言、ObjC等知識,對於很多朋友而言開發IOS第一天就想直接看到成果,看到可以執行的IOS程式。但是這裡我想強調一下,前面的知識是你日後開發IOS的基礎,沒有那些知識你開發IOS會很痛苦,現在很多開發人員做開發都是一知半解,程式質量確實令人擔憂,所以還是希望大家能夠熟練掌握前面的內容,開發過程中多思考,徹底理解程式執行的原理、機制。好了言歸正傳,不管怎麼樣正式進入IOS開發還是令人興奮的,今天的內容雖然說是開發預覽,其實還是有大量內容要說的:

  1. 第一個iOS程式
  2. iOS程式執行過程
  3. 檔案結構
  4. Storyboard
  5. 純程式碼實現iOS開發
  6. 補充知識點

第一個iOS程式

首先開啟Xcode—Create a new Xcode project—Single View Application--輸入專案名稱,同時選擇使用Objective-C語言,裝置選擇iPhone--接下來系統預設生成一個IOS專案模板。專案目錄結構如下:

firstios

此時什麼也不用做,直接執行看一下(注意這裡已經切換模擬器為iPhone5),沒錯我們看到了一個iOS應用程式:

firstIOS-iPhone5

程式的執行過程

在幾乎所有的程式開發中程式一般都是從main函式開始執行的,那麼IOS程式也不例外,在上圖中我們可以看到Xcode為我們生成了一個main.m檔案:

//
//  main.m
//  FirstIOS
//
//  Created by Kenshin Cui on 14-2-23.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

這個預設的iOS程式就是從main函式開始執行的,但是在main函式中我們其實只能看到一個方法,這個方法內部是一個訊息迴圈(相當於一個死迴圈),因此執行到這個方法UIApplicationMain之後程式不會自動退出,而只有當使用者手動關閉程式這個迴圈才結束。這個方法有四個引數:

  • 第一個引數和第二個引數其實就是main函式的引數,分別代表:引數個數、引數內容;
  • 第三個引數代表UIApplication類(或子類)字串,這個引數預設為nil則代表預設為UIApplication類,使用者可以自定義一個類繼承於這個類;如果為nil則等價於NSStringFromClass([UIApplication class]),大家可以自己試驗,效果完全一樣;UIApplication是單例模式,一個應用程式只有一個UIApplication物件或子物件;
  • 第四個引數是UIApplication的代理類字串,預設生成的是AppDelegate類,這個類主要用於監聽整個應用程式生命週期的各個事件(其實類似於之前我們文章中提到的事件監聽代理),當UIApplication執行過程中引發了某個事件之後會呼叫代理中對應的方法;

小技巧:

其實在Xcode中如果要看一些系統方法的解釋或者引數說明,可以直接滑鼠放到這個方法上,在Xcode右側皮膚中就會給出幫助提示,如下圖當我們放到UIApplicationMain上之後:

quickHelp

也就是說當執行UIApplicationMain方法後這個方法會根據第三個引數建立對應的UIApplication物件,這個物件會根據第四個引數AppDelegate建立並指定此物件為UIApplication的代理;同時UIApplication會開啟一個訊息迴圈不斷監聽應用程式的各個活動,當應用程式生命週期發生改變UIApplication就會呼叫代理對應的方法。

既然應用程式UIApplication是通過代理和外部互動的,那麼我們就有必要清楚AppDelegate的操作細節,下面是UIApplication詳細的程式碼:

AppDelegate.h

//
//  AppDelegate.h
//  
//
//  Created by Kenshin Cui on 14-2-23.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end

AppDelegate.m

//
//  AppDelegate.m
//  
//
//  Created by Kenshin Cui on 14-2-23.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "AppDelegate.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application
{
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

- (void)applicationWillTerminate:(UIApplication *)application
{
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

@end

這個類中定義了應用程式生命週期中各個事件的執行方法:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;程式啟動之後執行,只有在第一次程式啟動後才執行,以後不再執行;

- (void)applicationWillResignActive:(UIApplication *)application;程式將要被啟用時(獲得焦點)執行,程式啟用使用者才能操作;

- (void)applicationDidEnterBackground:(UIApplication *)application;程式進入後臺後執行,注意進入後臺時會先失去焦點再進入後臺;

- (void)applicationWillEnterForeground:(UIApplication *)application;程式將要進入前臺時執行;

- (void)applicationDidBecomeActive:(UIApplication *)application;程式被啟用(獲得焦點)後執行,注意程式被啟用時會先進入前臺再被啟用;

- (void)applicationWillTerminate:(UIApplication *)application;程式在終止時執行,包括正常終止或異常終止,例如說一個應用程式在後太執行(例如音樂播放軟體、社交軟體等)佔用太多記憶體這時會意外終止呼叫此方法;

為了演示程式的生命週期,不妨在每個事件中都輸出一段內容,簡單調整上面的程式碼:

AppDelegate.m

//
//  AppDelegate.m
//  FirstIOS
//
//  Created by Kenshin Cui on 14-2-23.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "AppDelegate.h"

@interface AppDelegate ()

@end

@implementation AppDelegate
            

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    NSLog(@"程式已經啟動...");
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application {
    NSLog(@"程式將要失去焦點...");
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    NSLog(@"程式已經進入後臺...");
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    NSLog(@"程式將要進入前臺...");
    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    NSLog(@"程式已經獲得焦點...");
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

- (void)applicationWillTerminate:(UIApplication *)application {
    NSLog(@"程式將要終止...");
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

-(void)applicationDidReceiveMemoryWarning:(UIApplication *)application{
    
}

@end

下面是各個不同操作的執行結果:

runProgress

相信通過上面執行過程大家會對整個執行週期有個大概瞭解。比較容易混淆的地方就是應用程式進入前臺、啟用、失去焦點、進入後臺,這幾個方法大家要清楚。如果一個應用程式失去焦點那麼意味著使用者當前無法進行互動操作,因此一般會先失去焦點再進入後臺防止進入後臺過程中使用者誤操作;如果一個應用程式進入前臺也是類似的,會先進入前臺再獲得焦點,這樣進入前臺過程中未完全準備好的情況下使用者無法操作。另外一般如果應用程式要儲存使用者資料會在登出啟用中進行(而不是在進入後臺方法中進行),因為如果使用者雙擊Home不會進入後臺只會登出啟用;如果使用者恢復應用狀態一般在進入啟用狀態時處理(而不是在進入前臺方法中進行),因為使用者可能是從工作列直接返回應用,此時不會執行進入前臺操作。

當然,上面的事件並不是所有AppDelegate事件,而是最常用的一些事件,其他事件大家可以查閱官方文件,例如-(void)applicationDidReceiveMemoryWarning:(UIApplication *)application;用於在記憶體佔用過多發出記憶體警告時呼叫並通知對應的ViewController呼叫其記憶體回收方法。這裡簡單以圖形方式描述一下應用程式的呼叫過程:

 image

檔案結構

這裡對於Xcode預設為我們生產的專案結果檔案做一下簡單介紹:

  1. AppDelegate(.h/.m):應用程式代理,主要用於監聽整個應用程式生命週期中各個階段的事件;
  2. ViewController(.h/.m):檢視控制器,主要負責管理UIView的生命週期、負責UIView之間的切換、對UIView事件進行監聽等;
  3. Main.storyboard:介面佈局檔案,承載對應UIView的檢視控制元件;
  4. Images.xcassets:應用程式影象資原始檔;
  5. Info.plist:應用程式配置檔案;
  6. main.m:應用程式入口函式檔案;
  7. xxx-prefix.pch:專案公共標頭檔案,此檔案中的匯入語句在編譯時會應用到所有的類檔案中,相當於公共引入檔案(注意在Xcode6中沒有提供此檔案)

Images.xcassets

關於AppDelegate、main.m前面已經介紹過了,ViewController和Main.storyboard在後面介紹,這裡先說一下Image.xcassets檔案。在Xcode中開啟這個檔案會發現裡面有兩個設定項:AppIcon和LaunchImage

AppIcon

AppIcon

在AppIcon中可以看到三個圖示設定,當我們勾選了右側ios6.1 and Prior Sizes或者其他選項這個圖示會自動增多,也就是說可以設計的圖示跟應用程式準備支援的裝置系統有關,這裡我們就以預設的ios7為例(現在基本上裝置都升級到ios7了):

a.iPhone Spotlight-iOS5,6 Settings-iOS 5-7 29pt:用於設定iOS5或者iOS6的搜尋圖示以及iOS5、iOS6、iOS7的設定圖示,大小是58*58。

iOS搜尋圖示:

 Spotlight

iOS設定圖示:

settingicon

b.iPhone Spootlight iOS 7 40pt:設定iOS7的搜尋圖示,大小是80*80。具體參見上圖。

c.iPhone App iOS7 60pt:設定iOS7的應用圖示,大小是120*120。

iOS應用圖示:

 IconSettings

LaunchImage

在LaunchImage中兩個圖示設計主要用於豎屏啟動圖

LaunchImage

a.iPhone Portraint iOS7 2x:大小為640*1136的啟動圖片;

b.iPhone Portraint iOS7 R4:大小為640*960的啟動圖片;

其實上面的圖片並不是所有圖片都必須設定,具體要求可以直接檢視蘋果官方要求,例如這裡我們設定應用圖示和R4啟動圖片後具體效果如下(這裡使用的圖示取材來自微信):

appico

launch

上面我們新增了一個應用圖示和一個啟動圖片,在Images.xcassets上右鍵在Fiddler中檢視檔案內容並進入Images.xcassets資料夾,可以看到兩個子資料夾:AppIcon.appiconset和LaunchImage.launchimage,如下圖:

ImagesXcassets

兩個資料夾中分別存放了我們前面設定的圖片資源,除此之外還各有一個Contents.json檔案,在這個檔案中記錄的資源資訊,例如AppIcon.appiconset資料夾中的Contents.json內容如下,這裡記錄了每個圖示的大小名稱等資訊:

ContentsJson 

Info.plist

Info.plist檔案記錄了應用程式的配置資訊,如下圖:

InfoPlist

其實這些資訊我們可以在專案屬性中進行配置,效果和編輯這個文件是一樣的,大家可以對照檢視:

GeneralSetting 

Storyboard

到目前為止我們還沒有解釋我們的程式是如何顯示預設檢視介面的。做過WinForm程式的朋友都知道每個Window視窗介面都有一個設計器(對應一個設計檔案),其實在IOS中也可以通過設計工具設計介面不用編寫程式碼,這個工具就是Interface Builder。用Interface Builder編輯的檔案在iOS5之前是一個“.xib”檔案,從IOS5開始進行了改進,使用“.storyboard”檔案進行設計。其實在上面我們已經看到這個檔案,這裡重點說明一下Storyboard檔案的使用。

首先我們開啟Main.storyboard,此時可以看到一個Interface Builder介面設計器出現在我們眼前:

storyboard

在這個介面中整個核心就是右側檢視控制器ViewController,在ViewController中有一個檢視UIView,這個檢視用來放置其他使用者操作控制元件。檢視控制器左側的箭頭表示這個檢視控制器是個主檢視控制器,程式啟動之後預設就會直接顯示這個檢視控制器的檢視。我們可以在專案屬性中通過修改“Main Interface”屬性來修改主檢視控制器。

這裡我們不妨從Xcode右側工具欄Object Library中拖放一些元件在上面簡單完成一個登入佈局。

layout

要實現這個登入,那麼接下來就是事件和屬性繫結的問題,大家應該可以猜到登入的邏輯程式碼肯定在ViewController.m中編寫,那麼storyboard檔案是如何關聯到這個類的呢?如果我們在storyboard介面選中ViewController在Xcode右側切換到Identity Inspector檢視就會發現裡面當前設定的是ViewController類,通過這個設定Main.storyboard和ViewController關聯在一起。

那麼如何在程式碼中讀取兩個TextField的值並通過點選按鈕觸發相關事件驗證登入合法性呢?要想在程式碼中使用UITextField,並且新增按鈕點選事件,則必須在ViewController.h中定義兩個UITextField屬性和一個登入方法。

ViewController

上面程式碼我們需要解釋一下IBOutlet、IBAction,其餘程式碼和我們之前寫的ObjC沒有任何區別。

  • IBOutlet:IBOutlet沒有做任何操作,它的唯一作用就是告訴Interface Builder這個屬性可以被關聯到其中某個控制元件(在程式碼中可以看到程式碼前面多了空心圓點)。
  • IBAction:其實就是void,只是當你定義為IBAction在Interface Builder中可以關聯到某個控制元件的事件方法(後面的關聯操作將會看到,而且我們在程式碼中也可以看到程式碼前面多了空心圓點);

下面看一下storyboard中的控制元件和程式碼中定義的屬性和事件如何關聯。Xcode為我們提供了幾種方式來實現程式碼和storyboard控制元件的關聯:

1.拖拽控制元件到程式碼中(首先點選Xcode右上方“Show the Assistant editor”切換對應檢視,然後在程式碼中開啟.h檔案,同時開啟Interface Builder,按住ctrl鍵拖拽控制元件到程式碼中相應的位置進行關聯),關聯後屬性或方法前的空心圓變成實心表示已經關聯到具體控制元件(注意:事實上,從控制元件拖拽到程式碼時如果程式碼中沒有定義對應的屬性和方法Interface Builder會自動生成程式碼)。

controlToCode

2.從控制元件場景中拖拽控制元件到程式碼,關聯後屬性或方法前的空心圓變成實心表示已經關聯到具體控制元件。(在Interface Builder中點選左下角“Show Document outline”顯示控制元件結構樹,選中相應的控制元件按住Ctrl鍵拖拽到程式碼中的屬性或方法上)

controlSceneToCode

3. 在控制元件上右鍵找到對應的屬性或方法關聯到程式碼中對應的屬性或方法即可,關聯後屬性或方法前的空心圓變成實心表示已經關聯到具體控制元件。

propertyToCode.gif

4.與方法3類似,不再截圖,只是通過View Control Scene中的控制元件右鍵來關聯,關聯後屬性或方法前的空心圓變成實心表示已經關聯到具體控制元件。(在Interface Builder中點選左下角Show Document outline顯示控制元件結構樹,選中相應的控制元件右鍵拖拽到程式碼中的屬性或方法上)

5.對於前面幾種方法其實我們還可以直接從程式碼拖拽到控制元件上面,這裡簡單演示一種,其他情況大家可以自己試驗。

codeToControl.gif

 

如果要刪除關聯,可以採用第三、第四種方式在控制元件上右鍵,在關聯選單中找到對應的關聯刪除即可。此外需要注意對於一個控制元件而言可能有多個事件,當我們使用第三種或第四種方式直接選擇具體某個事件關聯到login:方法自然沒有問題,但是第一、第二、第五種方式沒有提示我們關聯到哪個事件而是使用一個控制元件的預設事件(對於UIButton就是Touch Up Inside事件)。

既然控制元件和程式碼屬性或方法已經做了關聯,這裡我們看一下具體效果,這裡簡單修改一下ViewController.m

//
//  ViewController.m
//  FirstIOS
//
//  Created by Kenshin Cui on 14-2-23.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController
            
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(void)login:(UIButton *)btn{
    if ([_phoneNumber.text isEqual:@"123"]&&[_password.text isEqual:@"456"]) {
        NSLog(@"登入成功!");
    }
}

@end

這裡實現了login:方法模擬登入的過程,可以發現當在手機號碼中輸入“123”,在密碼中輸入“456”點選登入會輸出”登入成功!“。

純程式碼實現iOS開發

storyboard進行介面設計固然不錯,特別是對於初學者經常會使用設計器進行介面設計,但是實際開發過程中我們很多情況下會直接使用程式碼進行介面佈局,特別是對於複雜的介面佈局更是如此。下面我們就從一個空專案建立一個類似於前面的登入介面。

直接在Xcode中建立“Empty Application”(注意在Xcode6中這個選項已經沒有了,這裡採用Xcode5.1),此時會發現已經沒有ViewController和storyboard檔案,我們需要手動建立一個檢視控制器(在專案中右鍵選擇Objective-c class,預設繼承自UIViewController,輸入類名:KCMainViewController即可)。

新建的檢視控制器預設情況下是無法載入到程式執行介面上的,此時需要在應用程式代理的程式載入完畢事件中手動載入並顯示我們的檢視。修改之前KCAppDelegate.m程式碼如下:

//  KCAppDelegate.m
//  IOSByCode
//
//  Created by Kenshin Cui on 14-2-23.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "KCAppDelegate.h"

@implementation KCAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application
{
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

- (void)applicationWillTerminate:(UIApplication *)application
{
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

@end

我們修改上面- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions的程式碼如下:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    //設定window屬性(在KCAppDelegate中定義的window屬性),初始化windows的大小和位置
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    //設定window的背景
    self.window.backgroundColor = [UIColor whiteColor];
    
    //初始化KCMainViewController
    KCMainViewController *mainController=[[KCMainViewController alloc]init];
    //設定自定義控制器的大小和window相同,位置為(0,0)
    mainController.view.frame=self.window.bounds;
    //設定此控制器為window的根控制器
    self.window.rootViewController=mainController;
    
    //設定window為應用程式主視窗並設為可見
    [self.window makeKeyAndVisible];
    return YES;
}

然後在我們自定義的KCMainViewController.m中新增一個UIImageView、兩個控制元件UITextField和一個UIButton控制元件,並且實現具體的登入方法。

KCMainViewController.h

//
//  KCMainViewController.h
//  IOSByCode
//
//  Created by Kenshin Cui on 14-2-23.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface KCMainViewController : UIViewController

#pragma mark logo
@property (nonatomic,strong) UIImageView *logo;
#pragma mark 手機號碼
@property (nonatomic,strong) UITextField *phoneNumber;
#pragma mark 密碼
@property (nonatomic,strong) UITextField *password;
#pragma mark 登入按鈕
@property (nonatomic,strong) UIButton *loginButton;

#pragma mark 點選事件
-(void)login:(UIButton *)btn;

@end

KCMainViewController.m

//
//  KCMainViewController.m
//  IOSByCode
//
//  Created by Kenshin Cui on 14-2-23.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "KCMainViewController.h"

@interface KCMainViewController ()

@end

@implementation KCMainViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    //self.view.backgroundColor=[UIColor redColor];
    
    //新增圖片
    CGRect logoRect=CGRectMake(100, 50, 100, 200);
    _logo=[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"LoginBackground.png"]];//設定圖片
    _logo.contentMode=UIViewContentModeScaleAspectFit;//設定內容填充模式
    _logo.frame=logoRect;//設定控制元件大小和位置(相對於父控制元件的位置)
    [self.view addSubview:_logo];//新增到KCMainViewController的View中
    
    //新增手機號碼輸入框
    CGRect phoneNumberRect=CGRectMake(20, 320, 280, 30);
    _phoneNumber=[[UITextField alloc]initWithFrame:phoneNumberRect];
    _phoneNumber.borderStyle=UITextBorderStyleRoundedRect;//設定文字框的邊框樣式
    [self.view addSubview:_phoneNumber];
    
    //新增密碼輸入框
    CGRect passwordRect=CGRectMake(20, 380, 280, 30);
    _password=[[UITextField alloc]initWithFrame:passwordRect];
    _password.borderStyle=UITextBorderStyleRoundedRect;
    [self.view addSubview:_password];
    
    //新增登入按鈕
    CGRect loginButtonRect=CGRectMake(10, 440, 300, 25);
    _loginButton=[[UIButton alloc]initWithFrame:loginButtonRect];
    [_loginButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];//設定標題內容顏色
    [_loginButton setTitle:@"登入" forState:UIControlStateNormal];//設定按鈕標題
    [_loginButton addTarget:self action:@selector(login:) forControlEvents:UIControlEventTouchUpInside];//新增點選事件
    [self.view addSubview:_loginButton];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(void)login:(UIButton *)btn{
    if ([_phoneNumber.text isEqual:@"123"]&&[_password.text isEqual:@"456"]) {
        NSLog(@"登入成功!");
    }else{
        NSLog(@"登入失敗!");
    }
}


@end

執行效果如下,與之前使用storyboard建立的介面類似,同時也能點選登入:

runUI

UIView

在這裡我們需要對上面的程式碼一樣解釋,在弄清上面的程式碼之前我們不得不熟悉一個UIKit中最重要的類UIView。

  1. UIView就是指介面可見的控制元件元素,所有的控制元件最終都繼承自UIView,UIView中還可以新增其他UIView(通過addSubView方法);
  2. 在一個iOS應用中必須有一個主視窗UIWindow(理論上也可以有多個UIWindow但是隻有一個是主Window,而且只有主Window可以和使用者互動),UIWindow也是繼承自UIView,它擁有UIView的所有屬性、方法;
  3. 在UIWindow中必須有一個根控制器,這個控制器距離UIWindow是最近的;設定一個控制器為根控制器和直接通過addSubView新增控制器的檢視(view屬性)到window並不完全一樣(例如如果僅僅新增控制器檢視那麼應用雖然可以顯示但是不支援旋轉);
  4. UIViewController是檢視控制器,主要用來控制UIView,在UIViewController內部有一個UIView(view屬性);

在上面的程式碼中我們首先在應用程式載入完畢之後載入一個UIWindow物件,同時把我們的檢視控制器KCMainController設定為UIWindow的根檢視控制器,然後設定這個UIWindow為主視窗並可見。當主視窗設定為可見過程中會呼叫檢視控制器的loadView方法來載入檢視(注意檢視控制器的loadView方法是延遲載入的,第一次呼叫檢視控制器的view屬性才會呼叫此方法;由於makeKeyAndVisible方法中會使用檢視控制器的view屬性所以此時會呼叫檢視控制器的loadView方法),檢視載入完之後呼叫viewDidLoad方法,在這個方法中我們新增登入相關控制元件並將這些控制元件載入到檢視控制器KCMainViewController的檢視view中。

下面我們看一下應用程式最終的佈局,相信通過這張圖大家對於iOS的佈局會有一個大致瞭解:

iOSLayout

補充知識點

1.iOS尺寸設定--在iOS中尺寸的單位是點不是畫素,在retina螢幕中一個點有兩個畫素。此外在retina螢幕中圖片一般採用”xxx@2x.png”命名,在程式碼中使用時只需要寫成“xxx.png”程式會自動根據螢幕型別在retain螢幕下使用”xxx@2x.png”圖片,在非retain螢幕下采用”xxx.png”圖片。                                                   

ScreenSize

2.應用程式影象資源尺寸--其實關於圖片尺寸規定我們可以直接檢視Xcode自帶幫助文件,例如可以檢視“Icon and Image Sizes”一節獲得圖片大小說明:

IconSize

關於iOS圖示命名這裡不再贅述,蘋果官方也給出了具體的程式碼示例:Application Icons and Launch Images for iOS

3.模擬器檔案儲存的位置--模擬器中為什麼可以執行我們的程式,程式到底在什麼位置?

這些檔案其實在Mac中 OS X中是隱藏的,首先通過“defaults write com.apple.finder AppleShowAllFiles -bool true”命令顯示隱藏檔案(關閉隱藏檔案顯示通過“defaults write com.apple.finder AppleShowAllFiles -bool false”命令),然後到“/Users/kenshincui/Library/Application Support/iPhone Simulator/7.1/Applications”資料夾中會看到很多GUID命名的資料夾,只要一個一個檢視就可以找到我們的程式。模擬器執行時會載入這個資料夾中的應用程式包顯示到模擬器中。

bundle

可以看到在上圖中有一個IOSByCode的應用程式包,我們可以通過“顯示包內容”檢視具體程式資源:

bundleContent

4.UIApplication--前面一直提到UIApplication物件,這個物件在iOS中是一個單例,我們通過[UIApplication sharedApplication]獲得(注意在iOS開發中一般以shared開頭的物件都是單例)。這裡列舉一些UIApplication的常用方法:

UIApplicationMethods

例如呼叫applicationIconBadgeNumber方法之後效果如下:

badgeNumber

5.組織標示--前面我們在新建專案中有一個“Organization Identifier”是做什麼的呢?它是組織唯一標示,一般我們會使用公司的域名形式(這個域名一般會倒序書寫,例如公司域名為:www.cmjstudio.com,我們這裡就寫成com.cmjstudio),和專案名稱共同組成一個程式的唯一標示“Bundle Identifier”,這個標示在整個App Store中是唯一的,如果兩個應用程式標示完全一樣,那麼安裝時會先解除安裝前面的程式再安裝新程式。

相關文章