iOS一個絲滑的全屏滑動返回手勢

全屏返回手勢

自 iOS7 以後,Apple 增長了 屏幕邊緣右劃返回 交互的支持,再配合上 UINavigationController 的交互式動畫,pop 到上一級頁面的操做變的很是順暢和絲滑,今後,我不多再使用點擊左上角導航欄上的返回按鈕的方式返回了,由於這對單手操做十分不友好;若是一個 App 竟然膽敢不支持滑動返回,那離被卸載就不遠了。git

說到 全屏返回手勢 ,首先我感受這件事自己可能就有問題,畢竟有點反蘋果官方的交互,讓用戶從任意的地方都可以滑動返回這個交互在國內的 App 中很是廣泛,好比我手機中的手Q、微博、網易新聞、大衆點評等,固然還有百度知道- -。這裏得對微信的產品經理們得點個贊,從整個 App 來看,不管是交互仍是 UI 結構和樣式都很是的 iOS,沒有什麼特別奇葩的頁面和交互,以致於使用 UIKit 原生的框架能夠很是簡單的搭建起來,這也符合我我的對 App 的一個願景: 一個優秀的 App 不論從用戶角度看仍是從代碼角度看都應該是簡單且優雅的 ,呼籲各家產品經理能夠多借鑑下像微信這樣很本色的 App 設計。(之後能夠分享下如何使用 Storyboard 在一小時內快速搭建起微信 UI)github

FDFullscreenPopGesture

工做畢竟是工做,因而乎因此就被迫實現了套 pan 手勢處理加截圖和視差,雖然在運動曲線上、bar 截圖處理上下了很多功夫,但距離系統的絲滑效果仍是差距挺遠。隨時間推移,終於可以最低支持 iOS7 後,咱們把這個問題再次拿出來討論和研究,直到在微博上看到了 J_雨 同窗的 這篇文章後才找到了這個迄今爲止最簡單的解決方案。因而乎在他的受權下,咱們在 forkingdog 上把這個返回手勢開源, github地址 ,並果斷應用到了百度知道 App 內,這是 Demo 效果:安全

iOS一個絲滑的全屏滑動返回手勢

利用了系統本身的邊緣返回手勢處理函數後,一切動畫和曲線都和原生效果一毛同樣了。微信

因而乎發佈了FDFullscreenPopGesture1.0 版本,並且提供了一個 AOP 形式的 API,把它添加到工程裏面,什麼代碼都不用寫,全部 UINavigationController 就自帶這個全屏返回效果了。框架

絲滑的處理導航欄的顯示和隱藏

接下來咱們發現利用系統的 UINavigationBar 時,返回手勢中若碰到前一個頁面有 bar,後一個頁面沒 bar,或者反過來時,動畫就很是難看,舉兩個反例:函數

手Q iOS:工具

iOS一個絲滑的全屏滑動返回手勢

它的我的中心頁面上面的 bar 是隱藏狀態,而後作了個和其餘頁面很像的假 bar,但返回手勢一開始就露餡了,爲了彌補,還作了下後面真 bar 的 alpha 值動畫,兩個返回按鈕仍是重疊在了一塊兒。學習

新浪微博 iOS:動畫

iOS一個絲滑的全屏滑動返回手勢

和手Q同樣的實現方式,只不過沒作 alpha 動畫,因此就很是明顯了。spa

爲啥會這樣呢?這可能就是 UINavigationController 在導航欄控制 API 上設計的缺陷了。 一個 UINavigationController 管理了串行的 N 個 UIViewController 棧式的 push 和 pop,而 UINavigationBar 由 UINavigationController 管理,這就致使了 UIViewController 沒法控制本身上面的 bar 單獨的隱藏或顯示。 這很是像 UIApplication 全局的 status bar,牽一髮還得動全身,不過 Apple 在 iOS7 以後爲 vc 控制本身的 status bar 提供了下面幾個方法:

- (UIStatusBarStyle)preferredStatusBarStyle NS_AVAILABLE_IOS(7_0);
- (BOOL)prefersStatusBarHidden NS_AVAILABLE_IOS(7_0);
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation NS_AVAILABLE_IOS(7_0);

 

終於讓這個 全局變量 變成了 局部變量 ,雖然寫起來費勁了些。

可是對 UINavigationBar 的控制,依然是全局的,可能 Apple 以爲 App 不該該有這種奇怪的頁面結構?

解決這個問題的方法也不難,在滑動返回的後要出現的那個 view controller 中寫下面的代碼:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.navigationController setNavigationBarHidden:YES animated:animated];
}

系統就會把有 bar 和 無 bar 的 transition 動畫銜接起來。可是如上面所說,這是個全局變量,還得在全部由這個沒有 bar 的特殊頁面能 push 和 pop 的頁面都進行反向的處理,代碼很是的亂乎。因而乎,咱們試着解決了這個問題,先看效果:

iOS一個絲滑的全屏滑動返回手勢

我特地挑了個從真 bar 到假 bar,再從假 bar 到 真 bar 的頁面,還算蠻絲滑的,transition 動畫全是系統本身搞定的。

就事把FDFullscreenPopGesture更新到了 1.1 版本,貫徹咱們一貫的精簡 API,你只須要在 bar 要隱藏的 view controller 中寫一句話:

- (void)viewDidLoad
    [super viewDidLoad]; self.navigationController.fd_prefersNavigationBarHidden = YES;
}

或者喜歡重載的寫法也行:

- (BOOL)fd_prefersNavigationBarHidden { return YES;
}

刻意的模仿了下系統的命名風格,就這一句話,剩下的就都不用操心了。

關於私有API

你們會質疑說,這用到了 UIKit 的私有屬性和私有 API,要是系統升級變了咋辦?要是審覈被拒了咋辦?

首先,iOS 系統的 SDK 爲了向下兼容,通常只會增長方法或者修改方法實現,不太可能直接刪除一個共有方法,而私有方法的行爲確實可能有變化,但系統 release 頻率畢竟很低,每當新版本發佈時 check 下原來的功能是否能 work 就行了,大可沒必要擔憂這麼遠,SDK 是死的人是活的。

另外一個就是審覈問題,FDFullscreenPopGesture 的實現中有主要有兩處觸碰到了私有 API:

// 1. 私有變量標誌transition動畫是否正在進行
[self.navigationController valueForKey:@"_isTransitioning"];
// 2. 一個內部的selector
NSSelectorFromString(@"handleNavigationTransition:");

 

不管是 kvc 仍是 selector 反射,都是利用 objc runtime 完成的,而到了這一層,真的就沒啥公有私有可言了。設想你就是開發 Apple 私有 API 檢查工具的工程師,給你一個 ipa 的包,你會如何檢查出其中有沒有私有 API 呢?

首先,這個檢查必定是個靜態檢查吧,不多是運行時檢查,由於代碼邏輯那麼複雜,把程序跑起來看全部 objc_msgSend 中包不包括私有調用這件事太不現實了。對 ipa 文件作靜態檢查的話確定是去分析 Mach-O 可執行文件,由於這時不少源代碼級別的信息已經丟失,經分析能夠採起下面幾種手段:

  • 是否 link 了私有 framework 或者公開 framework 中的私有符號,這能夠防止開發者把私有 header 都 dump 出來供程序直接調用。
  • 同上,使用@selector(_private_sel)加上-performSelector:的方式直接調用私有 API。
  • 掃描全部符號,查看是否有繼承自私有類,重載私有方法,方法名是否有重合。
  • 掃描全部 string ,看字符串常量段是否出現和私有 API 對應的。

我以爲前三條被 catch 住的可能性最高,也最容易被檢查出來。再來看咱們用到用字符串的方法 kvc 和 反射 selector,應該屬於最後一條,這時候就很難抉擇了,拿handleNavigationTransition:來講,看上去人畜無害啊,我本身類裏面的方法也徹底可能命名出這個來,因此單單憑藉字符串命中私有 API 斷定,蘋果很容易誤傷一大票開發者。

綜上,我以爲使用字符串的方式使用私有 API 是相對安全的,咱們的 App 立刻要提交審覈,若是過了幾天你還能讀到這段文字,說明個人猜測是木有錯的,你們能夠放心使用。

0 代碼的 Demo

還有一個有意思的事,咱們在 github 上的 demo工程 木有寫一行代碼,就實現了下面的效果:

iOS一個絲滑的全屏滑動返回手勢

工程長這個樣子,view controller 類也沒寫,爲了體現FDFullscreenPopGesture的 AOP 性質:

iOS一個絲滑的全屏滑動返回手勢

頁面由 Storyboard 構建:

iOS一個絲滑的全屏滑動返回手勢

而控制頁面隱藏 bar 的屬性也能用 Runtime Attributes 模擬調用:

iOS一個絲滑的全屏滑動返回手勢

這樣就完成了一個很是乾淨的 Demo

加入到你的工程中

首先要求最低支持 iOS7,我想在 WWDC 2015 結束,iOS9 發佈後,主流的 App 就都會 iOS7 起跳了。依然是熟悉的 cocoapods 安裝:

pod 'FDFullscreenPopGesture', '~> 1.1'

要是沒有搜到就pod setup下。

廣告時間

我這邊正在招聘 iOS,座標北京,但願找到一個代碼規範的、愛用 IB 的、懶得寫重複代碼、不愛加班的同窗,相信這裏有很大空間供你學習和提高,還能夠參與到 forkingdog 開源小組中作點屌屌的東西,歡迎私聊或把簡歷丟到 sunyuan01@baidu.com

相關文章
相關標籤/搜索