適用於iOS的應用程式程式設計指南(七)

weixin_34353714發表於2017-05-16

在您的應用程式中啟用狀態儲存和恢復

狀態儲存和恢復不是自動功能,應用程式必須選擇使用它。應用程式通過在其應用程式委託中實施以下方法來表明他們對該功能的支援:

application:shouldSaveApplicationState:

application:shouldRestoreApplicationState:

通常,這些方法的實現只是返回YES,表示可以發生狀態儲存和恢復。但是,要在不發生操作的情況下,有條件地保留和恢復狀態的應用程式可以返回NO。例如,在釋出對您的應用程式的更新後,您可能希望從應用程式返回NO:application:shouldRestoreApplicationState:方法,如果您的應用程式無法從先前版本有效地還原狀態。

儲存您的檢視控制器的狀態

儲存應用程式檢視控制器的狀態應該是您的主要目標。檢視控制器定義使用者介面的結構。他們管理呈現該介面所需的檢視,並協調獲取和設定支援這些檢視的資料。要保留單個檢視控制器的狀態,您必須執行以下操作:

(必需)將修復識別符號分配給檢視控制器;請參閱標記您的檢視控制器進行儲存。Marking Your View Controllers for Preservation.

(必需)提供在啟動時建立或定位新的檢視控制器物件的程式碼;請參閱在啟動時恢復檢視控制器。Restoring Your View Controllers at Launch Time

(可選)實現encodeRestorableStateWithCoder:和decodeRestorableStateWithCoder:編碼和恢復在後續啟動期間無法重新建立的任何狀態資訊的方法;請參閱編碼和解碼檢視控制器的狀態。Encoding and Decoding Your View Controller’s State

標記您的檢視控制器進行儲存

UIKit僅保留其restoreIdentifier屬性包含有效字串物件的檢視控制器。對於您知道要保留的檢視控制器,在初始化檢視控制器物件時,請設定此屬性的值。如果從故事板或nib檔案載入檢視控制器,則可以在其中設定恢復識別符號。

為恢復識別符號選擇合適的值很重要。在恢復過程中,您的程式碼使用恢復識別符號來確定要檢索或建立哪個檢視控制器。如果每個檢視控制器物件都基於不同的類,則可以使用類名作為恢復識別符號。但是,如果檢視控制器層次結構包含同一類的多個例項,則可能需要根據每個檢視使用情況選擇不同的名稱。

當它要求您提供檢視控制器時,UIKit將為您提供檢視控制器物件的恢復路徑。恢復路徑是從根檢視控制器開始並沿著檢視控制器層次結構走向當前物件的恢復識別符號的序列。例如,假設您有一個標籤欄控制器,其恢復識別符號為TabBarControllerID,第一個選項卡包含一個導航控制器,其識別符號為NavControllerID,其根檢視控制器的識別符號為MyViewController。根檢視控制器的完整恢復路徑為TabBarControllerID/ NavControllerID / MyViewController。

每個物件的恢復路徑必須是唯一的。如果檢視控制器有兩個子檢視控制器,則每個子節點必須具有不同的恢復識別符號。然而,具有不同父物件的兩個檢視控制器可以使用相同的恢復識別符號,因為其餘的恢復路徑提供了所需的唯一性。一些UIKit檢視控制器(如導航控制器)會自動消除其子檢視控制器的歧義,從而允許您為每個子級使用相同的恢復識別符號。有關給定檢視控制器的行為的更多資訊,請參閱相應的類引用。

在還原時,您可以使用提供的恢復路徑來確定哪個檢視控制器返回到UIKit。有關如何使用恢復識別符號和恢復路徑恢復檢視控制器的更多資訊,請參閱在啟動時恢復檢視控制器。Restoring Your View Controllers at Launch Time

在啟動時恢復您的檢視控制器

在恢復過程中,UIKit詢問您的應用程式建立(或定位)構成您保留的使用者介面的檢視控制器物件。嘗試查詢檢視控制器時,UIKit將遵循以下過程:

如果檢視控制器具有恢復類,則UIKit將要求該類提供檢視控制器。 UIKit呼叫關聯的恢復類的viewControllerWithRestorationIdentifierPath:coder:方法來檢索檢視控制器。如果該方法返回nil,則假定應用程式不想重新建立檢視控制器,並且UIKit將停止尋找它。

如果檢視控制器沒有恢復類,則UIKit會嚮應用程式委託提供檢視控制器。 UIKit呼叫應用程式:viewControllerWithRestorationIdentifierPath:您的應用程式委託的coder:方法來查詢沒有恢復類的檢視控制器。如果該方法返回nil,則UIKit將嘗試隱式查詢檢視控制器。

如果具有正確恢復路徑的檢視控制器已存在,則UIKit將使用該物件。如果您的應用程式在啟動時建立檢視控制器(以程式設計方式或通過從資原始檔載入它們)並將恢復識別符號分配給它們,則UIKit將通過其恢復路徑隱式地找到它們。

如果檢視控制器最初從故事板檔案載入,則UIKit將使用儲存的故事板資訊來定位並建立它。 UIKit將關於檢視控制器的故事板的資訊儲存在恢復存檔中。在還原時,如果沒有通過任何其他方式找到檢視控制器,它將使用該資訊來定位相同的故事板檔案並例項化相應的檢視控制器。

值得注意的是,如果為檢視控制器指定了恢復類,UIKit不會隱含地找到您的檢視控制器。如果您的還原類的viewControllerWithRestorationIdentifierPath:coder:方法返回nil,UIKit將停止嘗試找到您的檢視控制器。這可以控制您是否真的要建立檢視控制器。如果您沒有指定恢復類,則UIKit會盡力為您找到檢視控制器,並根據需要從應用程式的故事板檔案中進行建立。

如果您選擇使用還原類,則您的viewControllerWithRestorationIdentifierPath:coder:method的實現應該建立一個類的新例項,執行一些最小化的初始化,並返回生成的物件。清單5-1顯示瞭如何使用此方法從故事板載入檢視控制器的示例。因為檢視控制器最初是從故事板載入的,所以該方法使用UIStateRestorationViewControllerStoryboardKey鍵從存檔中獲取故事板。請注意,此方法不會嘗試配置檢視控制器的資料欄位。當檢視控制器的狀態被解碼時,該步驟發生。

清單5-1在恢復期間建立新的檢視控制器

+ (UIViewController*)viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents

coder:(NSCoder *)coder {

MyViewController* vc;

UIStoryboard* sb = [coderdecodeObjectForKey:UIStateRestorationViewControllerStoryboardKey];

if (sb) {

vc = (PushViewController*)[sbinstantiateViewControllerWithIdentifier:@"MyViewController"];

vc.restorationIdentifier =[identifierComponents lastObject];

vc.restorationClass =[MyViewController class];

}

return vc;

}

重新分配恢復識別符號和恢復類,如上例所示,是建立新的檢視控制器時採用的習慣。恢復恢復識別符號的最簡單的方法是抓取identifierComponents陣列中的最後一個專案並將其分配給檢視控制器。

對於在啟動時已從應用程式的主要故事板檔案載入的物件,請勿建立每個物件的新例項。相反,實現應用程式:viewControllerWithRestorationIdentifierPath:您的應用程式委託的方法,並使用它來返回相應的物件或讓UIKit隱式查詢這些物件。

編碼和解碼您的檢視控制器的狀態

對於要儲存的每個物件,UIKit呼叫物件的encodeRestorableStateWithCoder:方法,以便有機會儲存其狀態。在解碼過程中,對decodeRestorableStateWithCoder:方法進行匹配呼叫,以對該狀態進行解碼並將其應用於物件。這些方法的實現是可選的,但建議您的檢視控制器使用。您可以使用它們來儲存和恢復以下型別的資訊:

引用顯示的任何資料(不是資料本身)

對於容器檢視控制器,對其子檢視控制器的引用

有關當前選擇的資訊

對於具有使用者可配置檢視的檢視控制器,有關該檢視的當前配置的資訊。

在編碼和解碼方法中,您可以編碼編碼器支援的任何值,包括其他物件。對於除檢視和檢視控制器之外的所有物件,物件必須採用NSCoding協議,並使用該協議的方法來寫入其狀態。對於檢視和檢視控制器,編碼器不使用NSCoding協議的方法來儲存物件的狀態。相反,編碼器儲存物件的恢復識別符號並將其新增到可儲存物件的列表中,這導致該物件的encodeRestorableStateWithCoder:方法被呼叫。

encodeRestorableStateWithCoder:和decodeRestorableStateWithCoder:您的檢視控制器的方法應始終在其實現中的某個時刻呼叫super。呼叫super方法使父類有機會儲存和恢復任何其他資訊。清單5-2顯示了儲存用於標識指定檢視控制器的數值的這些方法的示例實現。

清單5-2編碼和解碼檢視控制器的狀態。

- (void)encodeRestorableStateWithCoder:(NSCoder*)coder {

[superencodeRestorableStateWithCoder:coder];

[coder encodeInt:self.numberforKey:MyViewControllerNumber];

}

- (void)decodeRestorableStateWithCoder:(NSCoder*)coder {

[superdecodeRestorableStateWithCoder:coder];

self.number = [coderdecodeIntForKey:MyViewControllerNumber];

}

編碼物件在編碼和解碼過程中不共享。每個具有可保護狀態的物件都可以接收它自己的編碼器,它可以用來讀取或寫入資料。使用獨特的編碼器意味著您不必擔心自己的物件之間的關鍵名稱空間衝突。但是,您仍然必須避免使用UIKit提供的一些特殊鍵名稱。具體來說,每個編碼器都包含UIApplicationStateRateorationBundleVersionKey和UIApplicationStateRestorationUserInterfaceIdiomKey鍵,它們提供有關bundle版本和當前使用者介面成語的資訊。與檢視控制器相關聯的編輯器還可以包含UIStateRestorationViewControllerStoryboardKey鍵,它標識該檢視控制器來源的故事板。

有關為檢視控制器實現編碼和解碼方法的更多資訊,請參閱UIViewController類參考。UIViewController Class Reference.

顯示你的檢視狀態

如果檢視具有值得保留的狀態資訊,則可以將該狀態與應用程式的其他檢視控制器儲存在一起。因為它們通常由他們擁有的檢視控制器配置,所以大多數檢視不需要儲存狀態資訊。您唯一需要儲存檢視狀態的時間是檢視本身可以由使用者以獨立於其資料或擁有檢視控制器的方式進行更改。例如,滾動檢視儲存當前滾動位置,這是檢視控制器不感興趣的資訊,但這影響了檢視呈現的方式。

要指定檢視的狀態應該儲存,請執行以下操作:

為檢視的restoreIdentifier屬性分配一個有效的字串。

使用檢視控制器的檢視也具有有效的恢復識別符號。

對於表檢視和集合檢視,分配一個採用UIDataSourceModelAssociation協議的資料來源。

與檢視控制器一樣,將恢復識別符號分配給檢視會告訴系統檢視物件具有應用程式要儲存的狀態。還可以使用恢復識別符號來定位檢視。

像檢視控制器一樣,檢視定義了對自定義狀態進行編碼和解碼的方法。如果建立狀態值得儲存的檢視,則可以使用這些方法來讀取和寫入任何相關資料。

具有可儲存狀態的UIKit檢視

為了儲存任何檢視的狀態(包括自定義和標準系統檢視),您必須為檢視分配恢復識別符號。沒有恢復識別符號的檢視沒有被UIKit新增到可儲存物件的列表中。

以下UIKit檢視具有可以保留的狀態資訊:

UICollectionView

UIImageView

UIScrollView

UITableView

UITextField

UITextView

UIWebView

其他框架也可以具有可儲存狀態的檢視。有關檢視是否儲存狀態資訊和儲存什麼狀態的資訊,請參閱相應類的參考。

保留自定義檢視的狀態

如果您正在實現具有可恢復狀態的自定義檢視,請執行encodeRestorableStateWithCoder:和decodeRestorableStateWithCoder:方法,並使用它們對該狀態進行編碼和解碼。使用這些方法只儲存不能通過其他方式重新配置的資料。例如,使用這些方法來儲存使用者與檢視互動修改的資料。不要使用這些方法來儲存檢視中呈現的資料或者擁有檢視控制器可以輕鬆配置的任何資料。

清單5-3顯示瞭如何保留和還原包含可編輯文字的自定義檢視的選擇的示例。在該示例中,可以使用selectionRange和setSelectionRange:方法訪問該範圍,該方法是檢視用於管理選擇的自定義方法。對資料進行編碼僅需要將其寫入提供的編碼器物件。恢復資料需要閱讀並將其應用於檢視。

清單5-3儲存自定義文字檢視的選擇

// Preserve the text selection

- (void) encodeRestorableStateWithCoder:(NSCoder*)coder {

[superencodeRestorableStateWithCoder:coder];

NSRange range = [selfselectionRange];

[coder encodeInt:range.lengthforKey:kMyTextViewSelectionRangeLength];

[coder encodeInt:range.locationforKey:kMyTextViewSelectionRangeLocation];

}

// Restore the text selection.

- (void) decodeRestorableStateWithCoder:(NSCoder*)coder {

[superdecodeRestorableStateWithCoder:coder];

if ([coder containsValueForKey:kMyTextViewSelectionRangeLength]&&

[codercontainsValueForKey:kMyTextViewSelectionRangeLocation]) {

NSRange range;

range.length = [coderdecodeIntForKey:kMyTextViewSelectionRangeLength];

range.location = [coderdecodeIntForKey:kMyTextViewSelectionRangeLocation];

if (range.length > 0)

[selfsetSelectionRange:range];

}

}

實施易於維護的資料來源

因為表或集合檢視顯示的資料可以更改,所以只有當它們的資料來源實現了UIDataSourceModelAssociation協議時,兩個類都儲存有關當前選擇和可見單元的資訊。此協議為表或集合檢視提供了一種識別其所包含內容的方式,而不依賴該內容的索引路徑。因此,無論資料來源在下一個啟動週期中放置專案的位置,該檢視仍然具有查詢該專案所需的所有資訊。

為了成功實現UIDataSourceModelAssociation協議,您的資料來源物件必須能夠識別應用程式後續啟動時的專案。這意味著您設計的任何識別方案對於給定的資料必須是不變的。這是至關重要的,因為資料來源必須能夠在每次請求時為相同識別符號檢索同一條資料。實現協議本身是從資料項對映到其唯一ID並再次返回的問題。

使用Core Data的應用程式可以通過利用物件識別符號來實現協議。 Core Data儲存中的每個物件都有一個唯一的物件識別符號,可以將其轉換為URI,並用於稍後查詢物件。如果您的應用程式不使用CoreData,則如果要支援檢視的狀態保留,則需要設計自己的唯一識別符號形式。

注意:請記住,實現UIDataSourceModelAssociation協議只需要保留屬性,例如表或集合檢視中的當前選擇。此協議不用於保留由資料來源管理的實際資料。您的應用程式有責任確保其資料在適當的時間儲存。

保護您的應用程式的高階別狀態

除了由應用程式的檢視控制器和檢視保留的資料之外,UIKit還提供了鉤子來儲存應用程式所需的任何其他資料。具體來說,UIApplicationDelegate協議包括以下方法來覆蓋:

應用:willEncodeRestorableStateWithCoder:

應用:didDecodeRestorableStateWithCoder:

如果您的應用程式不包含在檢視控制器中的狀態,但需要保留的狀態,則可以使用上述方法儲存並還原它。應用程式:willEncodeRestorableStateWithCoder:方法在儲存過程的開始時被呼叫,以便您可以寫出任何高階應用程式狀態,如當前版本的使用者介面。應用程式:didDecodeRestorableStateWithCoder:方法在恢復狀態結束時呼叫,以便您可以解碼任何資料並執行應用程式所需的最終清理。

儲存和恢復狀態資訊的技巧

當您為您的應用程式新增對狀態儲存和恢復的支援時,請考慮以下準則:

編輯版本資訊以及您應用程式的其餘狀態。在儲存過程中,建議您編碼用於標識應用程式使用者介面當前版本的版本字串或數字。您可以在應用程式中編碼此狀態:willEncodeRestorableStateWithCoder:應用程式委託的方法。當您的應用程式委託應用程式:shouldRestoreApplicationState:方法被呼叫時,您可以從提供的編碼器檢索該資訊,並使用它來確定狀態是否可以儲存。

不要在您的應用程式的狀態中包含資料模型中的物件。應用程式應繼續將其資料單獨儲存在iCloud或磁碟上的本地檔案中。切勿使用狀態恢復機制來儲存資料。如果在恢復操作期間出現問題,則可以刪除保留的介面資料。因此,您寫入磁碟的任何儲存相關資料都應被視為可清除的。

狀態儲存系統期望您以設計使用方式使用檢視控制器。檢視控制器層次結構通過檢視控制器包含的組合和通過從另一個呈現一個檢視控制器來建立。如果您的應用程式通過其他方式顯示檢視控制器的檢視,例如,通過將其新增到另一個檢視中,而不在相應的檢視控制器之間建立一個包含關係,則儲存系統將無法找到您的檢視控制器來保留檢視控制器。

請記住,您可能不想保留所有檢視控制器。在某些情況下,保留檢視控制器可能沒有意義。例如,如果使用者在顯示檢視控制器以更改使用者密碼時離開了您的應用程式,則可能需要取消操作並將應用程式還原到上一螢幕。在這種情況下,您不會保留要求輸入新密碼資訊的檢視控制器。

避免在恢復過程中交換檢視控制器類。狀態儲存系統編碼它保留的檢視控制器的類。在恢復期間,如果您的應用程式返回其類與主物件不匹配(或不是子類)的物件,則系統不會要求檢視控制器解碼任何狀態資訊。因此,將舊檢視控制器交換出完全不同的檢視控制器不會恢復物件的完整狀態。

當使用者強制退出應用程式時,系統會自動刪除應用程式的保留狀態。當應用程式被殺死時,刪除保留的狀態資訊是一種安全預防措施。(為了安全起見,如果應用程式在啟動期間崩潰了兩次,系統也會刪除保留的狀態。)如果要測試應用程式恢復其狀態的能力,則不要在除錯過程中使用多工欄來殺死應用程式。相反,使用Xcode殺死應用程式或通過安裝臨時命令或手勢需呼叫退出方式以程式方式殺死應用程式。

開發VoIP應用程式的技巧

網際網路語音協議(VoIP)應用允許使用者使用因特網連線而不是裝置的蜂窩服務進行電話呼叫。在iOS8及更高版本中,您可以使用Apple Push Notification服務(APN)和PushKit框架的API來建立VoIP應用程式。依靠推送通知啟用VoIP功能意味著您的應用程式不必維護與相關服務的持續網路連線或配置用於VoIP使用的套接字。當VoIP推送通知到達時,您的應用程式有時間處理通知,即使該應用程式目前已被終止。

注意:VoIP推送通知僅傳送到執行iOS 8或更高版本的裝置。如果您需要支援執行早期版本的iOS的裝置,那麼您需要維護相容的實現。

與任何背景音訊應用程式一樣,VoIP應用程式的音訊會話必須正確配置,以確保應用程式與其他基於音訊的應用程式平滑執行。因為VoIP應用程式的音訊播放和記錄不會一直使用,所以特別重要的是,只有在需要時才能建立和配置應用程式的音訊會話物件。例如,您將建立音訊會話以通知使用者來電或使用者實際在通話中。一旦通話結束,您將刪除對音訊會話的強引用,併為其他音訊應用程式提供播放音訊的機會。

有關如何配置和管理VoIP應用程式的音訊會話的資訊,請參閱音訊會話程式設計指南Audio Session Programming Guide。要了解有關使用VoIP推送通知和PushKitAPI建立VoIP應用程式的更多資訊,請參閱iOS Apps的“能效指南”。Energy Efficiency Guide for iOS Apps

實施VoIP應用程式有以下幾個要求:

為您的應用啟用IP語音背景模式。 (因為VoIP應用程式涉及音訊內容,建議您還啟用Audio和AirPlay背景模式。)您可以在Xcode專案的Capabilities選項卡中啟用後臺模式。

使用PushKit API註冊接收VoIP推送通知並處理傳入的通知。

配置您的音訊會話以處理到和使用的轉換。

為了確保iPhone上更好的使用者體驗,請使用核心電話框架來調整與基於單元的電話有關的行為;請參閱核心電話框架參考。Core Telephony Framework Reference

為了確保您的VoIP應用程式的良好效能,請使用“系統配置”框架來檢測網路更改,並允許您的應用程式儘可能睡眠。

請求VoIP服務證書以允許您的通知伺服器連線到VoIP服務。

啟用VoIP背景模式讓系統知道它應該允許應用程式在後臺執行以管理其網路套接字。此鍵還允許您的應用程式播放背景音訊(儘管仍然鼓勵音訊和播放模式)。支援此模式的應用程式在系統啟動後立即重新啟動,以確保VoIP服務始終可用。

使用可達性介面來改善使用者體驗

由於VoIP應用程式嚴重依賴於網路,因此應使用“系統配置”框架的可達性介面來跟蹤網路可用性並相應地調整其行為。可達性介面允許在網路條件變化時通知應用程式。例如,當網路變得不可用時,VoIP應用可以關閉其網路連線,並在再次可用時重新建立它們。該應用程式還可以使用這些更改來保持使用者瞭解VoIP連線的狀態。

要使用可達性介面,您必須在框架中註冊一個回撥函式,並使用它來跟蹤更改。 註冊回撥函式:

為目標遠端主機建立SCNetworkReachabilityRef結構。

為您的結構(使用SCNetworkReachabilitySetCallback函式)分配回撥函式,以處理目標可達性狀態的更改。

使用SCNetworkReachabilityScheduleWithRunLoop函式將該目標新增到應用程式的活動執行迴圈(如主執行迴圈)。

根據網路的可用性調整應用的行為也有助於提高底層裝置的電池壽命。 讓系統跟蹤網路更改意味著您的應用程式可以讓自己更頻繁地休息。

有關可達性介面的更多資訊,請參閱系統配置框架參考。System Configuration Framework Reference

相關文章