【问题标题】:how to match (or compare) taps to annotations?如何匹配(或比较)点击到注释?
【发布时间】:2016-09-30 20:39:00
【问题描述】:

环境

  • Xcode 8
  • 斯威夫特 3
问题陈述
    我希望能够确定用户是否点击了MKPointAnnotation,然后从该注释中提取信息(如titlesubtitle)以在我的应用程序中使用。
    我想这并不是非常困难,但我有点迷失在我需要做什么/各种类/对象/方法/等方面。我需要用它来做到这一点。
    所以我正在寻找用于指针/指导 - 欢迎使用代码,但在这一点上,指针/指导对我来说将是向前迈出的重要一步。
代码片段
    迄今为止的代码节略版(试图将其限制在相关部分)
class NewLocationViewController: UIViewController, CLLocationManagerDelegate, UITextFieldDelegate {

    //... various @IBOutlet's for text fields, buttons, etc. ...
    @IBOutlet weak var map:         MKMapView!

    var coords:            CLLocationCoordinate2D?
    var locationManager:   CLLocationManager = CLLocationManager()
    var myLocation:        CLLocation!
    var annotation:        MKPointAnnotation!
    var annotationList:    [MKPointAnnotation] = []
    var matchingItems:     [MKMapItem] = [MKMapItem]()

    override func viewDidLoad() {
        super.viewDidLoad()

        //... text field delegates, and other initilizations ...
        locationManager.requestWhenInUseAuthorization()
        if CLLocationManager.locationServicesEnabled() {
            locationManager.desiredAccuracy = kCLLocationAccuracyBest
            locationManager.delegate        = self
        }
        myLocation       = nil
        //... other initializations...
    }

    // Search for things that match what my app is looking for ("<search string>")
    func performSearch() {
        annotationList.removeAll() // clear list
        matchingItems.removeAll()  // clear list 
        var closest                  = MKMapItem()
        var distance                 = 10000.0
        let request                  = MKLocalSearchRequest()
        let span                     = MKCoordinateSpan(latitudeDelta: 0.001, longitudeDelta: 0.001)
        request.naturalLanguageQuery = "<search string>"
        request.region               = MKCoordinateRegionMake(myLocation.coordinate, span)
        let search                   = MKLocalSearch(request: request)

        if search.isSearching {
            search.cancel()
        }
        search.start(completionHandler: {
            (_ response, _ error) in
            if error != nil {
                self.showAlert(msg: "Error occurred in search\nERROR: \(error?.localizedDescription)")
            }
            else if response!.mapItems.count == 0 {
                self.showAlert(msg: "No matches found") 
            }
            else {
                for item in response!.mapItems {

                    // Track the closest placemark to our current [specified] location
                    let (distanceBetween, prettyDistance) = self.getDistance(loc1: self.myLocation, loc2: item.placemark.location!)
                    let addrObj                           = self.getAddress(placemark: item.placemark)
                    //... some code omitted ...

                    // Add markers for all the matches found
                    self.matchingItems.append(item as MKMapItem)
                    let annotation        = MKPointAnnotation()
                    annotation.coordinate = item.placemark.coordinate
                    annotation.title      = item.name
                    annotation.subtitle   = "\(addrObj.address!) (\(prettyDistance))"
                    self.map.addAnnotation(annotation)
                    self.annotationList.append(annotation) 
                }

                //... some code omitted ...
            }
        })
    }

    //... code for getDistance(), getAddress() omitted for brevity - they work as designed ...
    //... other code omitted as not being relevant to the topic at hand
}

我想我需要覆盖touchesEnded,可能还有touchesBegan,也许还有touchesMoved,才能检测到水龙头。
我无法弄清楚的是如何将touch 的位置(表示为屏幕上的 X/Y 坐标)与MKPointAnnotationMKMapItem 的位置(表示为纬度/经度坐标)进行比较在地图上)
所以 - 这就是我目前陷入困境的地方。我在网上搜索了各种术语,但找不到 [简单地] 回答我的问题的任何内容 - 并且以 Swift 代码格式(有许多帖子看起来他们可能有帮助,但是提供的代码不在 Swift 中,而且我不会那么容易地进行翻译)。

更新(美国东部时间 19:48)

class NewLocationViewController: UIViewController, CLLocationManagerDelegate, UITextFieldDelegate, UIGestureRecognizerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        //...other code...
        let tapHandler = UITapGestureRecognizer(target: self, action: Selector(("handleTap:"))) //<<<== See notes below
        tapHandler.numberOfTapsRequired    = 1
        tapHandler.numberOfTouchesRequired = 1
        tapHandler.delegate                = self
        print("A")//#=#
        map.addGestureRecognizer(tapHandler)
        map.isUserInteractionEnabled       = true
        print("B")//#=#
    }

    func handleTap(tap: UITapGestureRecognizer) {
        print("ARRIVED")//#=#
        let here = tap.location(in: map)
        print("I AM HERE: \(here)")//#=#
    }

    //...
}

关于tapHandler的声明/定义,我尝试了以下方法:

    let tapHandler = UITapGestureRecognizer(target: self, action: "handleTap:")
    let tapHandler = UITapGestureRecognizer(target: self, action: Selector("handleTap:")) 
    let tapHandler = UITapGestureRecognizer(target: self, action: Selector(("handleTap:"))) // supresses warning

前两个导致 Xcode 中出现警告,最后一个只是抑制警告:

[W] No method declared with Objective-C selector 'handleTap:'

当我运行我的应用并点击一个图钉时 - 我的日志中出现以下内容:

A
B
libc++abi.dylib: terminating with uncaught exception of type NSException

这似乎(对我来说)表明viewDidLoad 中的一般设置是可以的,但是一旦它尝试处理水龙头,它就死了,而没有到达我的handleTap 函数 - 因此警告(如上所示)似乎要严重得多。

所以,我不确定我是否可以将这视为取得进展,但我正在尝试......

【问题讨论】:

    标签: ios swift swift3 ios10


    【解决方案1】:

    感谢MKAnnotationView and tap detection 我能够找到解决方案。我的代码与最初发布的代码有所不同:

    class NewLocationViewController: UIViewController, CLLocationManagerDelegate, UITextFieldDelegate, UIGestureRecognizerDelegate {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            //...other code...
            let tapHandler = UITapGestureRecognizer() //<<<== No parameters
            tapHandler.numberOfTapsRequired    = 1
            tapHandler.numberOfTouchesRequired = 1
            tapHandler.delegate                = self
            map.addGestureRecognizer(tapHandler)
            map.isUserInteractionEnabled       = true
        }
    
        // Not sure who calls this and requires the Bool response, but it seems to work...
        func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
            return self.handleTap(touch: touch).count > 0
        }
    
        // Major Changes
        private func handleTap(touch: UITouch) -> [MKAnnotationView] {
            var tappedAnnotations: [MKAnnotationView] = []
            for annotation in self.map.annotations {
                if let annotationView: MKAnnotationView = self.map.view(for: annotation) {
                    let annotationPoint = touch.location(in: annotationView)
                    if annotationView.bounds.contains(annotationPoint) {
                        self.name.text    = annotationView.annotation?.title!
                        let addr          = AddrInfo(composite: ((annotationView.annotation?.subtitle)!)!)
                        self.address.text = addr.street!
                        self.city.text    = addr.city!
                        self.state.text   = addr.state!
                        self.zipcode.text = addr.zip!
                        tappedAnnotations.append(annotationView)
                        break
                    }
                }
            }
            return tappedAnnotations
        }
    
        //...
    }
    

    AddrInfo 片段是我自己的小子类,除其他外,它采用“1000 Main St., Pittsburgh, PA 15212, United States 之类的字符串并将其分解为单个碎片,以便它们可以单独访问(如上面的代码所示)。

    可能有一种更简单或更好的方法来实现我正在寻找的东西 - 但上面的确实实现了它,所以我认为它是我问题的答案。

    【讨论】:

      猜你喜欢
      • 2013-05-23
      • 2013-08-27
      • 1970-01-01
      • 2013-11-18
      • 2021-03-04
      • 2012-10-24
      • 1970-01-01
      • 1970-01-01
      • 2022-12-02
      相关资源
      最近更新 更多