UIGestureRecognizer

UIGestureRecognizer

UIGestureRecognizer是一个基类,其实体类有:

在模拟器上的操作

  • shift + option+点击会改变手指的位置
  • option+拖动 缩放
  • option+鼠标旋转 旋转

冲突的处理

1.使用require(toFail: )
如下的例子,一个view既有单击事件,又有双击事件,如下:

    let t2 = UITapGestureRecognizer(target:self, action:#selector(doubleTap))
    t2.numberOfTapsRequired = 2
    self.v.addGestureRecognizer(t2)
    let t1 = UITapGestureRecognizer(target:self, action:#selector(singleTap))
    t1.require(toFail:t2) // *
    self.v.addGestureRecognizer(t1)

2.UITapGestureRecognizerUIControl的冲突
如下的例子,在UIControl的实例的superView上添加一个UITapGestureRecognizer

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
    self.control = [[CustomControl alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    self.control.backgroundColor = [UIColor redColor];
    [self.control addTarget:self action:@selector(controlAction:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:self.control];
    
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)];
    [self.view addGestureRecognizer:tap];
    
}

- (void)controlAction:(CustomControl *)control {
    NSLog(@"self.control action");
}

- (void)tap:(UITapGestureRecognizer *)tapGesture {
    
    NSLog(@"self.view tapped");
    
}

当点击UIControl时,输出为:

self.view tapped

可见其响应的是UITapGestureRecognizer事件,如果要解决冲突该怎么做呢?使用UITapGestureRecognizer的代理

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    
    if ([touch.view isKindOfClass:[CustomControl class]]) {
        return NO;// ignore the touch
    }
    return YES;
    
}

可参考:

那如果是UIButton呢?会发现,点击Button时,响应的是Button的UIControlEventTouchUpInside事件,而不是tap事件,这是为什么呢?

The UIView instance method gestureRecognizerShouldBegin(_:) solves the problem. It is called automatically; to modify its behavior, use a custom UIView subclass and override it. Its parameter is a gesture recognizer belonging to this view or to a view further up the view hierarchy. That gesture recognizer has recognized its gesture as taking place in this view; but by returning false, the view can tell the gesture recognizer to bow out(退出) and do nothing, not sending any action messages, and permitting this view to respond to the touch as if the gesture recognizer weren’t there.
Thus, for example, a UIButton could return false for a single tap UITapGestureRecognizer; a single tap on the button would then trigger the button’s action message and not the gesture recognizer’s action message. And in fact a UIButton, by default,does return false for a single tap UITapGestureRecognizer whose view is not the UIButton itself.
意思是:UIButton重写了 gestureRecognizerShouldBegin(_:)方法,当UITapGestureRecognizer的view不是button的时候,返回false
Other built-in controls may also implement gestureRecognizerShouldBegin(_:) in such a way as to prevent accidental interaction with a gesture recognizer; the documentation says that a UISlider implements it in such a way that a UISwipeGestureRecognizer won’t prevent the user from sliding the “thumb,” and there may be other cases that aren’t documented explicitly. Naturally, you can take advantage of this feature in your own UIView subclasses as well.

3.与上面的问题类似,在父view上添加一个手势,但要求在点击子view时不响应手势

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if ([touch.view isDescendantOfView:contentView]) {
        return NO;
    }
    
    return YES;
    
}

4.同时响应多个手势

如果某个view有pan,pinch,rotate手势,在识别出一个手势后,只能同时响应一个手势

可实现UIGestureRecognizerDelegate代理的如下方法,返回true

extension ViewController: UIGestureRecognizerDelegate {
    
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        
        return true
        
    }
    
}

如下的例子:

在这里插入图片描述

UIPanGestureRecognizer

通过 translationInView: 获取在指定view坐标系统中的平移

The x and y values report the total translation over time. They are not delta values from the last time that the translation was reported. Apply the translation value to the state of the view when the gesture is first recognized—do not concatenate the value each time the handler is called.
注意:不是增量

所以一般在获取平移后,通常会将translation重置为0

recoginzer.setTranslation(.zero, in: view)

通过velocity(in view: UIView?)方法可以获取到速度向量,可实现惯性动画,如下的例子:

    @IBAction func handlePan(_ recoginzer: UIPanGestureRecognizer) {
        
        guard let recoginzerView = recoginzer.view else {
            return
        }
        
        let translation = recoginzer.translation(in: view)
        recoginzerView.center.x += translation.x
        recoginzerView.center.y += translation.y
        
        recoginzer.setTranslation(.zero, in: view)
        
        guard recoginzer.state == .ended else {
            return
        }
        
        let velocity = recoginzer.velocity(in: view)
        let vectorToFinalPoint = CGPoint(x: velocity.x / 15, y: velocity.y / 15)
        let bounds = UIEdgeInsetsInsetRect(view.bounds, view.safeAreaInsets)
        var finalPoint = recoginzerView.center
        finalPoint.x += vectorToFinalPoint.x
        finalPoint.y += vectorToFinalPoint.y
        finalPoint.x = min(max(finalPoint.x, bounds.minX), bounds.maxX)
        finalPoint.y = min(max(finalPoint.y, bounds.minY), bounds.maxY)
        
        let vectorToFinalPointLength = (
            vectorToFinalPoint.x * vectorToFinalPoint.x + vectorToFinalPoint.y + vectorToFinalPoint.y
        ).squareRoot()
        
        UIView.animate(
            withDuration: TimeInterval(vectorToFinalPointLength / 1800),
            delay: 0,
            options: .curveEaseInOut,
            animations: {
                
                recoginzerView.center = finalPoint
                
            }) { _ in
                
            }
        
    }

UIPinchGestureRecognizer

pinch手势处理也一样,例如我们需要对某个view进行缩放,通常的处理如下:

  @IBAction func handlePinch(_ recognizer: UIPinchGestureRecognizer) {
    guard let recognizerView = recognizer.view else {
      return
    }
    
    recognizerView.transform = recognizerView.transform.scaledBy(x: recognizer.scale, y: recognizer.scale)
    recognizer.scale = 1
  }

UIRotationGestureRecognizer

rotation手势也类似

  @IBAction func handleRotate(_ recognizer: UIRotationGestureRecognizer) {
    guard let recognizerView = recognizer.view else {
      return
    }
    
    recognizerView.transform = recognizerView.transform.rotated(by: recognizer.rotation)
    recognizer.rotation = 0
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值