您已将您的actionTapPickup 声明为@IBAction。
您是否真的从在 Interface Builder 中设计的 NIB 或情节提要中实例化了此视图?如果是这样,请确保您已成功连接您的@IBAction?您是从NIB 实例化此视图(而不是通过编程方式)?
-
另一方面,如果您以编程方式创建此子视图,则不会使用 @IBAction 限定符,而是将其标记为 @objc,然后手动添加 .touchUpInside 事件的目标:
button.addTarget(self, action: #selector(didTapButton(_:)), for: .touchUpInside)
如果您让我们知道您是通过编程方式还是通过 NIB 实例化此自定义弹出/标注,我们可以更详细地概述这一点。 (下面我的示例是程序化实现。)
归根结底,在自定义标注中连接按钮很像在任何地方连接按钮。将@IBAction 用于IB 设计的视图,将@objc 和addTarget 用于以编程方式创建的按钮。
顺便说一句,一旦您解决了眼前的问题,值得注意的是,在让自定义标注正常工作方面存在一些微妙的问题。 Location Awareness Programming Guide: Creating Callouts 对此进行了概述,但不幸的是,这些示例是在 Objective-C 中的。无论如何,我们想要的行为是:
- 如果您点击地图上的其他位置(即取消选择注释视图),标注应该会消失;但是
- 如果您点击注释视图的自定义标注(而不是其按钮),您不希望取消选择注释。
为了实现这一点,它在很大程度上可以归结为两个不完全显而易见的小技巧:
为包含命中测试的注释视图添加hitTest。自定义标注。这是为了确保在您点击标注时不会取消选择注释。
在整个标注后面添加一个按钮,以便它消耗任何错过“60 分钟内接听”按钮但仍在标注中的触摸。
因此:
// let’s assume we have an annotation for our pickup location
class PickupLocationAnnotation: MKPointAnnotation {
let hours: String
init(hours: String) {
self.hours = hours
super.init()
}
}
// let’s have a protocol for the callout to inform view controller that the “pickup” button was tapped
protocol CalloutViewDelegate: class {
func calloutTapped(for annotation: MKAnnotation)
}
// Our callout view class
class CalloutView: UIView {
weak var annotation: MKAnnotation?
weak var delegate: CalloutViewDelegate?
let button: UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Tap here to pickup in 60 Mins", for: .normal)
button.addTarget(self, action: #selector(didTapButton(_:)), for: .touchUpInside)
return button
}()
lazy var label: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.text = (annotation as? PickupLocationAnnotation)?.hours
return label
}()
init(annotation: MKAnnotation?, delegate: CalloutViewDelegate) {
self.annotation = annotation
self.delegate = delegate
super.init(frame: .zero)
configure()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure() {
addBackgroundButton(to: self)
addSubview(button)
addSubview(label)
NSLayoutConstraint.activate([
button.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10),
button.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10),
button.topAnchor.constraint(equalTo: topAnchor, constant: 10),
button.bottomAnchor.constraint(equalTo: label.topAnchor, constant: -10),
label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10),
label.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10),
label.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10)
])
layer.cornerRadius = 10
layer.borderColor = UIColor.blue.cgColor
layer.borderWidth = 2
backgroundColor = .white
}
@objc func didTapButton(_ sender: Any) {
if let annotation = annotation {
delegate?.calloutTapped(for: annotation)
}
}
fileprivate func addBackgroundButton(to view: UIView) {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
NSLayoutConstraint.activate([
button.topAnchor.constraint(equalTo: view.topAnchor),
button.bottomAnchor.constraint(equalTo: view.bottomAnchor),
button.leadingAnchor.constraint(equalTo: view.leadingAnchor),
button.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
}
}
// our annotation view for our pickup annotations
class PickupLocationAnnotationView: MKPinAnnotationView {
weak var calloutView: UIView?
override func prepareForDisplay() {
super.prepareForDisplay()
canShowCallout = false
}
// make sure that hits in callout are recognized as not-deselecting the annotation view
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if let hitView = super.hitTest(point, with: event) { return hitView }
if let calloutView = calloutView {
let point = convert(point, to: calloutView)
return calloutView.hitTest(point, with: event)
}
return nil
}
// lets move the add callout here, inside the annotation view class,
// so the annotation view can keep track of its callout
func addCallout(delegate: CalloutViewDelegate) {
removeCallout()
let view = CalloutView(annotation: annotation, delegate: delegate)
view.translatesAutoresizingMaskIntoConstraints = false
addSubview(view)
calloutView = view
NSLayoutConstraint.activate([
view.centerXAnchor.constraint(equalTo: centerXAnchor),
view.bottomAnchor.constraint(equalTo: topAnchor, constant: -10)
])
}
func removeCallout() {
calloutView?.removeFromSuperview()
}
}
// random view controller example that adds an annotation
class ViewController: UIViewController {
@IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
let coordinate = CLLocationCoordinate2D(latitude: 37.332693, longitude: -122.03071)
mapView.camera = MKMapCamera(lookingAtCenter: coordinate, fromDistance: 1_000, pitch: 0, heading: 0)
mapView.register(PickupLocationAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
let hours = """
Mon to Thu 10am-7pm
Fri 12pm-9pm
Sat 10am-11pm
"""
let annotation = PickupLocationAnnotation(hours: hours)
annotation.coordinate = coordinate
mapView.addAnnotation(annotation)
}
}
// the selecting and deselecting of annotation views
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
if let view = view as? PickupLocationAnnotationView {
view.addCallout(delegate: self)
}
}
func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
if let view = view as? PickupLocationAnnotationView {
view.removeCallout()
}
}
}
// the delegate conformance so view controller can know when the callout button was tapped
extension ViewController: CalloutViewDelegate {
func calloutTapped(for annotation: MKAnnotation) {
print(#function)
}
}