iOS界面跳轉(zhuǎn)以及一些優(yōu)化方案

原文地址: http://blog.startry.com/2016/02/14/Think-Of-UIViewController-Switch/

iOS界面跳轉(zhuǎn)的一些優(yōu)化方案

App應(yīng)用程序開發(fā), 界面跳轉(zhuǎn)是基礎(chǔ)中的基礎(chǔ), 幾乎沒有一個(gè)App是用不到界面跳轉(zhuǎn)的, 那么怎么樣去書寫界面跳轉(zhuǎn)代碼才是比較合理的呢?

大家可能在想跳轉(zhuǎn)無非就2種方式, 能有什么內(nèi)容? 其實(shí)并不是這樣子的, 對于研發(fā)老手來說, 大型應(yīng)用幾乎都是利用URLScheme進(jìn)行全方位的解決方案; 對于研發(fā)新手來說, 他們可能并沒有遇到多路口界面跳轉(zhuǎn)的瓶頸, 只會(huì)使用一些常用跳轉(zhuǎn), 并不會(huì)意識到界面跳轉(zhuǎn)潛在的一些問題, 甚至無法嚴(yán)格區(qū)分Present和Push的操作區(qū)別~

本文將針對界面跳轉(zhuǎn)提出一些優(yōu)化解決方案~

常用跳轉(zhuǎn)方式

iOS常用的跳轉(zhuǎn)方式只有兩種PresentPush。Push和Present最直觀的區(qū)別是默認(rèn)的轉(zhuǎn)場效果, Present的默認(rèn)轉(zhuǎn)場效果是自下而上的, Push的轉(zhuǎn)場效果是自右到左的。Push往往需要搭配UINavigationController來使用。

Push跳轉(zhuǎn)使用示意:

1

UIViewController *nextViewController = [[UIViewController alloc] init];nextViewController.title = @"第二個(gè)界面";[self.navigationController pushViewController:nextViewController animated:YES];

Present跳轉(zhuǎn)使用示意:

1

UIViewController *nextViewController = [[UIViewController alloc] init];nextViewController.title = @"第二個(gè)界面";[self presentViewController:nextViewContrller animated:YES completed:nil];

在大部分情況下, iOS研發(fā)者都在濫用Push的跳轉(zhuǎn)方式, 往往一個(gè)App僅僅包含一個(gè)UINavigationController。產(chǎn)生濫用的原因是因?yàn)镻resent的跳轉(zhuǎn)太過難于定制轉(zhuǎn)場效果, 僅僅只能使用系統(tǒng)提供的4種打開方式。

在滿足產(chǎn)品要求的前提下, 其實(shí)可以基于模塊內(nèi)跳轉(zhuǎn)模塊外跳轉(zhuǎn)的思量去考慮采用Present的方式還是Push的方式去跳轉(zhuǎn)界面。

PS: 還有一種界面切換方式是利用ChildViewController, 進(jìn)行獨(dú)立界面的控制。需要高度定制的界面可以采用這種方式, 例如基于地圖或者相機(jī)的應(yīng)用?;贑hildViewController的方式內(nèi)容篇幅太長, 在本文就暫時(shí)不介紹了, 以后再補(bǔ)充~

常用跳轉(zhuǎn)方式瓶頸

常用的跳轉(zhuǎn)方式其實(shí)已經(jīng)幾乎可以滿足我們所有的跳轉(zhuǎn), 但是欠缺一層業(yè)務(wù)層次的高層級封裝

舉一個(gè)實(shí)際使用場景, 某App支持4種頁面打開方式:

Push推送

App外部網(wǎng)頁打開

App內(nèi)部網(wǎng)頁打開

應(yīng)用內(nèi)點(diǎn)擊打開

這四種方式均跳轉(zhuǎn)到DetailViewController界面。普通的跳轉(zhuǎn)依然可以滿足該場景, 最簡單的解決方案是在四個(gè)不同的地方都寫一個(gè)獨(dú)立的界面打開邏輯。

作為一名業(yè)務(wù)模塊人才, 如此不復(fù)用代碼合理么? 作為一名App架構(gòu)師, 如此冗余四份入口代碼合適么?

如果您覺得不合適, 那自然需要去抽離代碼到統(tǒng)一的地方去書寫一套統(tǒng)一的管理邏輯; 如果您覺得合適, 那么您會(huì)設(shè)想什么方案去根據(jù)外部網(wǎng)頁以及Push內(nèi)容去轉(zhuǎn)化代碼至普通跳轉(zhuǎn)代碼呢?

URLScheme解決方案

我們先根據(jù)前面提到的四種場景進(jìn)行場景分析:

外部網(wǎng)頁場景:

只能使用iOS自帶的URLScheme的方式去打開App, 然后通過AppDelegate中事件去獲取對應(yīng)的URL進(jìn)行匹配

Push推送場景:

在extra字段中定義個(gè)鏈接字段, 鏈接字段是個(gè)字符串或者數(shù)字代號, 用于在AppDelegate中事件去獲取對應(yīng)的代號進(jìn)行匹配; 既然代碼是自定義的, 那自然可以定義成一個(gè)URL了

內(nèi)部網(wǎng)頁場景

熟悉iOS WebView開發(fā)的童鞋們都知道, UIWebView的JS交互本質(zhì)上是通過截獲URL請求去實(shí)現(xiàn)的, 那么既然是傳遞URL地址, 就可以和外部網(wǎng)頁使用相關(guān)的方式, 只不過在不同的位置進(jìn)行對應(yīng)的URL匹配

應(yīng)用內(nèi)點(diǎn)擊打開

可以采用普通打開方式, 也可以通過一個(gè)抽離的URLScheme匹配器去匹配打開

根據(jù)上述四個(gè)場景, 我們可以發(fā)現(xiàn), 解決上述四個(gè)應(yīng)用場景, 我們需要的是引入一個(gè)抽離的URLScheme匹配器去匹配打開輪轉(zhuǎn)界面~

利用URLScheme的方式進(jìn)行一層封裝, 幾乎可以完美解決多入口打開App的邏輯復(fù)用問題。

PS: 一般情況下, URLScheme的抽離器不需要自己封裝, 可以使用開源現(xiàn)成的, 很少場景需要高度定制。

開源URLScheme解決方案:

RoutableAndroid和iOS均支持的一款權(quán)威的應(yīng)用內(nèi)URL跳轉(zhuǎn)路由, 幾乎可以滿足所有需求

代碼切入性比較低, 沒有冗余的繼承封裝。

可以指定NavigationController, 方便定制ChildController的跳轉(zhuǎn)

可以多個(gè)Router組合使用, 靈活性高

urlmananger國內(nèi)技術(shù)問題網(wǎng)站segmentfault.com開發(fā)者抽離的一個(gè)跳轉(zhuǎn)器

需要嵌入繼承和綁定使用NavigationController, 架構(gòu)設(shè)計(jì)層級嵌入性很高, 沒有Routable合理

無法多個(gè)組合使用, 靈活性沒有Routable高

封裝層次高, 快速使用可以采用

個(gè)人比較傾向使用Routable, 因?yàn)椴]有在架構(gòu)上對代碼進(jìn)行嵌入, 比較符合開發(fā)者口味~

以Routable作為示例, 本文可以通過如下代碼在App啟動(dòng)的時(shí)候就提前注冊(PS: Push點(diǎn)擊打開執(zhí)行Optional參數(shù)之前注冊)

1

[[Routable sharedRouter] map:@"detail/:id" toController:[DetailController class]];

假設(shè)您的App URLScheme前綴為demo123, 您只需要在上述四個(gè)場景分別傳遞demo123://detail/88過來即可。88只是示例的一個(gè)隨意亂寫的id編號, 作為Restful分格的參數(shù)進(jìn)行處理。

URLScheme些許問題

URLScheme進(jìn)行界面跳轉(zhuǎn)的解決方案也不是完美的, 個(gè)人開發(fā)時(shí)候遇到最大的問題就是傳值問題

怎么傳遞對象值

URLScheme原則上不支持傳遞復(fù)雜的對象, 通過URLScheme方式打開的界面理論上每個(gè)界面都相對保持邏輯獨(dú)立(邏輯獨(dú)立的代價(jià)往往是犧牲細(xì)微的用戶體驗(yàn)), 邏輯獨(dú)立的界面可以有更好的架構(gòu)設(shè)計(jì)

通過外部URL或者Push的方式是無法傳遞對象的, 可以不用考慮傳遞對象的場景

應(yīng)用內(nèi)界面跳轉(zhuǎn)可以根據(jù)實(shí)際場景去區(qū)分使用URLScheme的方式還是普通的方式進(jìn)行界面跳轉(zhuǎn)控制

怎么傳遞URL值

URLScheme打開的界面有時(shí)候也需要傳遞URL值用于對應(yīng)的界面, 最常見的是打開圖片管理器以及打開WebView的界面, 這種場景可以采用約定加密的方式進(jìn)行處理, 對傳遞的URL進(jìn)行URIEncode和取值時(shí)候的URIDecode。

Push長度限制

坑爹的APNs規(guī)定了Push內(nèi)容的總長度不能大于255字節(jié), 那么URLScheme的參數(shù)傳遞就收到限制。

最常用的解決方案是壓縮字段名字和內(nèi)容, 傳遞的字段勁量用一個(gè)單詞表示, 值字段可以隱藏掉URLScheme的前綴, 只保留后綴以及參數(shù)。

還有一種通過的Push長度解決方案是, push只是觸發(fā)器, 觸發(fā)App請求去獲取真正的內(nèi)容來繞過長度限制。

傳值對象

此處針對應(yīng)用內(nèi)跳轉(zhuǎn)使用url scheme還是普通方式進(jìn)行一些議論, 個(gè)人覺得一套應(yīng)用里如果有兩種方式跳轉(zhuǎn), 雖然靈活性高, 但是比較難以控制, 因此最好都采用一套方式跳轉(zhuǎn), 那自然是使用url scheme。那復(fù)雜傳值的問題依舊無法得到解決。

有些開發(fā)者為了省時(shí)間, 直接通過類似Notification的方式或者用單例對象去維護(hù)進(jìn)行值傳遞, 也不失為一個(gè)方法。

但是我在思考, 有沒有一種相對完美的解決方案, 能夠?qū)髦祮栴}徹底用url scheme進(jìn)行傳遞呢?

結(jié)合本人喜歡使用的庫JSONModel, 我想到了一種暴力且耗時(shí)的解決方案, 但是至少不產(chǎn)生耦合哈~

暴力解決方案步驟:

JSON化對象

將對象JSON字符串Base64加密

將Base64加密后的字符串作為url參數(shù)傳遞

接受者處理參數(shù)的時(shí)候反Base64解密

將解密后的JSON對象用模型實(shí)例化

針對暴力解決方案, 本人設(shè)想了四個(gè)自問自答:

為什么不直接Base64對象而需要將其JSON字符串話呢?

答: 為了接受者解密后的直觀性。在大型App開發(fā)過程中, 解密者解析后不一定知道用哪個(gè)模型去實(shí)例化JSON字符串, 通過這種方式, 解密者可以不關(guān)心接受數(shù)據(jù)后的模型實(shí)例而自由發(fā)揮。

利用這種方式暴力解決后, url會(huì)不會(huì)很長?

答: 這種方式傳遞的url會(huì)超級長, 但是在應(yīng)用內(nèi)進(jìn)行頁面處理的場景, 不需要可以的去考慮url的長度。但是url的長度可能會(huì)影響解析的性能。

為什么不直接通過廣播或者單例維護(hù)的方式傳值?

答: 為了解耦。 大型App維護(hù)的時(shí)候, 如果一個(gè)內(nèi)存對象是公用的, 是十分難以維護(hù)的, 應(yīng)該盡量減少傳遞對象之間的耦合。

為什么需要base64加密而不直接采用uriencode的方式?

答: 為了解決模型嵌套的問題。因?yàn)橐粋€(gè)模型里可能會(huì)嵌套另外一個(gè)模型, 當(dāng)然通過JSON字符串本身可以實(shí)現(xiàn)模型嵌套的解決, 那可以有更加節(jié)省性能的解決方案。

本人水平有限, 此處的暴力的方式是目前本人覺得相對耦合度低并且比較好的一種解決方案。對于目前的iOS手機(jī)設(shè)備來說, 這點(diǎn)JSON以及解密的性能并不能影響整個(gè)App的運(yùn)行, 因此采用這種方式進(jìn)行暴力解決。

總結(jié)

頁面輪轉(zhuǎn)在小型的App并不需要針對單獨(dú)進(jìn)行優(yōu)化設(shè)計(jì), 但是對于上20w行代碼的大型App來說, 往往都是需要針對優(yōu)化的。

本文引用了市面上最通用的URLScheme的解決方案來進(jìn)行頁面跳轉(zhuǎn)的設(shè)計(jì)優(yōu)化, 并建議采用開源庫Routable來進(jìn)行統(tǒng)一管理。

根據(jù)個(gè)人在界面跳轉(zhuǎn)開發(fā)中遇到的困難, 提出了幾個(gè)相應(yīng)的瓶頸和對應(yīng)的解決方案。針對傳遞對象值提出了一種暴力但是耦合度比較低的解決方案。

PS: 本人水平有限, 有錯(cuò)誤的地方還望大家及時(shí)指出~ 謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容