您将无法将手势识别器添加到 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
}
}