我已经添加了一个子视图到 didSelect
的方法,并且工作正常(截图)。我在弹出窗口里面有一个按钮,它是活动的,并且启用了用户交互,但是我没有得到按钮的动作。
我做了一个视图调试,似乎按钮是在视图堆栈的顶部。
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
DispatchQueue.main.async {
view.addSubview(self.popupView)
}
}
这个动作方法是在 Popupview
@IBAction func actionTapPickup(_ sender: UIButton) {
print("button tapped)
}
你已经声明了你的 actionTapPickup
作为 @IBAction
.
你真的从NIB或在Interface Builder中设计的故事板实例化了这个视图吗?如果是的话,请确保你已经将你的 @IBAction
成功?您是否从 NIB
(而不是以编程方式)?
另一方面,如果您以编程方式创建了这个子视图,您就不会使用 @IBAction
修饰语,而只是将其标记为 @objc
然后手动添加目标为 .touchUpInside
事件。
button.addTarget(self, action: #selector(didTapButton(_:)), for: .touchUpInside)
如果你让我们知道你是通过编程还是通过NIB实例化这个自定义的popupcallout,我们就可以更详细的说明一下。(下面我的例子,就是程序化的实现。)
一句话,在自定义的callout中挂上一个按钮就像在任何地方挂上一个按钮一样。使用 @IBAction
为IB设计的视图,并使用 @objc
和 addTarget
为编程创建的按钮。
作为一个旁观者,一旦你有了眼前的问题,值得注意的是,在让自定义调用正常工作时,有一些微妙的问题。这个 位置感知编程指南。创建呼出 概述了这一点,但不幸的是,例子是在Objective-C中。总之,我们想要的行为是:如果你点击地图上的其他地方(即取消选择注解视图),调用应该会消失。
要做到这一点,很大程度上可以归纳为两个并不明显的小技巧。
添加一个 hitTest
自定义呼出的注解视图,其中包括对点击的测试。这是为了确保当你点击呼出时,注释不会被取消选择。
在整个callout后面添加一个按钮,这样它就可以消耗任何错过你的 "60分钟后领取 "按钮,但仍在callout中的触动。
这样一来。
// 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)
}
}