如何将可点击的图像添加到 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.add
和UIImage.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 行?的主要内容,如果未能解决你的问题,请参考以下文章