右滑手勢返回##
原文
ios7開始 蘋果增加了頁面 右滑返回的效果;具體的是以UINavigationController為容器的ViewController間右滑切換頁面。
代碼里的設置是:
self.navigationController.interactivePopGestureRecognizer.enabled = YES;(default is YES)
可以看到蘋果給navigationController添加了一個手勢(具體為UIScreenEdgePanGestureRecognizer(邊緣手勢,同樣是ios7以后才有的)),就是利用這個手勢實現的 ios7的側滑返回。
問題1:然而事情并非我們想的那么簡單。
1.當我們用系統的UINavigationController,并且也是利用系統的navigateBar的時候,是完全沒有問題的
2.但是當我們沒有用系統的navigateBar或者自定義了返回按鈕的時候,這個時候 右滑返回是失效的。
解決(問題1)辦法:對于這種失效的情況,考慮到interactivePopGestureRecognizer也有delegate屬性,替換默認的self.navigationController.interactivePopGestureRecognizer.delegate來配置右滑返回的表現也是可行的。
我們可以在主NavigationController中設置一下:
self.navigationController.interactivePopGestureRecognizer.delegate =(id)self
問題2:
但是出現很多問題,比如說在rootViewController的時候這個手勢也可以響應,導致整個程序頁面不響應;push了多層后,快速的觸發兩次手勢,也會錯亂
解決(問題2)辦法:
@interface NavRootViewController : UINavigationController
@property(nonatomic,weak) UIViewController* currentShowVC;
@end
@implementation NavRootViewController
-(id)initWithRootViewController:(UIViewController *)rootViewController
{
NavRootViewController* nvc = [super initWithRootViewController:rootViewController];
self.interactivePopGestureRecognizer.delegate = self;
nvc.delegate = self;
return nvc;
}
-(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
}
-(void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
if (navigationController.viewControllers.count == 1)
self.currentShowVC = Nil;
else
self.currentShowVC = viewController;
}
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
if (gestureRecognizer == self.interactivePopGestureRecognizer) {
return (self.currentShowVC == self.topViewController); //the most important
}
return YES;
}
@end
問題三:
UIScrollView上手勢失靈:經研究,發現是UIScrollView上已經添加了 panGestureRecognizer(滑動)手勢
【解決方案】
蘋果以UIGestureRecognizerDelegate的形式,支持多個UIGestureRecognizer共存。其中的一個方法是:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
一句話總結就是此方法返回YES時,手勢事件會一直往下傳遞,不論當前層次是否對該事件進行響應。
@implementation UIScrollView (AllowPanGestureEventPass)
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UIScreenEdgePanGestureRecognizer class]]){
return YES;
}else {
return NO;
}
}
這種方式就是建一個UISCrollVIew的category,然后我用的時候是全局用的,不知道為什么在快速返回上一層的時候并快速點擊會照成線程卡死。 還有就是兩個手勢共存,所以在返回上一層的過程中,UIScrollerView還會滾動
事實上,對UIGestureRecognizer來說,它們對事件的接收順序和對事件的響應是可以分開設置的,即存在接收鏈和響應鏈。接收鏈如上文所述,和UIView綁定,由UIView的層次決定接收順序。
而響應鏈在apple君的定義下,邏輯出奇的簡單,只有一個方法可以設置多個gestureRecognizer的響應關系:
- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer;
//從UINavigationController里得到這個邊緣手勢
@implementation UINavigationController (iOS7Support)
@dynamic screenEdgePanGestureRecognizer;
- (UIScreenEdgePanGestureRecognizer *)screenEdgePanGestureRecognizer{
UIScreenEdgePanGestureRecognizer *screenEdgePanGestureRecognizer = nil;
if (self.view.gestureRecognizers.count > 0){
for (UIGestureRecognizer *recognizer in self.view.gestureRecognizers){
if ([recognizer isKindOfClass:[UIScreenEdgePanGestureRecognizer class]]){
screenEdgePanGestureRecognizer = (UIScreenEdgePanGestureRecognizer *)recognizer;
break;
}
}
}
return screenEdgePanGestureRecognizer;
}
@end
//當screenEdgePanGestureRecognizer生效時,UIScrollerView的panGestureRecognizer失效,這樣就解決了沖突
UIScreenEdgePanGestureRecognizer *screenEdgePanGestureRecognizer = self.navigationController.screenEdgePanGestureRecognizer;
[_scrollerView.panGestureRecognizer requireGestureRecognizerToFail:screenEdgePanGestureRecognizer];
全屏的右滑返回##
FDFullscreenPopGesture這個開源項目里用運行時很簡單的實現了全屏的右滑返回。
有人說直接用下面這種kvo的方式就能實現了,這是系統原有的私有屬性,增大那個響應區域就可以了。但我自己還沒實測過,有興趣的可以試試。
[self.interactivePopGestureRecognizer setValue:@([UIScreen mainScreen].bounds.size.width) forKeyPath:@"_recognizer._settings._edgeSettings._edgeRegionSize"];
touchesbegan未響應##
touchesbegan跟UITapGestureRecognizer同時存在時,tap會有一個屬性是cancelsTouchesInView,默認為YES,設置為NO就可以使touchesbegan響應了。
參考:一個Bug引發的對UIGestureRecognizer的思考