鍍金池/ 教程/ iOS/ iOS 7 : 隱藏技巧和變通之道
與四軸無人機(jī)的通訊
在沙盒中編寫腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學(xué)
NSString 與 Unicode
代碼簽名探析
測(cè)試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動(dòng)開發(fā)
Collection View 動(dòng)畫
截圖測(cè)試
MVVM 介紹
使 Mac 應(yīng)用數(shù)據(jù)腳本化
一個(gè)完整的 Core Data 應(yīng)用
插件
字符串
為 iOS 建立 Travis CI
先進(jìn)的自動(dòng)布局工具箱
動(dòng)畫
為 iOS 7 重新設(shè)計(jì) App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡(luò)應(yīng)用實(shí)例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動(dòng)畫解釋
響應(yīng)式 Android 應(yīng)用
初識(shí) TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項(xiàng)目介紹
Swift 的強(qiáng)大之處
測(cè)試并發(fā)程序
Android 通知中心
調(diào)試:案例學(xué)習(xí)
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機(jī)制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學(xué)習(xí)的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設(shè)計(jì)優(yōu)雅的移動(dòng)游戲
繪制像素到屏幕上
相機(jī)與照片
音頻 API 一覽
交互式動(dòng)畫
常見的后臺(tái)實(shí)踐
糟糕的測(cè)試
避免濫用單例
數(shù)據(jù)模型和模型對(duì)象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場(chǎng)
照片框架
響應(yīng)式視圖
Square Register 中的擴(kuò)張
DTrace
基礎(chǔ)集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點(diǎn)互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設(shè)計(jì)的藝術(shù)
導(dǎo)航應(yīng)用
線程安全類的設(shè)計(jì)
置換測(cè)試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無人機(jī)項(xiàng)目
Mach-O 可執(zhí)行文件
UI 測(cè)試
值對(duì)象
活動(dòng)追蹤
依賴注入
Swift
項(xiàng)目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務(wù)
自定義 Collection View 布局
測(cè)試 View Controllers
訪談
收據(jù)驗(yàn)證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場(chǎng)
游戲
調(diào)試核對(duì)清單
View Controller 容器
學(xué)無止境
XCTest 測(cè)試實(shí)戰(zhàn)
iOS 7
Layer 中自定義屬性的動(dòng)畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲(chǔ)
代碼審查的藝術(shù):Dropbox 的故事
GPU 加速下的圖像視覺
Artsy
照片擴(kuò)展
理解 Scroll Views
使用 VIPER 構(gòu)建 iOS 應(yīng)用
Android 中的 SQLite 數(shù)據(jù)庫支持
Fetch 請(qǐng)求
導(dǎo)入大數(shù)據(jù)集
iOS 開發(fā)者的 Android 第一課
iOS 上的相機(jī)捕捉
語言標(biāo)簽
同步案例學(xué)習(xí)
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識(shí)別
玩轉(zhuǎn)字符串
相機(jī)工作原理
Build 過程

iOS 7 : 隱藏技巧和變通之道

當(dāng) iOS 7 剛發(fā)布的時(shí)候,全世界的蘋果開發(fā)人員都立馬嘗試著去編譯他們的 app,接著再花上數(shù)月的時(shí)間來修復(fù)任何出現(xiàn)的錯(cuò)誤,甚至從頭開始重建這個(gè) app。這樣的結(jié)果,使得人們根本無暇去探究 iOS 7 所帶來的新思想。除開一些明顯而細(xì)微的更新,比如說 NSArray 的 firstObject 方法——這個(gè)方法可追溯到 iOS 4 時(shí)代,現(xiàn)在被提為公有 API——還有很多隱藏的技巧等著我們?nèi)ネ诰颉?/p>

平滑淡入淡出動(dòng)畫

我在這里要討論的并非新的彈性動(dòng)畫 API 或者 UIDynamics,而是一些更細(xì)微的東西。CALayer 增加了兩個(gè)新方法:allowsGroupOpacityallowsEdgeAntialiasing?,F(xiàn)在,組不透明度(group opacity)不再是什么新鮮的東西了。iOS 會(huì)多次使用存在于 Info.plist 中的鍵 UIViewGroupOpacity 并可在應(yīng)用程序范圍內(nèi)啟用或禁用它。對(duì)于大多數(shù) app 而言,這(譯注:?jiǎn)⒂茫┎⒎撬谕?,因?yàn)樗鼤?huì)降低整體性能。在 iOS 7 中,用 SDK 7 所鏈接的程序,這項(xiàng)屬性默認(rèn)是啟用的。當(dāng)它被啟用時(shí),一些動(dòng)畫將會(huì)變得不流暢,它也可以在 layer 層上被控制。

一個(gè)有趣的細(xì)節(jié),如果 allowsGroupOpacity 啟用的話,_UIBackdropView(被用作 UIToolbar 或者 UIPopoverView 的背景視圖)不能對(duì)其模糊進(jìn)行動(dòng)畫處理,所以當(dāng)你做一個(gè) alpha 轉(zhuǎn)換時(shí),你可能會(huì)臨時(shí)禁用這項(xiàng)屬性。因?yàn)檫@會(huì)降低動(dòng)畫體驗(yàn),你可以回到舊的方式然后在動(dòng)畫期間臨時(shí)啟用 shouldRasterize。別忘了設(shè)置適當(dāng)?shù)?rasterizationScale,否則在 retina 的設(shè)備上這些視圖會(huì)成鋸齒狀(pixelerated)。

如果你想要復(fù)制 Safari 顯示所有選項(xiàng)卡時(shí)的動(dòng)畫,那么邊緣抗鋸齒屬性將變得非常有用。

阻塞動(dòng)畫

有一個(gè)小但是非常有用的新方法 [UIView performWithoutAnimation:]。它是一個(gè)簡(jiǎn)單的封裝,先檢查動(dòng)畫當(dāng)前是否啟用,如果是則停用動(dòng)畫,執(zhí)行塊語句,然后重新啟用動(dòng)畫。一個(gè)需要說明的地方是,它并 不會(huì) 阻塞基于 CoreAnimation 的動(dòng)畫。因此,不用急于將你的方法調(diào)用從:

    [CATransaction begin];
    [CATransaction setDisableActions:YES];
    view.frame = CGRectMake(...);
    [CATransaction commit];

替換成:

    [UIView performWithoutAnimation:^{
        view.frame = CGRectMake(...);
    }];

但是,絕大多數(shù)情況下這樣也能工作得很好,只要你不直接跟 CALayer 打交道。

iOS 7 中,我有很多代碼路徑(主要是 UITableViewCells)需要額外保護(hù)以防止意外的動(dòng)畫,例如,如果一個(gè)彈窗(popover)的大小調(diào)整了,與此同時(shí)其中的表視圖將因?yàn)楦叨鹊淖兓虞d新的 cell。我通常的做法是將整個(gè) layoutSubviews 的代碼包扎到一個(gè)動(dòng)畫塊中:

- (void)layoutSubviews 
{
    // 否則在 iOS 7 的傳統(tǒng)模式下彈窗動(dòng)畫會(huì)滲入我們的單元格
    [UIView performWithoutAnimation:^{
        [super layoutSubviews];
        _renderView.frame = self.bounds;
    }];
}

處理長(zhǎng)的表視圖

UITableView 非??焖俑咝В悄汩_始使用 tableView:heightForRowAtIndexPath:,它會(huì)開始為你表中 每一個(gè) 元素調(diào)用此方法,即便沒有可視對(duì)象——這是為了讓其下層的 UIScrollView 能獲取正確的 contentSize。此前有一些變通方法,但都不好用。iOS 7 中,蘋果公司終于承認(rèn)這一問題,并添加了 tableView:estimatedHeightForRowAtIndexPath:,這個(gè)方法把絕大部分計(jì)算成本推遲到實(shí)際滾動(dòng)的時(shí)候。如果你完全不知道一個(gè) cell 的大小,返回 UITableViewAutomaticDimension 就行了。

對(duì)于段頭/尾(section headers/footers),現(xiàn)在也有類似的 API 了。

UISearchDisplayController

蘋果的 search controller 使用了新的技巧來簡(jiǎn)化移動(dòng) search bar 到 navigation bar 的過程。啟用 displaysSearchBarInNavigationBar 就可以了(除非你還在用 scope bar,那你就太不幸了)。我倒是很喜歡這么做,但遺憾的是,iOS 7 上的 UISearchDisplayController 貌似被破壞得相當(dāng)嚴(yán)重,尤其在 iPad 上。蘋果公司看上去像是沒時(shí)間處理這個(gè)問題,對(duì)于顯示的搜索結(jié)果并不會(huì)隱藏實(shí)際的表視圖。在 iOS 7 之前,這不算問題,但是現(xiàn)在 searchResultsTableView 有一個(gè)透明的背景色,使它看上去相當(dāng)糟糕。作為一種變通方法,你可以設(shè)置不透明背景色或者采取一些更富于技巧的手段來獲得你期望的效果。關(guān)于這個(gè)控件我碰到過各種各樣的結(jié)果,當(dāng)使用 displaysSearchBarInNavigationBar 時(shí)甚至 根本 不會(huì)顯示搜索表視圖。

你的結(jié)果可能有所不同,但我依賴于一些手段(severe hacks)來讓 displaysSearchBarInNavigationBar 工作:

- (void)restoreOriginalTableView 
{
    if (PSPDFIsUIKitFlatMode() && self.originalTableView) {
        self.view = self.originalTableView;
    }
}

- (UITableView *)tableView 
{
    return self.originalTableView ?: [super tableView];
}

- (void)searchDisplayController:(UISearchDisplayController *)controller 
  didShowSearchResultsTableView:(UITableView *)tableView 
{
    // HACK: iOS 7 依賴于重度的變通來顯示搜索表視圖
    if (PSPDFIsUIKitFlatMode()) {
        if (!self.originalTableView) self.originalTableView = self.tableView;
        self.view = controller.searchResultsTableView;
        controller.searchResultsTableView.contentInset = UIEdgeInsetsZero; // 移除 64 像素的空白
    }
}

- (void)searchDisplayController:(UISearchDisplayController *)controller 
  didHideSearchResultsTableView:(UITableView *)tableView 
{
    [self restoreOriginalTableView];
}

另外,別忘了在 viewWillDisappear 中調(diào)用 restoreOriginalTableView,否則程序會(huì) crash。 記住這只是一種解決辦法;可能還有不那么激進(jìn)的方法,不用替換視圖本身,但這個(gè)問題確實(shí)應(yīng)該由蘋果公司來修復(fù)。(TODO: RADAR!)

分頁

UIWebView 現(xiàn)在可以對(duì)帶有 paginationMode 的網(wǎng)站進(jìn)行自動(dòng)分頁。有一大堆與此功能相關(guān)的新屬性:

@property (nonatomic) UIWebPaginationMode paginationMode NS_AVAILABLE_IOS(7_0);
@property (nonatomic) UIWebPaginationBreakingMode paginationBreakingMode NS_AVAILABLE_IOS(7_0);
@property (nonatomic) CGFloat pageLength NS_AVAILABLE_IOS(7_0);
@property (nonatomic) CGFloat gapBetweenPages NS_AVAILABLE_IOS(7_0);
@property (nonatomic, readonly) NSUInteger pageCount NS_AVAILABLE_IOS(7_0);

目前而言,雖然這不一定對(duì)大多數(shù)網(wǎng)站都有用,但它肯定是生成簡(jiǎn)單的電子書閱讀器或者顯示文本的一種更好的方式。加點(diǎn)樂子的話,請(qǐng)嘗試將它設(shè)置為 UIWebPaginationModeBottomToTop。

會(huì)飛的 Popover

想知道為什么你的 popover 瘋了一樣到處亂飛?在 UIPopoverControllerDelegate 協(xié)議中有一個(gè)新的代理方法讓你能控制它:

-  (void)popoverController:(UIPopoverController *)popoverController
  willRepositionPopoverToRect:(inout CGRect *)rect 
                       inView:(inout UIView **)view

當(dāng) popover 錨點(diǎn)是指向一個(gè) UIBarButtonItem 時(shí),UIPopoverController 會(huì)做出合適的展現(xiàn),但是如果你讓它在一個(gè) view 或者 rect 中顯示,你可能就需要實(shí)現(xiàn)此方法并正常返回。一個(gè)花費(fèi)了我相當(dāng)長(zhǎng)時(shí)間來驗(yàn)證的問題——如果你通過改變 preferredContentSize 來動(dòng)態(tài)調(diào)整你的 popover,那么這個(gè)方法就尤其需要實(shí)現(xiàn)。蘋果公司現(xiàn)在對(duì)改變 popover 大小的請(qǐng)求更嚴(yán)格,如果沒有預(yù)留足夠的空間,popover 將會(huì)到處移動(dòng)。

鍵盤支持

蘋果公司不只為我們提供了全新的 framework 用于游戲控制器,它也給了我們這些鍵盤愛好者一些關(guān)注!你會(huì)發(fā)現(xiàn)新定義的公用鍵,比如 UIKeyInputEscapeUIKeyInputUpArrow,可以使用全新的 UIKeyCommand 類截查。在 iOS 7 之前,只能通過一些難以言表的手段來處理鍵盤命令,現(xiàn)在,就讓我們操起藍(lán)牙鍵盤試試看我們能用這個(gè)做什么!

開始之前,你需要對(duì)響應(yīng)鏈(responder chain)有個(gè)了解。你的 UIApplication 繼承自 UIResponder,UIViewUIViewController 也是如此。如果你曾經(jīng)處理過 UIMenuItem 并且沒有使用我的基于塊的包裝的話,那么你對(duì)此已經(jīng)有所了解。事件先被發(fā)送到最上層的響應(yīng)者,然后一級(jí)級(jí)往下傳遞直到 UIApplication。為了捕獲按鍵命令,你需要告訴系統(tǒng)你關(guān)心哪些按鍵命令(而不是全捕獲)。為了完成這個(gè),你需要重寫 keyCommands 這個(gè)新屬性:

- (NSArray *)keyCommands 
{
    return @[[UIKeyCommand keyCommandWithInput:@"f"
                                 modifierFlags:UIKeyModifierCommand  
                                        action:@selector(searchKeyPressed:)]];
}

- (void)searchKeyPressed:(UIKeyCommand *)keyCommand 
{
    // 響應(yīng)事件
}

http://wiki.jikexueyuan.com/project/objc/images/5-14.png" alt="" />

現(xiàn)在可別太激動(dòng),需要注意的是,這個(gè)方法只在鍵盤可見時(shí)有效(比如有類似 UITextView 這樣的對(duì)象作為第一響應(yīng)者時(shí))。對(duì)于全局熱鍵,你仍然需要用上面提到的 hack 方法。除去那些,這個(gè)解決途徑還是很優(yōu)雅的。不要覆蓋類似 cmd-V 這種系統(tǒng)的快捷鍵,它會(huì)被自動(dòng)映射到 paste: 方法。

還有一些新的預(yù)定義的響應(yīng)者行為:

- (void)increaseSize:(id)sender NS_AVAILABLE_IOS(7_0);
- (void)decreaseSize:(id)sender NS_AVAILABLE_IOS(7_0);

它們分別對(duì)應(yīng) cmd+ 和 cmd- 命令,用來放大/縮小內(nèi)容。

匹配鍵盤背景

蘋果公司終于公開了 UIInputView,其中提供了一種方式——使用 UIInputViewStyleKeyboard 來匹配鍵盤樣式。這使得你能編寫自定義的鍵盤或者適應(yīng)默認(rèn)樣式的默認(rèn)鍵盤的擴(kuò)展(工具條)。這個(gè)類一開始就存在了,不過現(xiàn)在我們終于可以繞過私有API的方式來使用它了。

如果 UIInputView 是一個(gè) inputView 或者 inputAccessoryView根視圖,它將只顯示一個(gè)背景,否則它將是透明的。遺憾的是,這并不能讓你實(shí)現(xiàn)一個(gè)未填充的分離態(tài)的鍵盤,但它仍然比用一個(gè)簡(jiǎn)單的 UIToolbar 要好。我還沒看到蘋果在何處使用這個(gè)新 API,看上去 Safari 里仍然使用著 UIToolbar

了解你的無線電通信

雖然早在 iOS 4 的時(shí)候,大部分的運(yùn)營(yíng)商信息已經(jīng)在 CTTelephony 暴露了,但它通常只用于特定場(chǎng)景并非十分有用。iOS 7 中,蘋果公司為其添加了一個(gè)方法,其中最有用的:currentRadioAccessTechnology。這個(gè)方法能告訴你手機(jī)是處于較慢的 GPRS 還是高速的 LTE 或者介于其中。目前還沒有方法得到連接速度(當(dāng)然手機(jī)本身也無法獲取這個(gè)),但是這足以用來優(yōu)化一個(gè)下載管理器,讓其在 EDGE 下不用嘗試 同時(shí) 去下載6張圖片了。

現(xiàn)在還沒有 currentRadioAccessTechnology 的相關(guān)文檔,為了讓它工作,會(huì)遇到一些麻煩和錯(cuò)誤。當(dāng)你想要獲取當(dāng)前網(wǎng)絡(luò)信號(hào)值,你應(yīng)當(dāng)注冊(cè)一個(gè) CTRadioAccessTechnologyDidChangeNotification 通知而不是去輪詢這個(gè)屬性。為了確切的使 iOS 發(fā)送這些通知,你需要持有一個(gè) CTTelephonyNetworkInfo 的實(shí)例,但不要在通知中創(chuàng)建 CTTelephonyNetworkInfo 的實(shí)例,否則會(huì) crash。

在這個(gè)簡(jiǎn)單的例子中,因?yàn)樵?block 中捕獲 telephonyInfo 將會(huì)持有它,所以我就這么用了:

CTTelephonyNetworkInfo *telephonyInfo = [CTTelephonyNetworkInfo new];
NSLog(@"Current Radio Access Technology: %@", telephonyInfo.currentRadioAccessTechnology);
[NSNotificationCenter.defaultCenter addObserverForName:CTRadioAccessTechnologyDidChangeNotification 
                                                object:nil 
                                                 queue:nil 
                                            usingBlock:^(NSNotification *note) 
{
    NSLog(@"New Radio Access Technology: %@", telephonyInfo.currentRadioAccessTechnology);
}];

當(dāng)手機(jī)從 Edge 環(huán)境切換到 3G,日志輸出應(yīng)該像這樣:

iOS7Tests[612:60b] Current Radio Access Technology: CTRadioAccessTechnologyEdge
iOS7Tests[612:1803] New Radio Access Technology: (null)
iOS7Tests[612:1803] New Radio Access Technology: CTRadioAccessTechnologyHSDPA

蘋果導(dǎo)出了所有字符串符號(hào),因此可以很簡(jiǎn)單的比較和檢測(cè)當(dāng)前的網(wǎng)絡(luò)信息。

Core Foundation,Autorelease 和你

Core Foundation 中出現(xiàn)了一個(gè)新的輔助方法,它被用于私有調(diào)用已有數(shù)年時(shí)間:

CFTypeRef CFAutorelease(CFTypeRef CF_RELEASES_ARGUMENT arg)

它的確做了你所期望的事,讓人費(fèi)解的是蘋果花了這么長(zhǎng)時(shí)間才把它公開。ARC 下,大多數(shù)人在處理返回 Core Foundation 對(duì)象時(shí)是通過轉(zhuǎn)換成對(duì)等的 NS 對(duì)象來完成的,如返回一個(gè) NSDictionary,雖然它是一個(gè) CFDictionaryRef,簡(jiǎn)單地使用 CFBridgingRelease() 就行了。這樣通常沒問題,除非你返回的沒有可用的對(duì)等 NS 對(duì)象,如 CFBagRef。你要么使用 id,這樣會(huì)失去類型安全性,要么你將你的方法重命名為 createMethod 并考慮所有的內(nèi)存語義,最后使用 CFRelease。還有一些手段,比如這個(gè),使用 non-ARC-file 參數(shù)你才能編譯它,但終歸得使用 CFAutorelease()。另外:不要編寫使用蘋果公司命名空間的代碼,所有這些自定義的 CF-宏將來都會(huì)被打破的。

圖片解壓縮

當(dāng)通過 UIImage 展示一張圖片時(shí),在顯示之前需要解壓縮(除非圖片源已經(jīng)像素緩存了)。對(duì)于 JPG/PNG 文件這會(huì)占用相當(dāng)可觀的時(shí)間并會(huì)造成卡頓。iOS 6 以前,通常是通過創(chuàng)建一個(gè)位圖上下文,然后在其中畫圖來解決。(參見 AFNetworking 如何處理這個(gè)問題)。

從 iOS 7 開始,你可以使用 kCGImageSourceShouldCacheImmediately: 強(qiáng)制圖片在創(chuàng)建時(shí)直接解壓縮:

+ (UIImage *)decompressedImageWithData:(NSData *)data 
{
    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
    CGImageRef cgImage = CGImageSourceCreateImageAtIndex(source, 0, (__bridge CFDictionaryRef)@{(id)kCGImageSourceShouldCacheImmediately: @YES});

    UIImage *image = [UIImage imageWithCGImage:cgImage];
    CGImageRelease(cgImage);
    CFRelease(source);
    return image;
}

剛發(fā)現(xiàn)這一點(diǎn)時(shí)我很很興奮,但不要高興得太早。在我的測(cè)試中,開啟即時(shí)緩存后性能實(shí)際上有所 降低。要么這個(gè)方法最終是在主線程中被調(diào)用的(好像不太可能),要么感官上的性能下降是因?yàn)槠湓诜椒?copyImageBlockSetJPEG 中鎖住了,因?yàn)檫@個(gè)方法也被用在主線程顯示非加密的圖片時(shí)。在我的 app 中,我在主線程中加載小的預(yù)覽圖,在后臺(tái)線程中加載大型圖,使用了 kCGImageSourceShouldCacheImmediately 后小小的解壓縮阻塞了主線程,同時(shí)在后臺(tái)處理大量開銷昂貴的操作。

http://wiki.jikexueyuan.com/project/objc/images/5-15.png" alt="" />

還有更多關(guān)于圖片解壓縮的卻不是 iOS 7 中的新東西,像 kCGImageSourceShouldCache,它用來控制系統(tǒng)自動(dòng)卸載解壓縮圖片數(shù)據(jù)的能力。確保你將它設(shè)置為 YES,否則所有的工作都將沒有意義。有趣的是,蘋果在 64-bit 運(yùn)行時(shí)的系統(tǒng)中將 kCGImageSourceShouldCache默認(rèn)值 從 NO 改為了 YES。

盜版檢查

蘋果添加了一個(gè)方式,通過 NSBunble 上的新方法 appStoreReceiptURL 來獲取和驗(yàn)證 Lion 系統(tǒng)上 App Store 的收據(jù),現(xiàn)在終于也移植到了 iOS 上了。這使得你可以檢查你的應(yīng)用是合法購買的還是被破解了的。檢查收據(jù)還有另一個(gè)重要的原因,它包含了 初始購買日期,這點(diǎn)對(duì)于把你的應(yīng)用從付費(fèi)模式遷移到免費(fèi)+應(yīng)用內(nèi)付費(fèi)模式很有幫助。你可以根據(jù)這個(gè)初始購買日期來決定額外內(nèi)容對(duì)于你的用戶是免費(fèi)(因?yàn)樗麄円呀?jīng)付過費(fèi)了)還是收費(fèi)的。

收據(jù)還允許你檢查應(yīng)用程序是否通過批量購買計(jì)劃購買以及該許可證是否仍有效,有一個(gè)名為 SKReceiptPropertyIsVolumePurchase 的屬性標(biāo)示了該值。

當(dāng)你調(diào)用 appStoreReceiptURL 時(shí),你需要特別注意,因?yàn)樵?iOS 6 上,它還是一個(gè)私有 API,你應(yīng)該在用戶代碼中先調(diào)用 doesNotRecognizeSelector:,在調(diào)用前檢查運(yùn)行(基礎(chǔ))版本。在開發(fā)期間,這個(gè)方法返回的 URL 不會(huì)指向一個(gè)文件。你可能需要使用 StoreKit 的 SKReceiptRefreshRequest,這也是 iOS 7 中的新東西,用它來下載證書。使用一個(gè)至少有過一次購買的測(cè)試用戶,否則它將沒法工作:

// 刷新收據(jù)
SKReceiptRefreshRequest *request = [[SKReceiptRefreshRequest alloc] init];
[request setDelegate:self];
[request start];

驗(yàn)證收據(jù)需要大量的代碼。你需要使用 OpenSSL 和內(nèi)嵌的蘋果根證書,并且你還要了解一些基本的東西像是證書、PCKS 容器以及 ASN.1。這里有一些樣例代碼,但是你不應(yīng)該讓它這么簡(jiǎn)單——尤其是對(duì)那些有“高尚意圖”的人,別只是拷貝現(xiàn)有的驗(yàn)證方法,至少做點(diǎn)修改或者編寫你自己的,你應(yīng)該不希望一個(gè)普通的補(bǔ)丁程序就能在數(shù)秒內(nèi)瓦解你的努力吧。

你絕對(duì)應(yīng)該讀讀蘋果的指南——驗(yàn)證 Mac App 商店收據(jù),這里面的大多數(shù)都適用于 iOS。蘋果在 WWDC 2013 的 Session 308 “Using Receipts to Protect Your Digital Sales” 中詳述了通過新加入的“Grand Unified Receipt”而帶來的變動(dòng)。

Comic Sans MS

承認(rèn)吧,你是懷念 Comic Sans MS 的。在 iOS 7 中,Comic Sans MS 終于回來了。iOS 6 中添加了可下載字體,但那時(shí)的字體列表很少也不見得有趣。在 iOS 7 中蘋果添加了不少字體,包括 “famous”,它和 PT SansComic Sans MS 有些類似。kCTFontDownloadableAttribute 并沒有在 iOS 6 中聲明,所以 iOS 7 之前它并不真正可用,但蘋果確是在 iOS 6 的時(shí)候就已經(jīng)做了私有聲明了。

http://wiki.jikexueyuan.com/project/objc/images/5-16.png" alt="" />

字體列表是動(dòng)態(tài)變化的,以后可能就會(huì)發(fā)生變動(dòng)。蘋果在 Tech Note HT5484 中羅列了一些可用的字體,但這個(gè)文檔已經(jīng)過時(shí)了,并不能反映 iOS 7 的變化。

這里顯示了你該如何獲取一個(gè)用 CTFontDescriptorRef 標(biāo)示的可下載的字體數(shù)組:

CFDictionary *descriptorOptions = @{(id)kCTFontDownloadableAttribute : @YES};
CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes((CFDictionaryRef)descriptorOptions);
CFArrayRef fontDescriptors = CTFontDescriptorCreateMatchingFontDescriptors(descriptor, NULL);

系統(tǒng)不會(huì)檢查字體是否已存在于磁盤上而將直接返回同樣的列表。另外,這個(gè)方法可能會(huì)啟用網(wǎng)絡(luò)并造成阻塞,你不應(yīng)該在主線程中使用它。

使用如下基于塊的 API 來下載字體:

bool CTFontDescriptorMatchFontDescriptorsWithProgressHandler(
         CFArrayRef                          descriptors,
         CFSetRef                            mandatoryAttributes,
         CTFontDescriptorProgressHandler     progressBlock)

這個(gè)方法能操作網(wǎng)絡(luò)并傳遞下載進(jìn)度信息來調(diào)用你的 progressBlock 方法直到下載成功或者失敗。參考蘋果的 DownloadFont 樣例看看如何使用它。

有一些值得注意的地方,這里的字體只在當(dāng)前程序運(yùn)行時(shí)有效,下次運(yùn)行將被重新載入內(nèi)存。因?yàn)樽煮w存放在共享空間中,你不能依賴于它們是否可用。很有可能但不能保證地說,系統(tǒng)會(huì)清理這個(gè)目錄,或者你的程序被拷貝到?jīng)]有這個(gè)字體的新設(shè)備中,同時(shí)你又沒有網(wǎng)絡(luò)。在 Mac 或是模擬器上,你能根據(jù) kCTFontURLAttribute 獲得字體的絕對(duì)路徑,加載速度也會(huì)提升,但是在 iOS 上是不行的,因?yàn)檫@個(gè)目錄在你的程序之外,你需要再次調(diào)用 CTFontDescriptorMatchFontDescriptorsWithProgressHandler。

你也可以注冊(cè)新的 kCTFontManagerRegisteredFontsChangedNotification 通知來跟蹤新字體在何時(shí)被載入到了字體注冊(cè)表中。你可以在 WWDC 2013 的 Session 223 “Using Fonts with TextKit”中查找更多信息。

這還不夠?

沒關(guān)系,iOS 7 的新東西遠(yuǎn)不止如此!了解一下 NSHipster 你將明白語音合成相關(guān)的東西,base64、全新的 NSURLComponents、NSProgress、條形碼掃描、閱讀列表以及 CIDetectorEyeBlink。還有很多我們沒有涵蓋到的,比如蘋果的 iOS 7 API 變化,指南以及 Foundation Release Notes(這些都是基于 OS X的,但是代碼都是共享的,很多也同樣適用于 iOS)。很多新方法都還沒形成文檔,等著你來探究和寫成博客。