如何给UIView添加触摸事件?

295

我该如何在UIView上添加触摸事件?
我的尝试:

UIView *headerView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, nextY)] autorelease];
[headerView addTarget:self action:@selector(myEvent:) forControlEvents:UIControlEventTouchDown];
// ERROR MESSAGE: UIView may not respond to '-addTarget:action:forControlEvents:'

我不想创建一个子类并覆盖

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

对于Swift,您可以从这里获取:https://dev59.com/el4b5IYBdhLWcg3wzUYp - Mr.Javed Multani
16个回答

589

在iOS 3.2及更高版本中,您可以使用手势识别器。例如,这是处理轻击事件的方法:

//The setup code (in viewDidLoad in your view controller)
UITapGestureRecognizer *singleFingerTap = 
  [[UITapGestureRecognizer alloc] initWithTarget:self 
                                          action:@selector(handleSingleTap:)];
[self.view addGestureRecognizer:singleFingerTap];

//The event handling method
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer
{
  CGPoint location = [recognizer locationInView:[recognizer.view superview]];

  //Do stuff here...
}

还有一堆内置手势。查看 iOS 事件处理和 UIGestureRecognizer 的文档。我在 github 上也有许多示例代码可能会有所帮助。


2
CGPoint这条线是做什么用的? - zakdances
2
@yourfriendzak CGPoint 表示点击视图在其父视图中的位置。您可以使用此点将被点击的视图(或兄弟视图)移动到被点击的位置。这在处理 UIPanGestureRecognizer 的拖动视图时更有用,可以将视图拖动到屏幕上的任意位置。 - Nathan Eror
5
非常简洁明了的回答,谢谢。但天哪,如果这能再简单一点那该多好啊! :) - natbro
你需要添加 view.userInteractionEnabled = YES; - dollar2048
单击和触摸事件之间存在差异,涉及多个指针和按下/移动/释放状态。几乎每个答案都谈到了单击... - sebesbal
显示剩余4条评论

150

手势识别器

当您向视图添加手势识别器时,您可以获得一些常用的触摸事件(或手势)的通知。默认支持以下手势类型:

  • UITapGestureRecognizer点按(简短地触摸屏幕一次或多次)
  • UILongPressGestureRecognizer长按(长时间触摸屏幕)
  • UIPanGestureRecognizer平移(在屏幕上移动手指)
  • UISwipeGestureRecognizer滑动(快速移动手指)
  • UIPinchGestureRecognizer捏合(将两个手指挤在一起或分开 - 通常用于缩放)
  • UIRotationGestureRecognizer旋转(以圆形方向移动两个手指)

除了这些手势识别器之外,您还可以自定义自己的手势识别器。

在Interface Builder中添加手势

从对象库中拖动手势识别器到您的视图上。

enter image description here

从文档大纲中控制拖动手势到您的视图控制器代码中,以创建 Outlet 和 Action。

enter image description here

这应该是默认设置的,但也要确保您的视图中启用用户操作设置为true。

enter image description here

编程添加手势

要通过编程添加手势,您需要 (1) 创建一个手势识别器,(2) 将其添加到视图中,以及 (3) 创建一个在手势被识别时调用的方法。

import UIKit
class ViewController: UIViewController {

    @IBOutlet weak var myView: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 1. create a gesture recognizer (tap gesture)
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(sender:)))
        
        // 2. add the gesture recognizer to a view
        myView.addGestureRecognizer(tapGesture)
    }
    
    // 3. this method is called when a tap is recognized
    @objc func handleTap(sender: UITapGestureRecognizer) {
        print("tap")
    }
}

注释

  • sender 参数是可选的。如果您不需要对手势的引用,则可以省略它。但如果这样做,请在操作方法名称后删除 (sender:)
  • handleTap 方法的命名是任意的。使用 action: #selector(一些方法名称(sender:)) 命名为您想要的任何名称。

更多示例

您可以研究我添加到这些视图中的手势识别器,以了解它们的工作原理。

enter image description here

这是该项目的代码:

import UIKit
class ViewController: UIViewController {
    
    @IBOutlet weak var tapView: UIView!
    @IBOutlet weak var doubleTapView: UIView!
    @IBOutlet weak var longPressView: UIView!
    @IBOutlet weak var panView: UIView!
    @IBOutlet weak var swipeView: UIView!
    @IBOutlet weak var pinchView: UIView!
    @IBOutlet weak var rotateView: UIView!
    @IBOutlet weak var label: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Tap
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap))
        tapView.addGestureRecognizer(tapGesture)
        
        // Double Tap
        let doubleTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap))
        doubleTapGesture.numberOfTapsRequired = 2
        doubleTapView.addGestureRecognizer(doubleTapGesture)
        
        // Long Press
        let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(gesture:)))
        longPressView.addGestureRecognizer(longPressGesture)
        
        // Pan
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(gesture:)))
        panView.addGestureRecognizer(panGesture)
        
        // Swipe (right and left)
        let swipeRightGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(gesture:)))
        let swipeLeftGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(gesture:)))
        swipeRightGesture.direction = UISwipeGestureRecognizerDirection.right
        swipeLeftGesture.direction = UISwipeGestureRecognizerDirection.left
        swipeView.addGestureRecognizer(swipeRightGesture)
        swipeView.addGestureRecognizer(swipeLeftGesture)
        
        // Pinch
        let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(gesture:)))
        pinchView.addGestureRecognizer(pinchGesture)
        
        // Rotate
        let rotateGesture = UIRotationGestureRecognizer(target: self, action: #selector(handleRotate(gesture:)))
        rotateView.addGestureRecognizer(rotateGesture)
        
    }
    
    // Tap action
    @objc func handleTap() {
        label.text = "Tap recognized"
        
        // example task: change background color
        if tapView.backgroundColor == UIColor.blue {
            tapView.backgroundColor = UIColor.red
        } else {
            tapView.backgroundColor = UIColor.blue
        }
        
    }
    
    // Double tap action
    @objc func handleDoubleTap() {
        label.text = "Double tap recognized"
        
        // example task: change background color
        if doubleTapView.backgroundColor == UIColor.yellow {
            doubleTapView.backgroundColor = UIColor.green
        } else {
            doubleTapView.backgroundColor = UIColor.yellow
        }
    }
    
    // Long press action
    @objc func handleLongPress(gesture: UILongPressGestureRecognizer) {
        label.text = "Long press recognized"
        
        // example task: show an alert
        if gesture.state == UIGestureRecognizerState.began {
            let alert = UIAlertController(title: "Long Press", message: "Can I help you?", preferredStyle: UIAlertControllerStyle.alert)
            alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
            self.present(alert, animated: true, completion: nil)
        }
    }
    
    // Pan action
    @objc func handlePan(gesture: UIPanGestureRecognizer) {
        label.text = "Pan recognized"
        
        // example task: drag view
        let location = gesture.location(in: view) // root view
        panView.center = location
    }
    
    // Swipe action
    @objc func handleSwipe(gesture: UISwipeGestureRecognizer) {
        label.text = "Swipe recognized"
        
        // example task: animate view off screen
        let originalLocation = swipeView.center
        if gesture.direction == UISwipeGestureRecognizerDirection.right {
            UIView.animate(withDuration: 0.5, animations: {
                self.swipeView.center.x += self.view.bounds.width
            }, completion: { (value: Bool) in
                self.swipeView.center = originalLocation
            })
        } else if gesture.direction == UISwipeGestureRecognizerDirection.left {
            UIView.animate(withDuration: 0.5, animations: {
                self.swipeView.center.x -= self.view.bounds.width
            }, completion: { (value: Bool) in
                self.swipeView.center = originalLocation
            })
        }
    }
    
    // Pinch action
    @objc func handlePinch(gesture: UIPinchGestureRecognizer) {
        label.text = "Pinch recognized"
        
        if gesture.state == UIGestureRecognizerState.changed {
            let transform = CGAffineTransform(scaleX: gesture.scale, y: gesture.scale)
            pinchView.transform = transform
        }
    }
    
    // Rotate action
    @objc func handleRotate(gesture: UIRotationGestureRecognizer) {
        label.text = "Rotate recognized"
        
        if gesture.state == UIGestureRecognizerState.changed {
            let transform = CGAffineTransform(rotationAngle: gesture.rotation)
            rotateView.transform = transform
        }
    }
}

注意事项

  • 您可以将多个手势识别器添加到单个视图中。但为了简单起见,我并没有这样做(除了滑动手势)。如果您的项目需要,请阅读手势识别器文档。它相当易懂且有帮助。
  • 上述示例存在已知问题:(1)平移视图在下一次手势事件时重置其框架。(2)第一次滑动时,滑动视图从错误的方向出现。(不过这些示例中的错误不应影响您对手势识别器工作原理的理解。)

3
你的答案非常好,详细地描述了解决方法,帮助了我很多。这也是为什么我会点赞你的答案。但是,你漏掉了一个我认为必须包含在答案中的指令,因为你已经详细解释了它。那个指令是关于设置“<yourView>.EnableUserInteraction = TRUE”的。我希望你同意我的看法。 - Er. Vihar
4
为了在 UIView 上使这个工作生效,你还需要添加:self.isUserInteractionEnabled = true; - neiker
我有一个视图,在其中我实现了DropMenu,每当点击视图时都会出现。但是,当我转到下一个屏幕时,我的View.addGestureRecognizer(tapGesture)会出现错误“Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value”。 - Muhammad Ahmed

56
我认为你可以简单地使用

UIControl *headerView = ...
[headerView addTarget:self action:@selector(myEvent:) forControlEvents:UIControlEventTouchDown];

我的意思是headerView继承自UIControl。


7
这是更好的答案。UITapGestureRecognizer不能完全替代UIControlEventTouchDown。一个“Tap(轻拍)”通常由UIControlEventTouchDownUIControlEventTouchUpInside组成。 - ohho
62
UIView不是UIControl,因此无法使用addTarget:action:forControlEvents:方法。 - RobertJoseph
9
请注意,UIControl 继承自 UIView。对于我的目的,我只需要简单地切换子类类型即可。 - pretzels1337
1
你可以将其类设置为UIControl @RobertJoseph.. 进入xib文件并将视图自定义类设置为UIControl。现在你可以在其中处理事件了。 - Zar E Ahmer
2
那么,既然我只是为了处理轻拍而更改继承关系,这又为什么是更好的解决方案呢? - Anthony Glyadchenko
@AnthonyGlyadchenko 这是一个更好的答案,因为原问题询问:如何为UIView添加_touch_事件?它不是在问_tap_事件。具体而言,OP想要实现UIControlEventTouchDown。将UIView切换为UIControl是正确的答案,因为手势识别器不知道.touchDown, .touchUpInside, .touchUpOutside等内容。 - janakmshah

27

Swift 3 和 Swift 4

import UIKit

extension UIView {
  func addTapGesture(tapNumber: Int, target: Any, action: Selector) {
    let tap = UITapGestureRecognizer(target: target, action: action)
    tap.numberOfTapsRequired = tapNumber
    addGestureRecognizer(tap)
    isUserInteractionEnabled = true
  }
}

使用

yourView.addTapGesture(tapNumber: 1, target: self, action: #selector(yourMethod))

谢谢。非常好用! - Mikrasya
1
太喜欢了!加到所有的项目中 ;) - budiDino

17

基于被接受的答案,您可以定义一个宏:

#define handle_tap(view, delegate, selector) do {\
    view.userInteractionEnabled = YES;\
    [view addGestureRecognizer: [[UITapGestureRecognizer alloc] initWithTarget:delegate action:selector]];\
} while(0)

这个宏使用了ARC,所以不需要进行release操作。

宏的使用示例:

handle_tap(userpic, self, @selector(onTapUserpic:));

4
如果您是在Storyboard中创建视图,请不要忘记启用“用户交互启用”选项。 - david
我们应该在.h或.m文件中定义宏,并且参数的名称应该是什么。我的意思是#define handle_tap(UIView view,Delegate delegate , SEL selector)do.. - Zar E Ahmer

13

Swift 4.2 和 Xcode 10 中,可以使用 UITapGestureRecognizer 来添加触摸事件。

//Add tap gesture to your view
let tap = UITapGestureRecognizer(target: self, action: #selector(handleGesture))
yourView.addGestureRecognizer(tap)

// GestureRecognizer
@objc func handleGesture(gesture: UITapGestureRecognizer) -> Void {
//Write your code here
}

如果您想使用SharedClass

//This is my shared class
import UIKit

class SharedClass: NSObject {

    static let sharedInstance = SharedClass()

    //Tap gesture function
    func addTapGesture(view: UIView, target: Any, action: Selector) {
        let tap = UITapGestureRecognizer(target: target, action: action)
        view.addGestureRecognizer(tap)
    }
} 

我的ViewController中有三个视图,它们分别叫做view1、view2和view3。

override func viewDidLoad() {
    super.viewDidLoad()
    //Add gestures to your views
    SharedClass.sharedInstance.addTapGesture(view: view1, target: self, action: #selector(handleGesture))
    SharedClass.sharedInstance.addTapGesture(view: view2, target: self, action: #selector(handleGesture))
    SharedClass.sharedInstance.addTapGesture(view: view3, target: self, action: #selector(handleGesture2))

}

// GestureRecognizer
@objc func handleGesture(gesture: UITapGestureRecognizer) -> Void {
    print("printed 1&2...")
}
// GestureRecognizer
@objc func handleGesture2(gesture: UITapGestureRecognizer) -> Void {
    print("printed3...")
}

8
你可以通过在代码中添加手势识别器来实现这一点。 步骤1:ViewController.m:
// Declare the Gesture.
UITapGestureRecognizer *gesRecognizer = [[UITapGestureRecognizer alloc] 
                                          initWithTarget:self 
                                          action:@selector(handleTap:)];
gesRecognizer.delegate = self;

// Add Gesture to your view.
[yourView addGestureRecognizer:gesRecognizer]; 

步骤2: ViewController.m:

// Declare the Gesture Recogniser handler method.
- (void)handleTap:(UITapGestureRecognizer *)gestureRecognizer{
   NSLog(@"Tapped");
}

注意:在我的情况下,这里的yourView是@property (strong, nonatomic) IBOutlet UIView *localView;

编辑:*localView是下面Main.storyboard中的白色框

输入图像描述

输入图像描述


我有多个视图,所以我可以更改发送者ID吗? - Moin Shirazi
你需要进入内部子视图,然后才能传递发送者。---------for (UIView *view in self.topicScrollView.subviews) {//进入父视图内部 if ([view isKindOfClass:[UIButton class]]) { // 进入特定的视图 // 到达此点。 } } - Programming Learner

6

Swift 3:

let tapGestureRecognizer: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTapGestureRecognizer(_:)))
view.addGestureRecognizer(tapGestureRecognizer)

func handleTapGestureRecognizer(_ gestureRecognizer: UITapGestureRecognizer) {

}

6
这是一个Swift版本:
// MARK: Gesture Extensions
extension UIView {

    func addTapGesture(#tapNumber: Int, target: AnyObject, action: Selector) {
        let tap = UITapGestureRecognizer (target: target, action: action)
        tap.numberOfTapsRequired = tapNumber
        addGestureRecognizer(tap)
        userInteractionEnabled = true
    }

    func addTapGesture(#tapNumber: Int, action: ((UITapGestureRecognizer)->())?) {
        let tap = BlockTap (tapCount: tapNumber, fingerCount: 1, action: action)
        addGestureRecognizer(tap)
        userInteractionEnabled = true
    }
}

什么是 BlockTap - He Yifei 何一非
1
哦,糟了,忘记那部分了。这是一个自定义函数。在这里:https://github.com/goktugyil/EZSwiftExtensions/blob/master/Sources/BlockTap.swift - Esqarrouth

3

Swift 5.3

基于闭包的解决方案,参考:使用闭包的UIGestureRecognizer

final class BindableGestureRecognizer: UITapGestureRecognizer {
    private var action: () -> Void

    init(action: @escaping () -> Void) {
        self.action = action
        super.init(target: nil, action: nil)
        self.addTarget(self, action: #selector(execute))
    }

    @objc private func execute() {
        action()
    }
}

public extension UIView {
    /// A discrete gesture recognizer that interprets single or multiple taps.
    /// - Parameters:
    ///   - tapNumber: The number of taps necessary for gesture recognition.
    ///   - closure: A selector that identifies the method implemented by the target to handle the gesture recognized by the receiver. The action selector must conform to the signature described in the class overview. NULL is not a valid value.
    func addTapGesture(tapNumber: Int = 1, _ closure: (() -> Void)?) {
        guard let closure = closure else { return }

        let tap = BindableGestureRecognizer(action: closure)
        tap.numberOfTapsRequired = tapNumber
        addGestureRecognizer(tap)

        isUserInteractionEnabled = true
    }
}

使用:

view.addTapGesture { [weak self] in
    self?.view.backgroundColor = .red
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接