當(dāng) iOS 7 剛發(fā)布的時(shí)候,全世界的蘋(píng)果開(kāi)發(fā)人員都立馬嘗試著去編譯他們的 app,接著再花上數(shù)月的時(shí)間來(lái)修復(fù)任何出現(xiàn)的錯(cuò)誤议经,甚至從頭開(kāi)始重建這個(gè) app横堡。這樣的結(jié)果埋市,使得人們根本無(wú)暇去探究 iOS 7 所帶來(lái)的新思想。除開(kāi)一些明顯而細(xì)微的更新命贴,比如說(shuō) NSArray 的firstObject方法——這個(gè)方法可追溯到 iOS 4 時(shí)代道宅,現(xiàn)在被提為公有 API——還有很多隱藏的技巧等著我們?nèi)ネ诰颉?br>
平滑淡入淡出動(dòng)畫(huà)
我在這里要討論的并非新的彈性動(dòng)畫(huà) API 或者 UIDynamics,而是一些更細(xì)微的東西胸蛛。CALayer 增加了兩個(gè)新方法:allowsGroupOpacity和allowsEdgeAntialiasing∥垡穑現(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à)將會(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ì)成鋸齒狀(pixelerated)巷查。
如果你想要復(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)用從:
但是舅逸,絕大多數(shù)情況下這樣也能工作得很好皇筛,只要你不直接跟 CALayer 打交道。
iOS 7 中旗笔,我有很多代碼路徑(主要是UITableViewCells)需要額外保護(hù)以防止意外的動(dòng)畫(huà)蝇恶,例如,如果一個(gè)彈窗(popover)的大小調(diào)整了潘懊,與此同時(shí)其中的表視圖將因?yàn)楦叨鹊淖兓虞d新的 cell授舟。我通常的做法是將整個(gè)layoutSubviews的代碼包扎到一個(gè)動(dòng)畫(huà)塊中:
處理長(zhǎng)的表視圖
UITableView非称袢矗快速高效,除非你開(kāi)始使用tableView:heightForRowAtIndexPath:署浩,它會(huì)開(kāi)始為你表中每一個(gè)元素調(diào)用此方法炊汤,即便沒(méi)有可視對(duì)象——這是為了讓其下層的UIScrollView能獲取正確的contentSize抢腐。此前有一些變通方法襟交,但都不好用捣域。iOS 7 中焕梅,蘋(píng)果公司終于承認(rèn)這一問(wèn)題,并添加了tableView:estimatedHeightForRowAtIndexPath:斜棚,這個(gè)方法把絕大部分計(jì)算成本推遲到實(shí)際滾動(dòng)的時(shí)候赫舒。如果你完全不知道一個(gè) cell 的大小谒府,返回UITableViewAutomaticDimension就行了。
對(duì)于段頭/尾(section headers/footers)滞乙,現(xiàn)在也有類(lèi)似的 API 了鉴嗤。
UISearchDisplayController
蘋(píng)果的 search controller 使用了新的技巧來(lái)簡(jiǎn)化移動(dòng) search bar 到 navigation bar 的過(guò)程醉锅。啟用displaysSearchBarInNavigationBar就可以了(除非你還在用 scope bar硬耍,那你就太不幸了)。我倒是很喜歡這么做狸窘,但遺憾的是翻擒,iOS 7 上的UISearchDisplayController貌似被破壞得相當(dāng)嚴(yán)重陋气,尤其在 iPad 上引润。蘋(píng)果公司看上去像是沒(méi)時(shí)間處理這個(gè)問(wèn)題椰拒,對(duì)于顯示的搜索結(jié)果并不會(huì)隱藏實(shí)際的表視圖燃观。在 iOS 7 之前,這不算問(wèn)題番川,但是現(xiàn)在searchResultsTableView有一個(gè)透明的背景色颁督,使它看上去相當(dāng)糟糕沉御。作為一種變通方法吠裆,你可以設(shè)置不透明背景色或者采取一些更富于技巧的手段來(lái)獲得你期望的效果试疙。關(guān)于這個(gè)控件我碰到過(guò)各種各樣的結(jié)果祝旷,當(dāng)使用displaysSearchBarInNavigationBar時(shí)甚至根本不會(huì)顯示搜索表視圖。
你的結(jié)果可能有所不同距贷,但我依賴(lài)于一些手段(severe hacks)來(lái)讓displaysSearchBarInNavigationBar工作:
目前而言,雖然這不一定對(duì)大多數(shù)網(wǎng)站都有用滨溉,但它肯定是生成簡(jiǎn)單的電子書(shū)閱讀器或者顯示文本的一種更好的方式晦攒。加點(diǎn)樂(lè)子的話脯颜,請(qǐng)嘗試將它設(shè)置為UIWebPaginationModeBottomToTop贩据。
會(huì)飛的 Popover
想知道為什么你的 popover 瘋了一樣到處亂飛饱亮?在UIPopoverControllerDelegate協(xié)議中有一個(gè)新的代理方法讓你能控制它:
當(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í)間來(lái)驗(yàn)證的問(wèn)題——如果你通過(guò)改變preferredContentSize來(lái)動(dòng)態(tài)調(diào)整你的 popover帮毁,那么這個(gè)方法就尤其需要實(shí)現(xiàn)作箍。蘋(píng)果公司現(xiàn)在對(duì)改變 popover 大小的請(qǐng)求更嚴(yán)格胞得,如果沒(méi)有預(yù)留足夠的空間阶剑,popover 將會(huì)到處移動(dòng)牧愁。
鍵盤(pán)支持
蘋(píng)果公司不只為我們提供了全新的 framework 用于游戲控制器猪半,它也給了我們這些鍵盤(pán)愛(ài)好者一些關(guān)注!你會(huì)發(fā)現(xiàn)新定義的公用鍵沽甥,比如UIKeyInputEscape或UIKeyInputUpArrow摆舟,可以使用全新的UIKeyCommand類(lèi)截查恨诱。在 iOS 7 之前照宝,只能通過(guò)一些難以言表的手段來(lái)處理鍵盤(pán)命令痕鳍,現(xiàn)在,就讓我們操起藍(lán)牙鍵盤(pán)試試看我們能用這個(gè)做什么旨别!
開(kāi)始之前秸弛,你需要對(duì)響應(yīng)鏈(responder chain)有個(gè)了解洪碳。你的UIApplication繼承自UIResponder瞳腌,UIView和UIViewController也是如此嫂侍。如果你曾經(jīng)處理過(guò)UIMenuItem并且沒(méi)有使用我的基于塊的包裝的話挑宠,那么你對(duì)此已經(jīng)有所了解。事件先被發(fā)送到最上層的響應(yīng)者懒鉴,然后一級(jí)級(jí)往下傳遞直到 UIApplication临谱。為了捕獲按鍵命令吴裤,你需要告訴系統(tǒng)你關(guān)心哪些按鍵命令(而不是全捕獲)。為了完成這個(gè)鞭缭,你需要重寫(xiě)keyCommands這個(gè)新屬性:
現(xiàn)在可別太激動(dòng)岭辣,需要注意的是沦童,這個(gè)方法只在鍵盤(pán)可見(jiàn)時(shí)有效(比如有類(lèi)似UITextView這樣的對(duì)象作為第一響應(yīng)者時(shí))偷遗。對(duì)于全局熱鍵氏豌,你仍然需要用上面提到的 hack 方法。除去那些泪电,這個(gè)解決途徑還是很優(yōu)雅的相速。不要覆蓋類(lèi)似 cmd-V 這種系統(tǒng)的快捷鍵突诬,它會(huì)被自動(dòng)映射到paste:方法攒霹。
還有一些新的預(yù)定義的響應(yīng)者行為:
- (void)increaseSize:(id)senderNS_AVAILABLE_IOS(7_0);
- (void)decreaseSize:(id)senderNS_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)或者適應(yīng)默認(rèn)樣式的默認(rèn)鍵盤(pán)的擴(kuò)展(工具條)稿饰。這個(gè)類(lèi)一開(kāi)始就存在了喉镰,不過(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游昼,看上去 Safari 里仍然使用著UIToolbar烘豌。
了解你的無(wú)線電通信
雖然早在 iOS 4 的時(shí)候廊佩,大部分的運(yùn)營(yíng)商信息已經(jīng)在 CTTelephony 暴露了标锄,但它通常只用于特定場(chǎng)景并非十分有用。iOS 7 中谓松,蘋(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)文檔艘蹋,為了讓它工作,會(huì)遇到一些麻煩和錯(cuò)誤宅荤。當(dāng)你想要獲取當(dāng)前網(wǎng)絡(luò)信號(hào)值冯键,你應(yīng)當(dāng)注冊(cè)一個(gè)CTRadioAccessTechnologyDidChangeNotification通知而不是去輪詢(xún)這個(gè)屬性惫确。為了確切的使 iOS 發(fā)送這些通知雕薪,你需要持有一個(gè)CTTelephonyNetworkInfo的實(shí)例所袁,但不要在通知中創(chuàng)建CTTelephonyNetworkInfo的實(shí)例燥爷,否則會(huì) crash前翎。
在這個(gè)簡(jiǎn)單的例子中港华,因?yàn)樵?block 中捕獲telephonyInfo將會(huì)持有它立宜,所以我就這么用了:
當(dāng)手機(jī)從 Edge 環(huán)境切換到 3G橙数,日志輸出應(yīng)該像這樣:
蘋(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í)間:
CFTypeRefCFAutorelease(CFTypeRef CF_RELEASES_ARGUMENT arg)
它的確做了你所期望的事咕幻,讓人費(fèi)解的是蘋(píng)果花了這么長(zhǎng)時(shí)間才把它公開(kāi)肄程。ARC 下蓝厌,大多數(shù)人在處理返回 Core Foundation 對(duì)象時(shí)是通過(guò)轉(zhuǎn)換成對(duì)等的 NS 對(duì)象來(lái)完成的拓提,如返回一個(gè)NSDictionary代态,雖然它是一個(gè)CFDictionaryRef蹦疑,簡(jiǎn)單地使用CFBridgingRelease()就行了歉摧。這樣通常沒(méi)問(wèn)題叁温,除非你返回的沒(méi)有可用的對(duì)等 NS 對(duì)象券盅,如CFBagRef锰镀。你要么使用 id泳炉,這樣會(huì)失去類(lèi)型安全性嚎杨,要么你將你的方法重命名為createMethod并考慮所有的內(nèi)存語(yǔ)義花鹅,最后使用 CFRelease。還有一些手段枫浙,比如這個(gè)刨肃,使用 non-ARC-file 參數(shù)你才能編譯它,但終歸得使用 CFAutorelease()箩帚。另外:不要編寫(xiě)使用蘋(píng)果公司命名空間的代碼真友,所有這些自定義的 CF-宏將來(lái)都會(huì)被打破的紧帕。
圖片解壓縮
當(dāng)通過(guò)UIImage展示一張圖片時(shí)盔然,在顯示之前需要解壓縮(除非圖片源已經(jīng)像素緩存了)。對(duì)于 JPG/PNG 文件這會(huì)占用相當(dāng)可觀的時(shí)間并會(huì)造成卡頓是嗜。iOS 6 以前愈案,通常是通過(guò)創(chuàng)建一個(gè)位圖上下文,然后在其中畫(huà)圖來(lái)解決鹅搪。(參見(jiàn) AFNetworking 如何處理這個(gè)問(wèn)題)站绪。
從 iOS 7 開(kāi)始,你可以使用kCGImageSourceShouldCacheImmediately: 強(qiáng)制圖片在創(chuàng)建時(shí)直接解壓縮:
剛發(fā)現(xiàn)這一點(diǎn)時(shí)我很很興奮涩嚣,但不要高興得太早崇众。在我的測(cè)試中,開(kāi)啟即時(shí)緩存后性能實(shí)際上有所降低航厚。要么這個(gè)方法最終是在主線程中被調(diào)用的(好像不太可能)顷歌,要么感官上的性能下降是因?yàn)槠湓诜椒╟opyImageBlockSetJPEG中鎖住了,因?yàn)檫@個(gè)方法也被用在主線程顯示非加密的圖片時(shí)幔睬。在我的 app 中眯漩,我在主線程中加載小的預(yù)覽圖,在后臺(tái)線程中加載大型圖,使用了kCGImageSourceShouldCacheImmediately后小小的解壓縮阻塞了主線程赦抖,同時(shí)在后臺(tái)處理大量開(kāi)銷(xiāo)昂貴的操作舱卡。
還有更多關(guān)于圖片解壓縮的卻不是 iOS 7 中的新東西,像kCGImageSourceShouldCache队萤,它用來(lái)控制系統(tǒng)自動(dòng)卸載解壓縮圖片數(shù)據(jù)的能力轮锥。確保你將它設(shè)置為 YES,否則所有的工作都將沒(méi)有意義要尔。有趣的是舍杜,蘋(píng)果在 64-bit 運(yùn)行時(shí)的系統(tǒng)中將kCGImageSourceShouldCache的默認(rèn)值從 NO 改為了 YES。
盜版檢查
蘋(píng)果添加了一個(gè)方式赵辕,通過(guò) NSBunble 上的新方法appStoreReceiptURL來(lái)獲取和驗(yàn)證 Lion 系統(tǒng)上 App Store 的收據(jù)既绩,現(xiàn)在終于也移植到了 iOS 上了。這使得你可以檢查你的應(yīng)用是合法購(gòu)買(mǎi)的還是被破解了的还惠。檢查收據(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ì)于你的用戶(hù)是免費(fèi)(因?yàn)樗麄円呀?jīng)付過(guò)費(fèi)了)還是收費(fèi)的救欧。
收據(jù)還允許你檢查應(yīng)用程序是否通過(guò)批量購(gòu)買(mǎi)計(jì)劃購(gòu)買(mǎi)以及該許可證是否仍有效,有一個(gè)名為SKReceiptPropertyIsVolumePurchase的屬性標(biāo)示了該值嚎幸。
當(dāng)你調(diào)用appStoreReceiptURL時(shí)颜矿,你需要特別注意,因?yàn)樵?iOS 6 上嫉晶,它還是一個(gè)私有 API骑疆,你應(yīng)該在用戶(hù)代碼中先調(diào)用doesNotRecognizeSelector:,在調(diào)用前檢查運(yùn)行(基礎(chǔ))版本替废。在開(kāi)發(fā)期間箍铭,這個(gè)方法返回的 URL 不會(huì)指向一個(gè)文件。你可能需要使用 StoreKit 的SKReceiptRefreshRequest椎镣,這也是 iOS 7 中的新東西诈火,用它來(lái)下載證書(shū)。使用一個(gè)至少有過(guò)一次購(gòu)買(mǎi)的測(cè)試用戶(hù)状答,否則它將沒(méi)法工作:
驗(yàn)證收據(jù)需要大量的代碼冷守。你需要使用 OpenSSL 和內(nèi)嵌的蘋(píng)果根證書(shū),并且你還要了解一些基本的東西像是證書(shū)惊科、PCKS 容器以及ASN.1拍摇。這里有一些樣例代碼,但是你不應(yīng)該讓它這么簡(jiǎn)單——尤其是對(duì)那些有“高尚意圖”的人馆截,別只是拷貝現(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)果在WWDC 2013 的 Session 308 “Using Receipts to Protect Your Digital Sales”中詳述了通過(guò)新加入的“Grand Unified Receipt”而帶來(lái)的變動(dòng)。
Comic Sans MS
承認(rèn)吧幕随,你是懷念 Comic Sans MS 的蚁滋。在 iOS 7 中,Comic Sans MS 終于回來(lái)了赘淮。iOS 6 中添加了可下載字體枢赔,但那時(shí)的字體列表很少也不見(jiàn)得有趣。在 iOS 7 中蘋(píng)果添加了不少字體拥知,包括 “famous”,它和PT Sans或Comic Sans MS有些類(lèi)似碎赢。kCTFontDownloadableAttribute并沒(méi)有在 iOS 6 中聲明低剔,所以 iOS 7 之前它并不真正可用,但蘋(píng)果確是在 iOS 6 的時(shí)候就已經(jīng)做了私有聲明了肮塞。
字體列表是動(dòng)態(tài)變化的襟齿,以后可能就會(huì)發(fā)生變動(dòng)。蘋(píng)果在Tech Note HT5484中羅列了一些可用的字體枕赵,但這個(gè)文檔已經(jīng)過(guò)時(shí)了猜欺,并不能反映 iOS 7 的變化。
這里顯示了你該如何獲取一個(gè)用CTFontDescriptorRef標(biāo)示的可下載的字體數(shù)組:
系統(tǒng)不會(huì)檢查字體是否已存在于磁盤(pán)上而將直接返回同樣的列表拷窜。另外开皿,這個(gè)方法可能會(huì)啟用網(wǎng)絡(luò)并造成阻塞,你不應(yīng)該在主線程中使用它篮昧。
使用如下基于塊的 API 來(lái)下載字體:
這個(gè)方法能操作網(wǎng)絡(luò)并傳遞下載進(jìn)度信息來(lái)調(diào)用你的progressBlock方法直到下載成功或者失敗赋荆。參考蘋(píng)果的DownloadFont 樣例看看如何使用它。
有一些值得注意的地方懊昨,這里的字體只在當(dāng)前程序運(yùn)行時(shí)有效窄潭,下次運(yùn)行將被重新載入內(nèi)存。因?yàn)樽煮w存放在共享空間中酵颁,你不能依賴(lài)于它們是否可用嫉你。很有可能但不能保證地說(shuō),系統(tǒng)會(huì)清理這個(gè)目錄躏惋,或者你的程序被拷貝到?jīng)]有這個(gè)字體的新設(shè)備中幽污,同時(shí)你又沒(méi)有網(wǎng)絡(luò)。在 Mac 或是模擬器上其掂,你能根據(jù)kCTFontURLAttribute獲得字體的絕對(duì)路徑油挥,加載速度也會(huì)提升,但是在 iOS 上是不行的,因?yàn)檫@個(gè)目錄在你的程序之外深寥,你需要再次調(diào)用CTFontDescriptorMatchFontDescriptorsWithProgressHandler攘乒。
你也可以注冊(cè)新的kCTFontManagerRegisteredFontsChangedNotification通知來(lái)跟蹤新字體在何時(shí)被載入到了字體注冊(cè)表中。你可以在WWDC 2013 的 Session 223 “Using Fonts with TextKit”中查找更多信息惋鹅。
這還不夠?
沒(méi)關(guān)系则酝,iOS 7 的新東西遠(yuǎn)不止如此!了解一下NSHipster你將明白語(yǔ)音合成相關(guān)的東西闰集,base64沽讹、全新的NSURLComponents、NSProgress武鲁、條形碼掃描爽雄、閱讀列表以及CIDetectorEyeBlink。還有很多我們沒(méi)有涵蓋到的沐鼠,比如蘋(píng)果的iOS 7 API 變化挚瘟,What's new in iOS指南以及Foundation Release Notes(這些都是基于 OS X的,但是代碼都是共享的饲梭,很多也同樣適用于 iOS)乘盖。很多新方法都還沒(méi)形成文檔,等著你來(lái)探究和寫(xiě)成博客憔涉。