iOS 7: 隱藏的特性和解決之道
當(dāng) iOS7 剛發(fā)布的時(shí)候,全世界的蘋(píng)果開(kāi)發(fā)人員都立馬嘗試著去編譯他們的app,接著再花上數(shù)月的時(shí)間來(lái)修復(fù)任何出現(xiàn)的故障,甚至重做app。這樣的結(jié)果,使得人們根本無(wú)暇去探究 iOS7 所帶來(lái)的新東西。一些明顯而細(xì)微的更新,比如說(shuō)[NSArray firstObject],這個(gè)方法可追溯到 iOS4 時(shí)代,現(xiàn)在被提為公有API,除此之外,還有很多隱藏的特性等著我們?nèi)ネ诰颉?/p>
平滑淡入淡出動(dòng)畫(huà)
我這里要討論的并非新的彈性動(dòng)畫(huà)APIs 或者 UIDynamics,而是一些更細(xì)微的東西。CALayer增加了兩個(gè)新方法:allowsGroupOpacity和allowsEdgeAntialiasing。現(xiàn)在,組不透明度(group opacity)不再是什么新鮮的東西了。iOS會(huì)多次使用存在于 Info.plist 中的鍵UIViewGroupOpacity并可在應(yīng)用程序范圍內(nèi)啟用或禁用它。對(duì)于大多數(shù)apps而言,這(譯注:?jiǎn)⒂?并非所期望的,因?yàn)樗鼤?huì)降低整體性能。在 iOS7 中,用 SDK7 所鏈接的程序,這項(xiàng)屬性默認(rèn)是啟用的。當(dāng)它被啟用時(shí),一些動(dòng)畫(huà)將會(huì)變得不流暢,它也可以在layer層上被控制。
一個(gè)有趣的細(xì)節(jié),如果allowsGroupOpacity啟用的話,_UIBackdropView(在UIToolbar或者UIPopoverView中的背景視圖)不能對(duì)其模糊進(jìn)行動(dòng)畫(huà)處理,所以當(dāng)你做一個(gè)alpha轉(zhuǎn)換時(shí),你可能會(huì)臨時(shí)禁用這項(xiàng)屬性。因?yàn)檫@會(huì)降低動(dòng)畫(huà)體驗(yàn),你可以回退到舊的方式然后在動(dòng)畫(huà)期間臨時(shí)啟用shouldRasterize。別忘了設(shè)置適當(dāng)?shù)膔asterizationScale,否則在retina的設(shè)備上這些視圖會(huì)成鋸齒狀。
如果你想要復(fù)制的 Safari 顯示所有選項(xiàng)卡時(shí)的動(dòng)畫(huà),那么邊緣抗鋸齒屬性將變得非常有用。
阻塞動(dòng)畫(huà)
一個(gè)小但非常有用的新方法[UIView performWithoutAnimation:]。它是一個(gè)簡(jiǎn)單的封裝,先檢查動(dòng)畫(huà)當(dāng)前是否啟用,然后禁止動(dòng)畫(huà),執(zhí)行塊語(yǔ)句,最后重新啟用動(dòng)畫(huà)。一個(gè)需要說(shuō)明的地方是,它并不會(huì)阻塞基于 CoreAnimation 的動(dòng)畫(huà)。因此,不用急于將你的方法調(diào)用從:
[CATransaction begin];
[CATransaction setDisableActions:YES];
view.frame = CGRectMake(...);
[CATransaction commit];
替換為:
1
2
3[UIView performWithoutAnimation:^{
view.frame = CGRectMake(...);
}];
但是,絕大多數(shù)情況下這樣也能工作的很好,只要你不直接處理CALayers。
iOS7 中,我有很多代碼路徑(主要是 UITableViewCells)需要額外的保護(hù),防止意外的動(dòng)畫(huà),例如,如果一個(gè)彈窗的大小調(diào)整了,那么同時(shí)顯示中的表視圖將因?yàn)楦叨鹊淖兓虞d新的cell。我通常的做法是將整個(gè) layoutSubviews 的代碼包扎到一個(gè)動(dòng)畫(huà)塊中:
(void)layoutSubviews
{
// Otherwise the popover animation could leak into our cells on iOS 7 legacy mode.
[UIView performWithoutAnimation:^{
[super layoutSubviews];
_renderView.frame = self.bounds;
}];
}
處理長(zhǎng)表視圖
UITableView 非??焖俑咝В悄汩_(kāi)始使用tableView:heightForRowAtIndexPath:,它會(huì)開(kāi)始為你表中任意元素調(diào)用此方法,即便沒(méi)有可視對(duì)象,就比如其內(nèi)在的UIScrollView只是去獲取正確的contentSize。此前有一些變通方法,但都不好用。iOS7 中,蘋(píng)果公司終于承認(rèn)這一問(wèn)題,并添加tableView:estimatedHeightForRowAtIndexPath:,這個(gè)方法延遲了實(shí)際滾動(dòng)時(shí)間成本的大部分。如果你不知道一個(gè)cell的大小,返回UITableViewAutomaticDimension即可。
對(duì)于節(jié)頭/尾(section headers/footers),現(xiàn)在也有類似的API了。
UISearchDisplayController
蘋(píng)果的 search controller 使用了新的技巧來(lái)簡(jiǎn)化移動(dòng) search bar 到 navigation bar 的過(guò)程。啟用 displaysSearchBarInNavigationBar 就可以了(除非你還要用到 scope bar,我只能說(shuō)你真不幸)。我倒是很喜歡這么做,但比較遺憾的是,iOS7 上的 UISearchDisplayController 貌似被摧殘的比較嚴(yán)重,尤其是iPad。蘋(píng)果公司看上去像是沒(méi)時(shí)間處理這個(gè)問(wèn)題的樣子(原文:Apple seems to have run out of time),對(duì)于顯示的搜索結(jié)果并不會(huì)隱藏實(shí)際的表視圖。在 iOS7 之前,這并沒(méi)有問(wèn)題,但是現(xiàn)在 searchResultsTableView 有一個(gè)透明的背景色,使它看上去相當(dāng)糟糕。作為一種變通方法,你可以設(shè)置不透明色或者取道于富于技巧的手段來(lái)獲得你所期望的。關(guān)于這個(gè)控件會(huì)出現(xiàn)各種各樣的結(jié)果,當(dāng)使用displaysSearchBarInNavigationBar時(shí)甚至不會(huì)展示搜索表視圖。
你的結(jié)果可能有所不同,但我是使用了一些手段來(lái)讓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 requires a cruel workaround to show the search table view.
if (PSPDFIsUIKitFlatMode()) {
if (!self.originalTableView) self.originalTableView = self.tableView;
self.view = controller.searchResultsTableView;
controller.searchResultsTableView.contentInset = UIEdgeInsetsZero; // Remove 64 pixel gap
}
}
- (void)searchDisplayController:(UISearchDisplayController *)controller
didHideSearchResultsTableView:(UITableView *)tableView
{
[self restoreOriginalTableView];
}
這里,別忘了在viewWillDisappear中調(diào)用restoreOriginalTableView,否則會(huì)發(fā)送crash。
記住這是唯一的解決辦法;可能有不少激進(jìn)的方法不替換視圖本身,但這個(gè)問(wèn)題確實(shí)應(yīng)該由蘋(píng)果公司來(lái)修復(fù)。(TODO: RADAR!)[!--empirenews.page--]
分頁(yè)
UIWebView 使用了新的技巧來(lái)自動(dòng)分頁(yè)帶paginationMode的網(wǎ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);
現(xiàn)在而言,雖然這可能并非對(duì)于大多數(shù)網(wǎng)站都有用,但它肯定是生成簡(jiǎn)單的電子書(shū)閱讀器或顯示文本的一種更好的方式。加點(diǎn)樂(lè)子的話,請(qǐng)嘗試將它設(shè)置為UIWebPaginationModeBottomToTop。
會(huì)飛的 Popovers
想知道為什么你的popovers瘋了一樣到處亂飛?在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ì)有一些動(dòng)作,但如果你讓它在一個(gè)view或者rect中顯示,你可能就需要實(shí)現(xiàn)此方法并正常返回。一個(gè)花費(fèi)了我相當(dāng)長(zhǎng)的時(shí)間來(lái)驗(yàn)證的問(wèn)題——如果你通過(guò)改變preferredContentSize來(lái)動(dòng)態(tài)調(diào)整你的popovers,那么這個(gè)方法就特別要求得以實(shí)現(xiàn)。蘋(píng)果公司現(xiàn)在對(duì)改變popovers大小的請(qǐng)求更嚴(yán)格,如果沒(méi)有預(yù)留足夠的空間,popover將會(huì)到處移動(dòng)。
鍵盤(pán)支持
蘋(píng)果公司不只為我們提供了全新的framework用于游戲控制器,它也給了我們這些鍵盤(pán)愛(ài)好者一些提示!你會(huì)發(fā)現(xiàn)新定義的公用鍵像 UIKeyInputEscape 或 UIKeyInputUpArrow,可以使用所有新的 UIKeyCommand 類截查。在 iOS7 之前,只能通過(guò)一些難以言表的手段來(lái)處理鍵盤(pán)命令,現(xiàn)在,就讓我們操起藍(lán)牙鍵盤(pán)試試看我們能用這個(gè)做什么!
開(kāi)始之前,你需要對(duì)責(zé)任者鏈有個(gè)了解。你的 UIApplication 繼承自 UIResponder,UIView 和 UIViewController 也是如此。如果你處理過(guò) UIMenuItem 并且沒(méi)有使用我的基于塊的包裝的話,那么你已經(jīng)了解了這些。事件先被發(fā)送到最上層的響應(yīng)者,然后一級(jí)級(jí)往下傳遞直到 UIApplication 。為了捕獲按鍵命令,你需要告訴系統(tǒng)你關(guān)心哪些鍵命令(而不是全捕獲)。為了完成這個(gè),你需要重寫(xiě)keyCommands這個(gè)新屬性:
(NSArray *)keyCommands
{
return @[[UIKeyCommand keyCommandWithInput:@"f"
modifierFlags:UIKeyModifierCommand
action:@selector(searchKeyPressed:)]];
}
- (void)searchKeyPressed:(UIKeyCommand *)keyCommand
{
// Respond to the event
}
現(xiàn)在可別太激動(dòng),需要注意的是,這個(gè)方法只在鍵盤(pán)可見(jiàn)時(shí)有效(比如有類似 UITextView 這樣的對(duì)象作為第一響應(yīng)者時(shí))。對(duì)于全局熱鍵,你仍然需要用上面的方法。除卻那些,這個(gè)路徑還是很優(yōu)雅的。不要覆蓋類似 cmd-V 系統(tǒng)的快捷鍵,它會(huì)被自動(dòng)映射為粘貼功能。
還有一些新的預(yù)定義的響應(yīng)行為如:
1
2- (void)increaseSize:(id)sender NS_AVAILABLE_IOS(7_0);
- (void)decreaseSize:(id)sender NS_AVAILABLE_IOS(7_0);
它們分別對(duì)應(yīng)著 cmd+ 和 cmd- 命令,用來(lái)放大/縮小內(nèi)容。
匹配鍵盤(pán)背景
蘋(píng)果公司終于公開(kāi)了 UIInputView,其中提供了一種方式——使用UIInputViewStyleKeyboard來(lái)匹配鍵盤(pán)樣式。這使得你可以編寫(xiě)自定義的鍵盤(pán)或者帶默認(rèn)樣式的默認(rèn)鍵盤(pán)擴(kuò)展(工具條)。這個(gè)類以前就存在了,不過(guò)現(xiàn)在我們終于可以繞過(guò)私有API的方式來(lái)使用它了。
如果 UIInputView 是一個(gè) inputView 或者 inputAccessoryView 的根視圖,它將只顯示一個(gè)背景,否則它將是透明的。遺憾的是,這并不能讓你實(shí)現(xiàn)一個(gè)未填充的分離態(tài)的鍵盤(pán),但它仍然比用一個(gè)簡(jiǎn)單的 UIToolbar 要好。我還沒(méi)看到蘋(píng)果在何處使用這個(gè)新API,貌似它只作為一個(gè) UIToolbar 使用在 Safari 上。
了解你的網(wǎng)絡(luò)
雖然早在 iOS4 的時(shí)候,關(guān)于網(wǎng)絡(luò)信息的大部分已經(jīng)在 CTTelephony 暴露了,但它通常只用于特定場(chǎng)景并非十分有用。iOS7 中,蘋(píng)果公司為其添加了一個(gè)方法,其中最有用的:currentRadioAccessTechnology。這個(gè)使你能知曉手機(jī)是處于較慢的GPRS還是高速的LTE或者介于其中。目前還沒(méi)有方法得到連接速度(當(dāng)然手機(jī)本身也無(wú)法獲取這個(gè)),但是這足以用來(lái)優(yōu)化一個(gè)下載管理器,讓其在EDGE下不用嘗試同時(shí)去下載6張圖片了。
現(xiàn)在還沒(méi)有currentRadioAccessTechnology的相關(guān)文檔,因此存在一些不正規(guī)或者錯(cuò)誤的用法。當(dāng)你想要獲取當(dāng)前網(wǎng)絡(luò)信號(hào)值,你應(yīng)當(dāng)注冊(cè)一個(gè)CTRadioAccessTechnologyDidChangeNotification通知而不應(yīng)該去輪詢這個(gè)屬性。為了獲取這些通知,你需要使用CTTelephonyNetworkInfo的一個(gè)實(shí)例,注意不要在通知中創(chuàng)建 CTTelephonyNetworkInfo 的實(shí)例,否則會(huì) crash。
在這個(gè)簡(jiǎn)單的例子中,我在block中捕獲并持有了 telephonyInfo,大家可以忽略這個(gè):
1
2
3
4
5
6
7
8
9CTTelephonyNetworkInfo *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);[!--empirenews.page--]
}];
當(dāng)手機(jī)從edge環(huán)境到3G,log輸出應(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
蘋(píng)果導(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)
它確實(shí)做了你所期望的事,讓人費(fèi)解的是蘋(píng)果花了這么長(zhǎng)時(shí)間才把它公開(kāi)。ARC 下,大多數(shù)人在處理返回 Core Foundation 對(duì)象時(shí)是通過(guò)轉(zhuǎn)換成對(duì)等的 NS 對(duì)象來(lái)完成的,如 NSDictionary,即便它只是一個(gè) CFDictionaryRef 然后簡(jiǎn)單地 CFBridgingRelease() 。這樣通常沒(méi)問(wèn)題,除非你返回的對(duì)等 NS 對(duì)象不可用時(shí),如 CFBagRef。你要么使用 id,這樣會(huì)失去類型安全性,要么你將你的方法重命名為 createMethod 并考慮所有的內(nèi)存語(yǔ)義,最后使用 CFRelease。還有一些手段,比如這個(gè),用 non-ARC-file 標(biāo)簽然后編譯,但終歸得使用CFAutorelease()。另外:不要編寫(xiě)使用蘋(píng)果公司命名空間的代碼,所有這些自定義的 CF-宏將來(lái)都會(huì)被打破的。
圖片解壓縮
當(dāng)通過(guò) UIImage 展示一張圖時(shí),在顯示之前需要解壓縮(除非源已經(jīng)像素緩存了)。對(duì)于 JPG/PNG 文件這會(huì)占用相當(dāng)可觀的時(shí)間并會(huì)造成卡頓。iOS6 以前,通常是創(chuàng)建一個(gè)位圖上下文,然后在其中畫(huà)圖來(lái)解決。(參見(jiàn) AFNetworking 如何處理)。
iOS7 開(kāi)始,你可以使用kCGImageSourceShouldCacheImmediately:來(lái)強(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;
}
當(dāng)我剛發(fā)現(xiàn)這一點(diǎn)時(shí)確實(shí)很興奮,但事實(shí)并非如此。在我的測(cè)試中,發(fā)現(xiàn)當(dāng)開(kāi)啟了即時(shí)緩存后性能有明顯的降低。要么這個(gè)方法是在主線程中調(diào)用的(不太可能),感覺(jué)上性能更糟,因?yàn)樗诜椒╟opyImageBlockSetJPEG中鎖住了,而同時(shí)在主線程中在顯示非加密的圖片所致。在我的程序中,我在主線程中加載小的預(yù)覽圖,在后臺(tái)線程中加載大型圖,使用了kCGImageSourceShouldCacheImmediately后小小的解壓縮阻塞了主線程,同時(shí)在后臺(tái)處理大量開(kāi)銷昂貴的操作。
還有更多關(guān)于圖片解壓縮相關(guān)的卻不是 iOS7 中的新東西,像kCGImageSourceShouldCache,它用來(lái)控制系統(tǒng)自動(dòng)卸載解壓縮的圖片數(shù)據(jù)的能力。確保你將它設(shè)置為YES,否則所有的工作都將沒(méi)有意義。有趣的是,蘋(píng)果在64bit運(yùn)行時(shí)的系統(tǒng)中將kCGImageSourceShouldCache的默認(rèn)值從 NO 改為了 YES。
盜版檢查
蘋(píng)果添加了一個(gè)方式,通過(guò) NSBunble 上的新方法appStoreReceiptURL來(lái)評(píng)估Lion系統(tǒng)上 App Store 的收據(jù),同時(shí)也將其移植到了 iOS 上。這使得你可以檢查你的應(yīng)用是在被合法購(gòu)買(mǎi)或者已經(jīng)被破解了。檢查收據(jù)還有一個(gè)重要的原因,它包含了初始購(gòu)買(mǎi)日期,這點(diǎn)對(duì)于把你的應(yīng)用從付費(fèi)模型遷移到免費(fèi)+應(yīng)用內(nèi)付費(fèi)方式很有幫助意義。你可以根據(jù)這個(gè)初始購(gòu)買(mǎi)日期來(lái)決定額外內(nèi)容對(duì)于你的用戶是免費(fèi)的還是收費(fèi)的。
收據(jù)還允許你檢查應(yīng)用程序是否通過(guò)批量購(gòu)買(mǎi)計(jì)劃購(gòu)買(mǎi)以及該許可證是否仍有效,有一個(gè)名為SKReceiptPropertyIsVolumePurchase的屬性顯示了該值。
當(dāng)你調(diào)用appStoreReceiptURL時(shí),你需要特別注意,因?yàn)樵?iOS6 上,它還是一個(gè)私有API,你應(yīng)該在用戶代碼中先調(diào)用doesNotRecognizeSelector:,在調(diào)用前檢查運(yùn)行(基礎(chǔ))版本。在開(kāi)發(fā)期間,這個(gè)方法返回的 URL 不會(huì)是指向一個(gè)文件。你可能需要使用 StoreKit 的SKReceiptRefreshRequest,這也是 iOS7 中的新東西,用它來(lái)下載證書(shū)。使用一個(gè)至少購(gòu)買(mǎi)過(guò)一次的測(cè)試用戶,否則它將沒(méi)法工作:
Refresh the Receipt
SKReceiptRefreshRequest *request = [[SKReceiptRefreshRequest alloc] init];
[request setDelegate:self];
[request start];
驗(yàn)證收據(jù)需要大量的代碼。你需要使用OpenSSL和內(nèi)嵌的蘋(píng)果根證書(shū),并且你還要了解一些基本的東西像是證書(shū)、PCKS容器以及ASN.1。這里有一些樣例代碼,但是你不應(yīng)該讓它這么簡(jiǎn)單——別只是拷貝現(xiàn)有的驗(yàn)證方法,至少做點(diǎn)修改或者編寫(xiě)你自己的,你應(yīng)該不希望一個(gè)普通的補(bǔ)丁程序就能在數(shù)秒內(nèi)瓦解你的努力吧。
你絕對(duì)應(yīng)該讀讀蘋(píng)果的指南——驗(yàn)證 Mac App 商店收據(jù),這里面的大多數(shù)都適用于 iOS。蘋(píng)果在 WWDC2013 的 Session308 “Using Receipts to Protect Your Digital Sales” 中詳述了“Grand Unified Receipt”的變動(dòng)。
Comic Sans MS
iOS7 中,終于迎回了 Comic Sans MS?,F(xiàn)在,它以可下載的字體被添加到 iOS6 中,但當(dāng)時(shí)的字體列表很少也不見(jiàn)得多么有趣。在 iOS7 中蘋(píng)果添加了不少字體,包括“famous”,它和 PT Sans 或 Comic Sans MS 有些類似。kCTFontDownloadableAttribute并沒(méi)有在 iOS6 中聲明,所以 iOS7 以前它并不真正可用,但蘋(píng)果確是在 iOS6 的時(shí)候就已經(jīng)做了私有聲明了。
字體列表是動(dòng)態(tài)變化的,以后可能就會(huì)發(fā)生變動(dòng)。蘋(píng)果在 Tech Note HT5484 中羅列了一些可用的字體,但這個(gè)文檔已經(jīng)過(guò)時(shí)了,同時(shí)也不能反映 iOS7 的變化。[!--empirenews.page--]
這里顯示了你該如何獲取一個(gè)用CTFontDescriptorRef標(biāo)示可下載的字體數(shù)組:
CFDictionary *descriptorOptions = @{(id)kCTFontDownloadableAttribute : @YES};
CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes((CFDictionaryRef)descriptorOptions);
CFArrayRef fontDescriptors = CTFontDescriptorCreateMatchingFontDescriptors(descriptor, NULL);
系統(tǒng)不會(huì)檢查字體是否已存在于磁盤(pán)上而將直接返回同樣的列表。另外,這個(gè)方法可能會(huì)啟用網(wǎng)絡(luò)并造成阻塞,你不應(yīng)該在主線程中使用它。
使用如下基于塊的 API 來(lái)下載字體:
bool CTFontDescriptorMatchFontDescriptorsWithProgressHandler(
CFArrayRef descriptors,
CFSetRef mandatoryAttributes,
CTFontDescriptorProgressHandler progressBlock)
這個(gè)方法能操作網(wǎng)絡(luò)并傳遞下載進(jìn)度信息來(lái)調(diào)用你的progressBlock方法直到下載成功或者失敗。參考蘋(píng)果的 DownloadFont 樣例看如何使用它。
有一些值得注意的地方,這里的字體只在當(dāng)前程序周期內(nèi)有效,下次運(yùn)行將被重新載入內(nèi)存。因?yàn)樽煮w存放在共享空間中,你不能依賴于它們是否可用。很有可能也不能保證的說(shuō),系統(tǒng)會(huì)清理這個(gè)目錄,或者你的程序被拷貝到新的設(shè)備環(huán)境中,而這時(shí)又沒(méi)有這個(gè)字體存在,同時(shí)當(dāng)前處于沒(méi)有網(wǎng)絡(luò)的環(huán)境中。在 Mac 或是模擬器上,你能根據(jù)kCTFontURLAttribute獲得字體的絕對(duì)路徑,加載速度也會(huì)提升,但是在 iOS 上是不可能的,因?yàn)檫@個(gè)目錄在你程序之外,你需要再次調(diào)用CTFontDescriptorMatchFontDescriptorsWithProgressHandler。
你也可以注冊(cè)新的kCTFontManagerRegisteredFontsChangedNotification通知來(lái)跟蹤新字體在何時(shí)載入到了字體注冊(cè)表中。你可以在 WWDC2013 的 Session223 “Using Fonts with TextKit”中查找更多信息。
這還不夠?
沒(méi)關(guān)系,iOS7 的新東西遠(yuǎn)不止如此!了解一下 NSHipster 你將明白語(yǔ)音合成相關(guān)的東西,base64、NSURLComponents、NSProgress、bar codes、reading lists 以及 CIDetectorEyeBlink。還有很多我們沒(méi)有涵蓋到的,比如蘋(píng)果 iOS7 的 API 變化,iOS 指南的新東西以及 Foundation Release Notes(這些都是服務(wù)于 OS X的,但是代碼都是共享的,也同樣適用于 iOS)。很多方法都還沒(méi)形成文檔,等著你來(lái)探究和 blog。