【问题标题】:How to add clickable images to UIPickerView rows?如何将可点击的图像添加到 UIPickerView 行?
【发布时间】:2021-12-29 16:07:59
【问题描述】:

我有一个 UIPickerView 用于选择乐器,并且每个乐器名称旁边都有一个播放图标,当用户单击它时我想播放适当的声音,以便他们可以听到乐器的声音在他们在选择器中选择它之前。

创建响应点击的图像通常没有问题。

但是,在 UIPickerView 中,作为行的一部分创建的图像似乎没有收到点击(我猜 UIPickerView 会以某种方式获得优先权?)

我需要做些什么来确保我的图像获得点击事件?

非常感谢!

func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
    let parentView = UIView()
    let label = UILabel(frame: CGRect(x: 60, y: 0, width: 80, height: 50))
    let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 50, height:50))
    if #available(iOS 13.0, *) {
        imageView.image = UIImage.add
        
        let tapGR = UITapGestureRecognizer(target: self, action: #selector(self.imageTapped))
        imageView.addGestureRecognizer(tapGR)
        imageView.isUserInteractionEnabled = true
    } else {
        // Fallback on earlier versions
    }
    label.text = "red"
    parentView.addSubview(label)
    parentView.addSubview(imageView)
    
    return parentView
}

【问题讨论】:

  • 您是否希望能够在“中心”行时点击图像,并保留所有其他标准选择器视图行为?或者您是否希望能够点击任何可见(或部分可见)行中的图像?
  • 感谢@DonMag - 仅当它位于中心行时才使其可点击。 (外面可以点击也可以,但不是必须的)

标签: ios swift


【解决方案1】:

您将无法将手势识别器添加到 viewForRow 视图 - 所有触摸都会被选择器视图吃掉。

因此,您可以编写自己的自己的选择器视图,这取决于您希望复制内置选择器视图的紧密程度,可能相当简单或超级复杂。

或者...您可以将点击识别器添加到选择器视图...然后检查点击是否在中间行的图像视图内。

这是一个简单的例子...

我们将使用它作为我们的自定义行视图:

class MyPickerRowView: UIView {

    let label = UILabel(frame: CGRect(x: 60, y: 0, width: 80, height: 50))
    let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 50, height:50))

    var selected: Bool = false
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() {
        if #available(iOS 13.0, *) {
            imageView.image = UIImage.add
        } else {
            // Fallback on earlier versions
            imageView.backgroundColor = .red
        }
        addSubview(label)
        addSubview(imageView)
    }
    func tappedImageView(_ p: CGPoint) -> Bool {
        // let's toggle the image if the tap hits
        if imageView.frame.contains(p) {
            selected.toggle()
            if #available(iOS 13.0, *) {
                imageView.image = selected ? UIImage.checkmark : UIImage.add
            } else {
                imageView.backgroundColor = selected ? .green : .red
            }
            return true
        }
        return false
    }

}

唯一“不寻常”的东西是func tappedImageView(_ p: CGPoint) -> Bool...它将检查传递的点,如果该点在图像视图框架内,则返回true,否则返回false。

我们还将在UIImage.addUIImage.checkmark 之间切换图像,以便我们获得一些视觉反馈。

我们的点击手势处理程序(在控制器类中)将:

  • 获取所选(中心)行的视图
  • 确保它是我们自定义的 MyPickerRowView
  • 将点击点转换为该视图
  • 询问该点是否在图像视图框架内

所以,这里是控制器类:

class PickerTestViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate, UIGestureRecognizerDelegate {
    
    let picker = UIPickerView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        picker.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(picker)
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            picker.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            picker.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            picker.centerYAnchor.constraint(equalTo: g.centerYAnchor),
        ])
        
        // create a tap recognizer
        let tap = UITapGestureRecognizer(target: self, action: #selector(pickerTapped(_:)))
        // don't prevent all the normal behaviors of the picker view
        tap.cancelsTouchesInView = false
        // tap delegate needs to be set so we can use shouldRecognizeSimultaneouslyWith
        tap.delegate = self
        // add the tap recognizer to the PICKER view itself
        picker.addGestureRecognizer(tap)
        
        picker.delegate = self
        picker.dataSource = self

        // just so we can easily see the frame of the picker view
        picker.layer.borderColor = UIColor.red.cgColor
        picker.layer.borderWidth = 1
        
    }
    
    @objc func pickerTapped(_ tapRecognizer:UITapGestureRecognizer) {
        
        // get the index of the selected row (the "center" row)
        let n = picker.selectedRow(inComponent: 0)
        
        // make sure the view for that row is a MyPickerRowView
        if let v = picker.view(forRow: n, forComponent: 0) as? MyPickerRowView {
            // convert the tap location to that view
            let p = tapRecognizer.location(in: v)
            // ask the view if the tap is in the image view frame
            if v.tappedImageView(p) {
                print("tapped in image view in center row")
                // do something
            }
        }
    }
    
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return 100
    }
    func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
        return 50
    }
    
    func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
        let parentView = MyPickerRowView()
        parentView.label.text = "\(row)"
        return parentView
    }
    
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
    
}

【讨论】:

  • 啊,谢谢!这看起来是一个非常简洁和强大的解决方案。也感谢所有的代码。明天我会玩它,然后回来标记正确(只是觉得我应该先实际使用它)。
【解决方案2】:

好问题。我没有对这个特定 场景做很多事情,但我用 UITableView 做了类似的事情。一些想法:

  1. 仔细检查以下内容:isUserEnabled 是否适用于从 parentView 到 imageView 的整个子视图集? imageView 是靠前的吗?也许让 imageView.layer.zPosition 更接近 1 而其他更远?您可以使用调试视图层次结构堆栈图标在模拟器中仔细检查: Debug View Hierarchy Icon in XCode
  2. 解决方法:是否需要在点击时发生,或者您可以在点击 didSelectRow 委托方法时播放一个小样本?

【讨论】:

  • 谢谢 - 我现在按照建议在层次结构上设置了 isUserEnabled,但它似乎没有什么不同。我也尝试过 zPosition 技巧。你的工作听起来不错-谢谢。如果我知道如何做,我会回到它,但解决方法应该足以满足当前的需求。谢谢!
  • 很高兴它为你解决了!保持我的发布。
猜你喜欢
  • 2015-03-04
  • 2012-05-08
  • 2013-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-16
  • 2023-03-13
  • 2021-01-01
相关资源
最近更新 更多