【问题标题】:UITapGestureRecognizer in custom UIView doesn't work自定义 UIView 中的 UITapGestureRecognizer 不起作用
【发布时间】:2021-06-19 03:23:01
【问题描述】:

Swift 5/Xcode 12.4

我为我的自定义 MarkerView 创建了一个 xib 文件 - 布局非常简单:

- View
-- StackView
--- DotLabel
--- NameLabel

ViewStackView 默认在检查器中都设置为“启用用户交互”。 DotLabelNameLabel 没有,但勾选他们的框似乎并没有真正改变任何东西。

在运行时,我在 ViewController 中创建 MarkerViews(仅用于测试目的),并将它们添加到已经包含图像的 ScrollView(这可行):

override func viewDidAppear(_ animated: Bool) {
    createMarkers()
    setUpMarkers()
}

private func createMarkers() {
    let marker = MarkerView()
    marker.setUp("Some Text")
    markers.append(marker)
    scrollView.addSubview(marker)
    marker.alpha = 0.0
}

private func setUpMarkers() {
    for (i,m) in markers.enumerated() {
        m.frame.origin = CGPoint(x: (i+1)*100,y: (i+1)*100)
        m.alpha = 1.0
    }
}

这些 MarkerViews 应该是可点击的,所以我添加了一个 UITapGestureRecognizer 但从未调用过链接的函数。这是我的完整 MarkerView:

class MarkerView: UIView {
    @IBOutlet weak var stackView: UIStackView!
    @IBOutlet weak var dotLabel: UILabel!
    @IBOutlet weak var nameLabel: UILabel!
    
    let nibName = "MarkerView"
    var contentView:UIView?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() {
        guard let view = loadViewFromNib() else { return }
        view.frame = self.bounds
        self.addSubview(view)
        contentView = view
        
        let gesture = UITapGestureRecognizer(target: self, action: #selector(self.clickedMarker))
        self.addGestureRecognizer(gesture)
    }
    func loadViewFromNib() -> UIView? {
        let bundle = Bundle(for: type(of: self))
        let nib = UINib(nibName: nibName, bundle: bundle)
        return nib.instantiate(withOwner: self, options: nil).first as? UIView
    }
    
    func setUp(_ name:String) {
        nameLabel.text = name
        nameLabel.sizeToFit()
        stackView.sizeToFit()
    }
    
    @objc private func clickedMarker() {
        print("Clicked Marker!")
    }
}

其他问题建议在添加手势识别器之前添加self.isUserInteractionEnabled = true。我什至为 StackView 和两个标签都启用了它,还尝试将手势识别器添加到 ScrollView,但都没有帮助。

为什么手势识别器不工作,我该如何解决这个问题?

编辑:根据 Sweeper 的建议,我的代码现在如下所示:

class MarkerView: UIView, UIGestureRecognizerDelegate {
    .....
    func commonInit() {
        guard let view = loadViewFromNib() else { return }
        view.frame = self.bounds
        let gesture = UITapGestureRecognizer(target: self, action: #selector(self.clickedMarker))
        gesture.delegate = self
        self.isUserInteractionEnabled = true
        self.addGestureRecognizer(gesture)
        self.addSubview(view)
        contentView = view
    }
    .....
    func gestureRecognizer(_: UIGestureRecognizer, _ otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        print("gestureRecognizer")
        return true
    }
}

“gestureRecognizer”永远不会打印到控制台。

【问题讨论】:

  • 滚动视图的手势识别器可能会干扰您的手势识别器。
  • @Sweeper 我没有向 ScrollView 添加额外的手势识别器,如果它有一个(不知道 ScrollView 是如何工作的),我没有触摸它。我“搞砸”的唯一识别器是 MarkerView 一个,但那个不起作用。
  • 滚动视图有手势识别器。否则你不能滚动它,对吗?您无需触摸它即可干扰您的手势识别器。看到这个possible duplicate
  • 您没有使用 MarkerView 初始化程序覆盖,如 let marker = MarkerView.init(frame: CGRect.zero)
  • @CSmith 我刚刚在init(frame: CGRect) 中打印了一个打印件,它与我的版本一起打印到控制台。我也测试了你的,没有发现有什么不同。是否有理由改为使用较长的版本?

标签: ios swift uiview uigesturerecognizer uitapgesturerecognizer


【解决方案1】:

@Sweeper linked 问题的公认答案下方的一个 cmets 给出了一个很好的提示:

内容视图的大小是多少(记录它)?

print(self.frame.size)
print(contentView.frame.size)

在我的应用程序中都打印了(0.0, 0.0)UITapGestureRecognizer 附加到 self,因此即使它有效,也根本没有你可以点击的区域让它识别点击。

解决办法:设置UIView的大小,UITapGestureRecognizer附加到:

stackView.layoutIfNeeded()
stackView.sizeToFit()
self.layoutIfNeeded()
self.sizeToFit()

对我不起作用,之后大小仍然是(0.0, 0.0)。相反,我直接设置self 的大小:

self.frame.size = stackView.frame.size

这也设置了contentView 的大小(因为它是MarkerView 的孩子),但当然也需要正确设置stackView 的大小。您可以自己添加孩子的宽度/高度(包括间距/边距)或直接调用:

stackView.layoutIfNeeded()
stackView.sizeToFit()

完成的代码:

func setUp(_ name:String) {
    nameLabel.text = name
    //nameLabel.sizeToFit() //Not needed anymore
    stackView.layoutIfNeeded() //Makes sure that the labels already use the proper size after updating the text
    stackView.sizeToFit()
    self.frame.size = stackView.frame.size

    let gesture = UITapGestureRecognizer(target: self, action: #selector(self.onClickStackView))
    self.addGestureRecognizer(gesture)
}

具体说明:

  1. 没有必要让delegate 和覆盖func gestureRecognizer(_: UIGestureRecognizer, _ otherGestureRecognizer: UIGestureRecognizer) -> Bool 总是返回true,这可能是一个替代方案,但我没有设法让那个版本工作——这个函数从未被调用过。如果有人知道该怎么做,请随时发布它作为替代解决方案。
  2. 一旦父视图知道它的大小,就必须附加UITapGestureRecognizer,而commonInit 中没有,因为标签的文本和所有内容的大小还没有设置在那里。可以在附加识别器后添加更多文本,但如果使用上述代码,则超出原始长度(附加识别器时)的所有内容都将无法点击。
  3. 使用父视图的背景颜色(stackView 被忽略),但可以简单地将其设置为透明颜色。
  4. 将手势识别器附加到stackView 而不是self 的工作方式相同。您甚至可以使用UILabel,但默认情况下他们的“用户交互已启用”处于关闭状态 - 首先在“属性检查器”(“视图”部分中的复选框)或代码(myLabel.isUserInteractionEnabled = true)中启用它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    • 2021-08-28
    • 1970-01-01
    • 2020-10-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多