如何将可点击的图像添加到 UIPickerView 行?

Posted

技术标签:

【中文标题】如何将可点击的图像添加到 UIPickerView 行?【英文标题】:How to add clickable images to UIPickerView rows? 【发布时间】: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 - 仅当它位于中心行时才使其可点击。 (外面可以点击也可以,但不是必须的) 【参考方案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 做了类似的事情。一些想法:

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

【讨论】:

谢谢 - 我现在按照建议在层次结构上设置了 isUserEnabled,但它似乎没有什么不同。我也尝试过 zPosition 技巧。你的工作听起来不错-谢谢。如果我知道如何做,我会回到它,但解决方法应该足以满足当前的需求。谢谢! 很高兴它为你解决了!保持我的发布。

以上是关于如何将可点击的图像添加到 UIPickerView 行?的主要内容,如果未能解决你的问题,请参考以下文章

如何将可编辑的文本和图像添加到threejs

如何将可绘制资源中的图像保存到 SD 卡?

将图像添加到我的 UIPickerView 哪里出错了?

如何在具有多个组件的 UIPickerView 中为特定组件添加图像?

Android-是否可以将可点击链接添加到字符串资源中

如何将可关闭的文本标签添加到 Textarea Kendo | jQuery