我想得到的效果:
當用戶點擊backBarButtonItem
的時候,在pop前,我想處理一些邏輯來判斷是否pop。
并且我想要保留backBarButtonItem
的'<'。
為什么得不到這種效果
- 為
backBarButtonItem
綁定事件會被忽略,UINavigatonController
自動為其綁定事件,只做POP動作。There is nothing we can do. - 使用
leftBarButtonItem
可以綁定事件,但是'<'就不存在了,當然可以定制View來達到效果,但是如果需要兼容iOS6則需要更多的工作(iOS6的backBarButtonItem
試樣與iOS7不同),而且誰也不會知道在iOS9中,會出現什么新設計。(在iOS9快釋出的時候還適配iOS6?其實只是強行找個寫這篇文字的理由 :])
我試過的方法:
- Add target on
backBarButtonItem
. Failed. - Set
leftBarButtonItem
with charactor '<'. (All kind of '<' I could find in Characters Viewer) 用一個字符'<'來顯示backBarButtonItem
的'<'效果,比如'?'和'√'。這樣就不用自己繪制或貼圖了。 - Set
leftItemsSupplementBackButton
toYES
. 該屬性使backBarButtonItem
和leftBarButtonItem
同時顯示,leftBarButtonItem
在backBarButtonItem
的右邊,于是我就想讓backBarButtonItem
只顯示一個'<',讓leftBarButtonItem
顯示文字,并disablebackBarButtonItem
不就可以了?但是劇本不是我寫的。set "" tobackBarButtonItem
and set "Back" toleftBarButtonItem
, but there is a gap between '<' and 'Back'.
最終解決方案:
- Subclass
UINavigatonController
, override- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
- Category
UINavigatonController
, expose super's- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
解釋如下:
UINavigationBarDelegate
定義了一些方法來控制POP和PUSH行為:
navigationBar:shouldPushItem:
navigationBar:didPushItem:
navigationBar:shouldPopItem:
navigationBar:didPopItem:
這里我們利用了navigationBar:shouldPopItem:
,如果該方法返回NO,則不POP。
因此我們創建UINavigatonController
的子類,來定制navigationBar:shouldPopItem:
的邏輯。
這里有個小地方要注意,就是我們不需要設置delegate,UINavigatonController
會自動將包含的UINavigationBar
的delegate指向自己。
子類的navigationBar:shouldPopItem:
我們希望在處理完定制的邏輯后調用父類的該方法完成POP,但是父類UINavigatonController
并沒有把navigationBar:shouldPopItem:
作為接口暴露出來,因此我們需要一點Category
的小技巧來為父類創建navigationBar:shouldPopItem:
的接口。
代碼如下:
WFNavigationController.h文件
@protocol WFNavigationControllerDelegate <NSObject>
@optional
- (BOOL)controllerWillPopHandler;
@end
@interface WFNavigationController : UINavigationController
@end
WFNavigationController.m文件
@interface UINavigationController(UINavigationControllerNeedshouldPopItem)
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item;
@end
@implementation UINavigationController(UINavigationControllerNeedshouldPopItem)
@end
// 以上幾行就是使用Category使UINavigationController將其實現的navigationBar:shouldPopItem:暴露出來,
// 讓我們定制的子類可以調用
@interface WFNavigationController() <UINavigationBarDelegate>
@end
@implementation WFNavigationController
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
UIViewController *vc = self.topViewController;
if ([vc respondsToSelector:@selector(controllerWillPopHandler)])
{
if ([vc performSelector:@selector(controllerWillPopHandler)])
{
return [super navigationBar:navigationBar shouldPopItem:item];
}
else
{
return NO;
}
}
else
{
return [super navigationBar:navigationBar shouldPopItem:item];
}
}
@end
navigation controller棧頂的vc,遵循WFNavigationControllerDelegate
協議,實現- (BOOL)controllerWillPopHandler
方法即可。
推薦使用block而不是delegate
我在這里使用了delegate而不是block,其實是在偷懶,block是更好的方式,可以讓你的代碼更易閱讀。
因為相關邏輯都放在一起,而不是像使用委托這樣到處散落。
在該場景下,還有個推薦使用block的更重要的原因:在NavigationController
的push和pop過程中,topViewController
可能不是你預期的那個VC。block可以方便的加載,卸載。
由于topViewController
的不穩定性,所以這篇文字介紹的方法不是最好的。以后有時間再尋找下別的方式。
考慮以下場景:
A -> B -> C
A創建并 push B,B創建并 push C。
B需要在pop前進行邏輯判斷,所以B遵循協議。
這種場景下,有兩個地方會觸發B實現的委托:
- B點擊
backBarButtonItem
返回A,這是我們期望的。 - C手動
[self.navigationController popViewControllerAnimated:YES];
比如點擊rightBarButtonItem
返回B。這里也會觸發!
也就是說backBarButtonItem
時獲取到的topViewController
是pop前VC,而[self.navigationController popViewControllerAnimated:YES];
獲取到的是pop后VC。這種兩種路徑的設計也蠻“有意思”。
visibleViewController
和viewControllers.lastObject
也一樣。
這里我沒有深入下去,而是在view life circle中加載,卸載popBlock。或者使用delegate外加一個Bool變量在view life circle中啟用,禁用委托。
以上解決方案不是很理想,有時間我會再研究整理,看有沒有更好的方法。
如果沒看懂的話,請留言,我可以寫個demo。
以下可以選擇性適當忽略:
禁止pop后,< Back中的<會置灰,文字Back卻不會(可能又是Apple Inc.的小Bug)。
解決方法很簡單:在vc中調用以下兩句代碼,兩句,嗯。
[self.navigationController setNavigationBarHidden:YES animated:YES];
[self.navigationController setNavigationBarHidden:NO animated:YES];
比如,用戶點擊backBarButtonItem
時,我提示用戶是否繼續離開,如果用戶選擇OK則POP離開,如果用戶選擇NO則留在本頁并執行上面兩句,使<Back中的<恢復正常顏色。
感謝大家閱讀完這篇文字:]