2015年2月16日 星期一

UIApplication初探

本文分享如何使用UIApplication,每個App都會有一個UIApplication實體,不需要建立,而是透過sharedApplication方法取得。

取得UIApplication實體


var currentApp = UIApplication.sharedApplication();

Local and Push Notification


當App在背景執行時,也能主動通知使用者,此為Local and Push Notification;因需較為深入探討,再撰寫專文介紹,可先參考如下連結。

開啟超連結


要在App中開啟超連結,可以用openURL方法,參考以下程式碼。

let url = "http://google.com"

let param:NSURL = NSURL(string:url)!

UIApplication.sharedApplication().openURL(param)

除了開啟超連結外,也可以用此方法來打電話、傳簡訊、FaceTime…等;詳細可參考Apple URL Scheme Reference,整理範例如下。

Links類型
範例
Email
mailto:web@kunhsiang.com
Phone
tel:1-408-555-5555
FaceTime
facetime://user@example.com
SMS
sms:1-408-555-1212
Map
http://maps.apple.com/?daddr=San+Francisco,+CA&saddr=cupertino
iTunes
http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewAlbum?i=156093464&id=156093462&s=143441
Youtube
http://www.youtube.com/watch?v=VIDEO_IDENTIFIER

openURL不僅可以開啟常用的應用程式,它更是應用程式間溝通的方式,我們可以自訂URL用來開啟別的App並且傳遞參數,以下面URL其中todolist是代表的應用程式名稱,後面是參數,詳細可參考Using URL Schemes to Communicate with Apps

todolist://www.acme.com?Quarterly%20Report#200806231300

移除Statusbar


可以將App上方的Statusbar移除,方法有兩種,一種是從Application的角度來實作,另一種則是則從ViewController角度來實作。

從Application角度來實作,先在Application加入Key為View controller-based status bar appearance,Value為No;然後在AppDelegate加入以下程式碼。

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
       application.statusBarHidden = true
       return true
}

從ViewController角度來實作較為簡單,直接在ViewController加入程式碼。

override func prefersStatusBarHidden() -> Bool {
   return false
}

或是

override func  preferredStatusBarStyle() -> UIStatusBarStyle {
       return UIStatusBarStyle.LightContent
}

應用程式狀態


UIApplication物件可以用applicationState方法取得目前App的狀態,分為Active、InActive及Background;簡單來說Active是App在前景執行,並且能接收事件、InActive也是在前景執行,但是無法接收事件,可能被來電中斷或是正在執行某段程式碼正在等待中、Background則是按下Home鍵後App會在背景執行。

var AppStatus = ""
       
if (currentApp.applicationState == UIApplicationState.Active)
{
 AppStatus = "Application Status:Active"
}
else if (currentApp.applicationState == UIApplicationState.Inactive)
{
 AppStatus = "Application Status:Inactive"
}
else if (currentApp.applicationState == UIApplicationState.Background)
{
 AppStatus = "Application Status:Background"
}

搖晃偵測


UIApplication可用來設定是否能偵測搖晃,屬性如下。

app.applicationSupportsShakeToEdit = YES;

但是實際上可從ViewController來實作為佳,參考以下程式碼。

   override func viewDidAppear(animated: Bool) {
       super.viewDidAppear(animated)
       self.becomeFirstResponder()
   }
   
   override func canBecomeFirstResponder() -> Bool {
       return true
   }
   
   override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent) {
       if(event.subtype == UIEventSubtype.MotionShake) {
           var alert = UIAlertController(title: "Shaken",
               message: "Not Stirred",
               preferredStyle: UIAlertControllerStyle.Alert)
           alert.addAction(UIAlertAction(title: "OK",
               style: UIAlertActionStyle.Default, handler: nil))
           self.presentViewController(alert, animated: true, completion: nil)
       }
   }


關閉自動睡眠


iPhone/iPad在一段時間未使用時,因為省電的原因會自動進入睡眠狀態,若要關閉自動睡眠功能可用以下屬性。

application.idleTimerDisabled = true

網路狀態指示


可顯示或移除網路狀態指示,運用以下屬性。

application.networkActivityIndicatorVisible = false

iOS App應用程式生命週期(下)

The Main Run Loop

App的main run loop用來處理所有使用者相關的事件,UIApplication物件在開始時會設定main run loop,用來處理事 bv件及畫面的更新;就像名稱所提及,main run loop在App的main thread執行。這樣的機制確保使用者相關的事件,能夠循序的在程式中處理。下圖說明了man run loop的流程。
使用者與iPhone/iPad互動時,系統會根據使用者的互動產生相關事件,並由UIKit傳遞給App。
事件在內部會放到Event queue,並依序傳給main run loop去執行; UIApplication 是第一個接受到這些事件的物件,並且決定誰會處理這些事件。以Touch event為例通常會派送到main window物件,然後會送到發生事件的view上,其他事件也是用類似方式。

Processing events in the main run loop







常見事件可以看到下表,大部份的事件用main run loop傳遞到app,但是一些則不是;一些事件是透過App開發者提供的委託物件或是程式碼片段。詳細可參考Event Handling Guide for iOS
事件類別
傳遞給...
註解
Touch
事件發生時的view object
當觸碰螢幕時會發生事件,最初接觸到的Views 是responder objects,當第一個接觸到的未處理,會轉給上層的responder objects來處理。
Remote control
Shake motion events
Remote control events是由耳機按鈕或其他配件所產生的事件;Shake motion events則是使用者搖晃事件。
Accelerometer
Magnetometer
Gyroscope
開發者指定的物件
Accelerometer加速感應器可用來精確測量出步行和跑步的距離、Magentometer則是磁力儀,用來偵測附近的磁場、Gyroscope陀螺儀用來指向固定方向,可用來實作指南針。
Location

開發者指定的物件
使用Core Location framework來接受location events,更多的資訊可參考Location and Maps Programming Guide
Redraw


當View需要更新
重繪事件(Redraw events)不會引發事件相關物件,只是簡單呼叫view去重畫它自己。詳細可以參考Drawing and Printing Guide for iOS

一些事件,像是touch及remote control事件,會由App的responder objects來處理;Responder objects在App中隨處可見,像是UIApplication object、view objects及view controller objects。
大部份事件會傳遞給特定的Responder object,但是也可傳遞給其他的Responder objects(稱之為responder chain)。
控制項(像是Buttons)的Touch事件的處理方式和其他類型的Views不一樣,通常只有固定的幾項互動在這控制項,這些互動會被包裝成Action Messages並傳遞到正確的Target Object。Target-action design pattern讓控制項很容易去觸發在App的自訂程式碼。

Apps 執行狀態

狀態
描述
Not running
App尚未執行。
Inactive
App正在前景執行但是沒有接受事件(可能正在執行其他程式碼);App通常只會在此狀態一小段時間,就會轉為其他狀態。
Active
App正在前景執行,也正在接受事件。
Background
App正在背景執行程式碼,大部份App短暫進入此狀態後就會進入暫停狀態;對於如何在背景狀態執行程式碼,可以參考Background Execution
Suspended
App正在背景但是沒有執行程式碼,系統會自動將Apps進入此狀態,並且不會通知它們。當進入暫時狀態時,App仍然在記憶體中,但是不會執行任何程式碼。當記憶體不足時,系統可能會不通知而直接中斷暫停的App,為了提供更多的記憶體空間給前景App。


大部份App狀態轉換是透過呼叫app delegate object的方法來達成;這些方法是程式撰寫者的機會去處理這些轉換,這些方法簡單說明如下。

App Termination

App必須要準備好隨時被使用者或系統所結束,而且不能等待儲存使用者資料或做其他重要的工作;由系統所引發的結束是App應用程式生命週期正常的一部份。
系統通常會終止App以釋放更多的記憶體空間給其他的應用程式使用,暫停的Apps被中止時不會接受到任何的通知。
假設App正在背景執行,並且尚未暫停,若要中止系統會呼applicationWillTerminate方法。當系統重啟時也不會呼叫此方法。
除了系統中止App外,使用者也可以直接使用介面來中止App;由使用者所中止的App就和系統中止暫停的App是一樣的,App並不會接受到任何的通知。 

Threads and Concurrency

系統建立App的main thread,而開發者建立額外的thread用來去執行其他工作;就iOS Apps,較傾向的方式是用Grand Central Dispatch (GCD),GCD的觀念是讓你去定義你想做的工作,以及這些工作想執行的優先次序,但是由系統來實際決定最佳的執行方式,而不是自行建立及管理thread。讓系統來管理thread可以簡化開發者要撰寫的程式碼,並且也會有較佳的程式碼正確性及系統效能。當考慮到threads及concurrency,想像以下的情況。
  • 操作Views、Core Animation及許多其他UIKit類別時通常會在app的主thread進行操作,但是有些例外,例如以影像為基礎的操作常會在背景thread。
  • 長時間的工作應該總是在背景thread執行,例如網路存取、檔案存取、大量資料存取,應用GCD來做同步處理。
  • 在App啟動時,盡可能不要在main thread執行任務;在App啟動時應該快將使用者介面設定好。在main thread應該只執行設定使用者介面相關動作,其他任務應在其他的thread採用同步執行。

關於更多GCD同步的技術可以參考Concurrency Programming Guide

2015年1月26日 星期一

iOS App應用程式生命週期(上)

App的組成可視為撰寫程式碼(Custom Code)系統架構(System Frameworks)的互動,系統架構提供基本的運作模式,我們撰寫程式碼(Custom Code)讓App依照我們想要的方式去運作。若要寫好一個App,我們就要深入瞭解系統架構(System Frameworks)。

Main Function

每個C應用程式都會一個main function的進入點,iOS app也是一樣;不一樣的是iOS app的main function不用自行撰寫。XCode會在建立專案時自動產生main function,下面是main function示意程式碼。

import UIKit
func  main(argc:Int, argv:[String]) -> String
{
     return UIApplicationMain(argc, argv, nil, NSStringFromClass(new AppDelegate()));
}
main function主要會呼叫UIApplicationMain函式,UIApplicationMain函式主要會建立App的核心物件、從storyboard載入使用者介面、呼叫自行撰寫的程式碼,有機會做初始化及捕捉使用者的動作。開發者主要提供的是storyboard檔案及初始化程式碼。

App的結構

應用程式啟動時UIApplicationMain函式會建立關鍵物件並讓App執行;對每個iOS App最關鍵的物件是UIApplication,它主要的工作是協調系統與App中的物件之間的互動。下圖顯示每個App會出現的關鍵物件,以及它們之間的互動,而下表列出了每個物件的主要角色。
首先需要注意的是是iOS Apps使用Model-View-Controller架構;這個模式將應用程式資料及商業邏輯從使用者介面中分離;這樣設計很重要的好處是可讓Apps能在不同機裝置及不同螢幕畫面上執行。


物件
描述
UIApplication物件管理事件迴圈及其它高階應用程式行為,它也會反應用程式關鍵的轉換及特殊事件(例如push notifications)給在應用程式中自訂的物件來處理。每個iOS App只會有一個UIApplication物件。
App delegate物件
UIApplication物件是掌握iOS App的核心物件,而App delegate則是撰寫自訂程式碼的核心物件,App delegate與UIApplication物件一同合作,處理應用程式啟始、應用程式狀態轉換及其它App事件。App delegate一定會在每個App中出現,所以常用它來初始化App需要的資料結構。
Documents and data model objects
資料模型物件(Data model objects)用來儲存App專屬資料,舉例來說銀行App會儲存財務交易記錄、繪畫App會儲存使用者筆畫及順序資料。App可以使用document objects來管理資料模型物件,這是提供個方便的方法來管理,但並不是必要性的,開發者也可用自己的方式來管理。詳細可以參考Document-Based App Programming Guide for iOS
View controller objects
View controller 物件管理螢幕上呈現的App內容,一個View Controller管理單一View及它的子集合。實際呈現內容時View Controller會讓View狀態成為可見的,在App的視窗呈現。

UIViewController類別是所有View Controller物件的基礎類別,它提供了基本的功能包括載入Views、顯示它們,根據裝置旋轉而旋轉螢幕畫面,以及其他標準系統行為。

UIKit及其他Framework會定義其他的View Controller類別去實作其他的介面包括Image Picker、Tab Bar Interface及Navigation Interface。
若需要更多的資訊如何使用View Controllers,可參考 View Controller Programming Guide for iOS
UIWindowobject
UIWindow件用來代表視窗,協調一到多個View在主畫面中呈現,大部份的Apps只會有一個視窗。要改變App的內容,使用View Controller來改變顯示的View,並在對應的Windows上呈現;我們不會改變Windows本身。

除了代管Views外,UIWindow也與UIApplication合作將事件傳遞給Views及View controlls。
View objects,control objects, and layer objects
Views及Controls是實際顯示App內容的元素,Views物件用來在特定區域內顯示繪畫內容,並且會對於該區內的事件反應;Controls則是對於特定的使用者動作進行回應,像是Buttons、Text Fields…等。

UIKit Framework提供了許多標準的Views用來顯示內容,您也可以定義繼承自UIView用來定義自己的Views。