通过 iOS 上 Mapbox 中的按钮在当前位置添加 Maker

Posted

技术标签:

【中文标题】通过 iOS 上 Mapbox 中的按钮在当前位置添加 Maker【英文标题】:Add Maker at current location via Button in Mapbox on iOS 【发布时间】:2020-09-26 10:32:21 【问题描述】:

我正在尝试构建一个应用程序,我可以通过按一个按钮向地图添加标记。我已经可以通过按下按钮来实现这一点,当前位置和默认标题被写入数组中,类型为 MGLPointAnnotation。但我对新注释的可视化 感到困惑。我总是得到错误

Type of expression is ambiguous without more context

到目前为止,这是我的代码:

func makeEntry()
        guard let locValue: CLLocationCoordinate2D = locationManager.location?.coordinate else  return 

        locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
        locationManager.startUpdatingLocation()

        let point = Annotation()
        point.coordinate = CLLocationCoordinate2D(latitude: (locValue.latitude), longitude: (locValue.longitude))
        point.title = "Entry"
        pointAnnotations.append(point)
        mapView.addAnnotations(pointAnnotations) <-- triggers the error waring
        

我是 Swift 的新手,希望你能帮助我。

编辑:

以下是应用程序的其余核心功能:

func addButton()
        entryButton = UIButton(frame: CGRect(x: (view.frame.width/2)-100 , y: (view.frame.height)-75, width: 200, height: 50))
        entryButton.setTitle("Neuer Eintrag", for: .normal)
        entryButton.setTitleColor(UIColor(red: 100/255, green: 180/255, blue: 40/255, alpha: 1), for: .normal)
        entryButton.backgroundColor=(UIColor(red: 255/255, green: 255/255, blue: 255/255, alpha: 1))
        entryButton.layer.cornerRadius = 20
        entryButton.addTarget(self, action: #selector(entryButtonWasPressed(_sender:)), for: .touchUpInside)
        view.addSubview(entryButton)
    
    @objc func entryButtonWasPressed(_sender: UIButton)
        makeEntry()
        print(pointAnnotations)   
    

编辑编辑:这里是完整的代码:

import UIKit
import Mapbox

class ViewController: UIViewController, MGLMapViewDelegate 
    let point = MGLPointAnnotation()
    var entryButton: UIButton!
    let locationManager = CLLocationManager()
    var annotation = MGLPointAnnotation()
    var pointAnnotations = [MGLPointAnnotation]()

    override func viewDidLoad() 
        super.viewDidLoad()
        let mapView = MGLMapView(frame: view.bounds)
        mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        mapView.delegate = self

        // Enable heading tracking mode so that the arrow will appear.
        mapView.userTrackingMode = .followWithHeading

        // Enable the permanent heading indicator, which will appear when the tracking mode is not `.followWithHeading`.
        mapView.showsUserHeadingIndicator = true
        view.addSubview(mapView)

        // Set the map view's delegate
        mapView.delegate = self

        // Allow the map view to display the user's location
        mapView.showsUserLocation = true

        addButton()
        

    func addButton()
        entryButton = UIButton(frame: CGRect(x: (view.frame.width/2)-100 , y: (view.frame.height)-75, width: 200, height: 50))
        entryButton.setTitle("New Entry", for: .normal)
        entryButton.setTitleColor(UIColor(red: 100/255, green: 180/255, blue: 40/255, alpha: 1), for: .normal)
        entryButton.backgroundColor=(UIColor(red: 255/255, green: 255/255, blue: 255/255, alpha: 1))
        entryButton.layer.cornerRadius = 20
        entryButton.addTarget(self, action: #selector(entryButtonWasPressed(_sender:)), for: .touchUpInside)
        view.addSubview(entryButton)
    


    @objc func entryButtonWasPressed(_sender: UIButton)
        makeEntry()
        print(pointAnnotations)
    


    func makeEntry()
        guard let locValue: CLLocationCoordinate2D = locationManager.location?.coordinate else  return 

        locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
        locationManager.startUpdatingLocation()

        let point = MGLPointAnnotation()
        point.coordinate = CLLocationCoordinate2D(latitude: (locValue.latitude), longitude: (locValue.longitude))
        point.title = "Entry"
        pointAnnotations.append(point)
//        mapView.addAnnotations(pointAnnotations)
    

        func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool 
        // Always allow callouts to popup when annotations are tapped.
        return true
        

        func mapView(_ mapView: MGLMapView, didSelect annotation: MGLAnnotation) 
        let camera = MGLMapCamera(lookingAtCenter: annotation.coordinate, fromDistance: 4500, pitch: 15, heading: 0)
        mapView.fly(to: camera, withDuration: 2,
        peakAltitude: 3000, completionHandler: nil)
        
    

    func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? 
        // Substitute our custom view for the user location annotation. This custom view is defined below.
        if annotation is MGLUserLocation && mapView.userLocation != nil 
            return CustomUserLocationAnnotationView()
        
        return nil
    

    // Optional: tap the user location annotation to toggle heading tracking mode.
    func mapView(_ mapView: MGLMapView, didSelect annotation: MGLAnnotation) 
        if mapView.userTrackingMode != .followWithHeading 
            mapView.userTrackingMode = .followWithHeading
         else 
            mapView.resetNorth()
        

        // We're borrowing this method as a gesture recognizer, so reset selection state.
        mapView.deselectAnnotation(annotation, animated: false)
    


// Create a subclass of MGLUserLocationAnnotationView.
class CustomUserLocationAnnotationView: MGLUserLocationAnnotationView 
    let size: CGFloat = 48
    var dot: CALayer!
    var arrow: CAShapeLayer!

    // -update is a method inherited from MGLUserLocationAnnotationView. It updates the appearance of the user location annotation when needed. This can be called many times a second, so be careful to keep it lightweight.
    override func update() 
        if frame.isNull 
            frame = CGRect(x: 0, y: 0, width: size, height: size)
            return setNeedsLayout()
        

        // Check whether we have the user’s location yet.
        if CLLocationCoordinate2DIsValid(userLocation!.coordinate) 
            setupLayers()
            updateHeading()
        
    

    private func updateHeading() 
        // Show the heading arrow, if the heading of the user is available.
        if let heading = userLocation!.heading?.trueHeading 
            arrow.isHidden = false

            // Get the difference between the map’s current direction and the user’s heading, then convert it from degrees to radians.
            let rotation: CGFloat = -MGLRadiansFromDegrees(mapView!.direction - heading)

            // If the difference would be perceptible, rotate the arrow.
            if abs(rotation) > 0.01 
                // Disable implicit animations of this rotation, which reduces lag between changes.
                CATransaction.begin()
                CATransaction.setDisableActions(true)
                arrow.setAffineTransform(CGAffineTransform.identity.rotated(by: rotation))
                CATransaction.commit()
            
         else 
            arrow.isHidden = true
        
    

    private func setupLayers() 
        // This dot forms the base of the annotation.
        if dot == nil 
            dot = CALayer()
            dot.bounds = CGRect(x: 0, y: 0, width: size, height: size)

            // Use CALayer’s corner radius to turn this layer into a circle.
            dot.cornerRadius = size / 2
            dot.backgroundColor = super.tintColor.cgColor
            dot.borderWidth = 4
            dot.borderColor = UIColor.white.cgColor
            layer.addSublayer(dot)
        

        // This arrow overlays the dot and is rotated with the user’s heading.
        if arrow == nil 
            arrow = CAShapeLayer()
            arrow.path = arrowPath()
            arrow.frame = CGRect(x: 0, y: 0, width: size / 2, height: size / 2)
            arrow.position = CGPoint(x: dot.frame.midX, y: dot.frame.midY)
            arrow.fillColor = dot.borderColor
            layer.addSublayer(arrow)
        
    

    // Calculate the vector path for an arrow, for use in a shape layer.
    private func arrowPath() -> CGPath 
        let max: CGFloat = size / 2
        let pad: CGFloat = 3

        let top =    CGPoint(x: max * 0.5, y: 0)
        let left =   CGPoint(x: 0 + pad,   y: max - pad)
        let right =  CGPoint(x: max - pad, y: max - pad)
        let center = CGPoint(x: max * 0.5, y: max * 0.6)

        let bezierPath = UIBezierPath()
        bezierPath.move(to: top)
        bezierPath.addLine(to: left)
        bezierPath.addLine(to: center)
        bezierPath.addLine(to: right)
        bezierPath.addLine(to: top)
        bezierPath.close()

        return bezierPath.cgPath
    

【问题讨论】:

【参考方案1】:

编辑 ******************************************编辑

我将您的整个代码复制/粘贴到一个新的 Mapbox 项目中,并在模拟器上运行它。它在我的模拟位置编译并显示了通常的(小)蓝点。按下按钮什么也没做 - 没有新的注释,也没有错误消息。

查看您的代码,您可以看到在ViewController 类之外有两个方法。他们只是漂浮在无人区。其中之一是委托方法:

func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView?

如果您将此方法移回ViewController 类中,它将在需要时调用,并为您想要的用户位置提供自定义外观,即。大蓝点。

类外的另一个方法是:

func mapView(_ mapView: MGLMapView, didSelect annotation: MGLAnnotation)

但是,您已经在类中拥有此方法的另一个版本,因此您可能希望将两者的功能结合到一个inside类中,然后摆脱外部的。

您遇到“模糊”错误消息的原因是您在 ViewDidLoad 中声明了 mapView,因此它在该类中的任何其他方法中均不可用。相反,在方法外部声明 mapView 以及您的其他类属性,然后在 ViewDidLoad 中正常使用它,如下所示:

var annotation = MGLPointAnnotation()
var pointAnnotations = [MGLPointAnnotation]()

var mapView: MGLMapView! // Declared here

override func viewDidLoad() 
    super.viewDidLoad()

    mapView = MGLMapView(frame: view.bounds) // Instantiated here. (No `let`)
    mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    mapView.delegate = self

现在您的按钮将在用户的位置放置一个标准注释。你可以在你的大蓝点后面看到它。如果有任何不清楚的地方,请告诉我。

【讨论】:

感谢您的详细解答! “mapView.addAnnotation(annotation)”这一行是导致我在原始帖子中引用的错误的那一行(表达式类型不明确,没有更多上下文)。它在我的函数和你的函数中被触发。你有办法解决这个问题吗? 你有let point = Annotation()这一行你能显示你的类注释代码吗? 这已经过时了。 Annotation() 类只是一个 MGLPointAnnotation() 类,带有一个 .id 成员,用于将每个条目链接到一个 UUID,但我无法做到这一点...... 命令“mapView.addAnnotation(annotation)”只在函数外有效。一旦我把它写在一个里面,就会发生错误...... 您真的不应该在函数之外编写该语句 :) 听起来好像您可能不小心将函数放在了函数中或类似的东西中。这可能是错误歧义的根本原因。您是否能够削减代码以便能够发布完整的课程同时仍然保留错误?

以上是关于通过 iOS 上 Mapbox 中的按钮在当前位置添加 Maker的主要内容,如果未能解决你的问题,请参考以下文章

在 Mapbox 的导航视图上添加自定义按钮

ios ionic app中的Mapbox位置请求提示

Mapbox Flutter:初始相机位置显示不正确的位置

在地图 Mapbox JS 上显示用户位置

在 Mapbox 上添加模糊效果

ios:mapkit如何在地图上启用当前位置按钮?