在项目中遇到一个问题,在使用AVPlayer播放视频时,如果使用系统手势返回,视频播放会卡顿。为了实现自定义手势的回归,我参考了《精通iOS框架》手势章节介绍的内容。让我介绍一些关于手势的总结。下面第一部分主要是引自这本书。手势识别对象如何工作?要理解的第一个概念是手势识别操作在正常视图响应链之外。首先UIWindow会发送触摸事件给手势识别对象,它们必须表明自己无法处理该事件。只有这样,事件才会默认转发到视图的响应链。当应用程序试图确定手势是否被识别时,事件发生的顺序很重要。1.窗口将触摸事件发送给手势识别对象。2、手势识别对象会进入UIGestureRecognizerStatePossible状态。3、对于离散手势,手势识别对象会判断是UIGestureRecognizerStateRecognized还是UIGestureRecognizerStateFailed类型。3.1如果是UIGestureRecognizerStateRecognized,手势识别接收触摸时间,调用指定的委托方法。3.2如果是UIGestureRecognizerStateFailed,手势识别对象会将触摸事件返回到响应链。4.对于连续的手势,手势识别对象会判断是UIGestureRecognizerStateBegan还是UIGestureRecognizerStateFailed。4.1如果是UIGestureRecognizerStateBegan,手势识别对象会收到触摸事件,调用指定的委托方法。之后,每当手势发生变化时,状态就会更新为UIGestureRecognizerStateChanged,并且会一直调用委托方法,直到最后一次触摸事件结束,此时状态为UIGestureRecognizerStateEnded。如果触摸模式不再匹配预期的手势,您可以将状态更改为UIGestureRecognizerStateCancelled。4.2如果为UIGestureRecognizerStateFailed,则手势识别对象返回触摸事件到响应链。注意UIGestureRecognizerStatePossible和UIGestureRecognizerStateFailed两种状态之间花费的时间很长。如果UI中的手势识别对象由于触摸动作运行缓慢,无法理解,问题可能就出在这里。最好的办法是在具体的处理方法中添加一条记录--记录每次方法调用时的状态。然后就可以根据记录的时间戳信息清楚地了解状态变化过程,也可以判断延迟问题出在哪里(这部分不是很好理解。。汗,不过不影响写全屏手势)。在一个视图中识别多个手势了解了上述步骤之后,我们来处理两个或多个手势识别对象。这就需要实现UIGestureRecognizerDelegate协议,以及如何将触摸动作发送给手势识别对象,进行更复杂的控制。主要看下面三个协议方法:-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer*)gestureRec组织者;使用此方法指示手势识别对象是否应根据应用程序的状态从UIGestureRecognizerStatePossible状态转换到UIGestureRecognizerStateBegan状态。如果返回YES,处理手势识别对象;否则状态变为UIGestureRecognizerStateFailed。-(BOOL)gestureRecognizer:(UIGestureRecognizer)gestureRecognizershouldReceiveTouch:(UITouch)touch;使用此方法指示手势识别器对象是否应接受触摸操作。此方法允许手势识别器对象根据用户设置的某些约束拒绝识别手势动作。-(BOOL)gestureRecognizer:(UIGestureRecognizer)gestureRecognizershouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer)otherGestureRecognizer;当多个手势识别对象同步接收触摸事件时使用该方法。返回YES同步处理所有请求,或者测试传入的手势识别对象来判断这些动作是否满足同步处理的要求。实际应用项目SJVideoPlayerBackGR(全屏手势)在处理手势识别的过程中,首先实现的是:-(BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer*)gestureRecognizer{if(self.childViewControllers.count<=1)returnNO;CGPointtranslate=[gestureRecognizertranslationInView:self.view];BOOLpossible=translate.x>0&&translate.y==0;if(possible)returnYES;elsereturnNO;}这个方法就是上面说的第一个协议方法,表示手势根据应用程序的状态确定对象是否应从Possible过渡到Beginning。这里我判断子控制器的个数和用户操作的方向。如果是横向向右操作,返回YES,开启全屏返回手势。同时考虑到有多个手势识别对象,我还实现了如下方法:}if([otherGestureRecognizerisMemberOfClass:NSClassFromString(@"UIScrollViewPanGestureRecognizer")]||[otherGestureRecognizerisMemberOfClass:NSClassFromString(@"UIScrollViewPagingSwipeGestureRecognizer")]){if([otherGestureRecognizer.viewisKindOfClass:[UIScrollViewclass]]){return_View:SelfconsJVideoPotherGestureRecognizer.viewotherGestureRecognizer:otherGestureRecognizer];}returnNO;}returnYES;}这个方法就是上面提到的第三个协议方法,表示当存在多个手势识别对象时是否同步处理这些手势对象,在实现中,第一种情况是UIPanGestureRecognizer。如果是另外一个pan手势,为了避免冲突,这里直接返回NO。表示不触发返回手势。第二个和第三个手势是和scrollView相关的。当存在scrollView时,需要考虑多个scrollView的嵌套问题。-(BOOL)SJVideoPlayer_considerScrollView:(UIScrollView*)subScrollViewotherGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer{if(0!=subScrollView.contentOffset.x)returnNO;//当scrollview.contentOffset.x==0时,判断手势移动方向,leftorrightCGPointtranslate=[self.sj_pantranslationInView:self.view];if(translate.x<=0)returnNO;//如果左移,不触发全屏手势else{[otherGestureRecognizersetValue:@(UIGestureRecognizerStateCancelled)forKey:@"state"];returnYES;}}首先,在多个嵌套scrollView的情况下,需要约定何时触发全屏手势。我这里的约定是当sub-scrollView的contentOffset.x==0时触发全局手势。上面代码中我判断手势移动方向(左或右),考虑contentOffset.x是否等于0。效果如下:全屏手势遇到的问题问题一:触发全屏手势后,在移动过程中会出现如下问题:为了避免这种情况,我为viewController/添加了三个分类方法//全屏手势触发@property(nonatomic,copy,readwrite)void(^sj_viewWillBeginDragging)(__kindofUIViewController*vc);///全屏手势触发@property(nonatomic,copy,readwrite)void(^sj_viewDidDrag)(__kindofUIViewController*vc);///全屏手势结束@property(nonatomic,copy,readwrite)void(^sj_viewDidEndDragging)(__kindofUIViewController*vc);同时,在使用scrollView的地方,做了如下处理:collectionView.scrollEnabled=YES;};这样,当全屏手势触发时,scrollView停止滚动,当全屏手势结束时,scrollView恢复滚动,效果如下:但是,全屏手势触发后,找到视图中的根scrollView并禁止滚动。下面的代码是找到view中的rootscrollView,并在手势触发开始时禁止滚动,手势结束后开启滚动。-(UIScrollView*)_findingSubScrollView{__blockUIScrollView*scrollView=nil;[self.topViewController.view.subviewsenumerateObjectsUsingBlock:^(__kindofUIView*_Nonnullobj,NSUIntegeridx,BOOL*_Nonnullstop){if(![objisKindOfClass:[UIScrollViewclass]])返回;*停止=YES;scrollView=obj;}];returnscrollView;}-(void)SJVideoPlayer_handlePanGR:(UIPanGestureRecognizer*)pan{CGFloatoffset=[pantranslationInView:self.view].x;开关(pan.state){caseUIGestureRecognizerStateBegan:{[selfSJVideoPlayer_ViewWillBeginDragging];}break;caseUIGestureRecognizerStateChanged:{if(offset<0)return;[selfSJVideoPlayer_ViewDidDrag:offset];}break;caseUIGestureRecognizerStatePossible:caseUIGestureRecognizerStateEnded:caseUIGestureRecognizerStateCancelled:caseUIGestureRecognizerStateFailed:{[selfSJVideoPlayer_ViewDidEndDragging:offset];}break;}}问题2:在使用UIPageViewController时,由于其复用机制,无法判断其内部的contentOffset,所以遇到UIPageViewController的controller时,使用系统的回边手势,在边缘滑动时,触发系统return,在中间部分滑动时,触发UIPageViewController滚动。为此,我在UINavigationController中添加了一个用于来回切换两个手势的分类方法://用于来回切换两个手势@property(nonatomic,assign,readwrite)BOOLuseNativeGesture;-(void)viewWillAppear:(BOOL)animated{[superviewWillAppear:animated];self.navigationController.useNativeGesture=YES;}-(void)viewWillDisappear:(BOOL)animated{[superviewWillDisappear:animated];self.navigationController.useNativeGesture=NO;}问题3:当没有设置navigationBar的颜色时,10以下的系统导航栏头部莫名其妙的变黑了...所以我在基类中设置了navigationBar在ntroller中添加如下代码//NavigationController-(void)viewDidLoad{[superviewDidLoad];self.navigationBar.barTintColor=[UIColorwhiteColor];self.view.backgroundColor=[UIColorwhiteColor];//Doanyadditionalsetupafterloadingtheview.}Over..
