21

iOS 10 のマップ アプリには、 の上に見出し方向矢印が含まれるようになりましたMKUserLocation MKAnnotationViewMKMapViewこれを自分のアプリに追加する方法はありますか?

ここに画像の説明を入力

編集:これを手動で行うことができれば幸いですが、それが可能かどうかはわかりませんか? 地図に注釈を追加して、アニメーションの動きを含め、ユーザーの位置を追跡することはできますか?

4

4 に答える 4

21

私もこれと同じ問題を経験しました (Apple マップ アプリと同様に、マップを回転させずに方向インジケーターが必要です)。残念ながら、Apple はまだ「見出しの青いアイコン」API を利用できるようにしていません。

@alku83 の実装から派生した次のソリューションを作成しました。

  1. クラスが MKViewDelegate に準拠していることを確認してください
  2. デリゲート メソッドを追加して、青い矢印アイコンをマップの場所のドットに追加します。

    func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) {
        if views.last?.annotation is MKUserLocation {
            addHeadingView(toAnnotationView: views.last!)
        }
    }
    
  3. 「青い矢印アイコン」を作成するメソッドを追加します。

    func addHeadingView(toAnnotationView annotationView: MKAnnotationView) {
        if headingImageView == nil {
            let image = #YOUR BLUE ARROW ICON#
            headingImageView = UIImageView(image: image)
            headingImageView!.frame = CGRect(x: (annotationView.frame.size.width - image.size.width)/2, y: (annotationView.frame.size.height - image.size.height)/2, width: image.size.width, height: image.size.height)
            annotationView.insertSubview(headingImageView!, at: 0)
            headingImageView!.isHidden = true
         }
    }
    
  4. クラスに追加var headingImageView: UIImageView?します。これは主に、青い矢印の画像を変換/回転するために必要です。

  5. (アーキテクチャに応じて異なるクラス/オブジェクトで)CLLocationManagerDelegateプロトコルに準拠したクラスで、ロケーション マネージャー インスタンスを作成します。

    lazy var locationManager: CLLocationManager = {
        let manager = CLLocationManager()
        // Set up your manager properties here
        manager.delegate = self
        return manager
    }()
    
  6. ロケーション マネージャーがユーザーの見出しデータlocationManager.startUpdatingHeading()を追跡していること、および適切な場合に追跡を停止していることを確認してくださいlocationManager.stopUpdatingHeading()

  7. var userHeading: CLLocationDirection?方向の値を保持するものを追加します

  8. 見出しの値が変更されたときに通知されるデリゲート メソッドを追加し、userHeading の値を適切に変更します。

    func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
         if newHeading.headingAccuracy < 0 { return }
    
         let heading = newHeading.trueHeading > 0 ? newHeading.trueHeading : newHeading.magneticHeading
         userHeading = heading
         NotificationCenter.default.post(name: Notification.Name(rawValue: #YOUR KEY#), object: self, userInfo: nil)
        }
    
  9. MKMapViewDelegateに準拠するクラスで、見出し画像の向きを「変換」するメソッドを追加します

       func updateHeadingRotation() {
            if let heading = # YOUR locationManager instance#,
                let headingImageView = headingImageView {
    
                headingImageView.isHidden = false
                let rotation = CGFloat(heading/180 * Double.pi)
                headingImageView.transform = CGAffineTransform(rotationAngle: rotation)
            }
        }
    
于 2016-11-25T15:55:39.040 に答える
5

はい、手動で行うことができます。

基本的な考え方は、ユーザーの位置を追跡しCLLocationManager、そのデータを使用して地図上に注釈ビューを配置および回転することです。

これがコードです。質問に直接関係のない特定の事項は省略しています (たとえば、ユーザーがアプリの位置情報へのアクセスを既に承認していると想定しています)。そのため、このコードを少し変更することをお勧めします。

ViewController.swift

import UIKit
import MapKit

class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
    @IBOutlet var mapView: MKMapView!
    lazy var locationManager: CLLocationManager = {
        let manager = CLLocationManager()
        manager.delegate = self
        return manager
    }()

    var userLocationAnnotation: UserLocationAnnotation!

    override func viewDidLoad() {
        super.viewDidLoad()

        locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation

        locationManager.startUpdatingHeading()
        locationManager.startUpdatingLocation()

        userLocationAnnotation = UserLocationAnnotation(withCoordinate: CLLocationCoordinate2D(), heading: 0.0)

        mapView.addAnnotation(userLocationAnnotation)
    }

    func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
        userLocationAnnotation.heading = newHeading.trueHeading
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        userLocationAnnotation.coordinate = locations.last!.coordinate
    }

    public func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        if let annotation = annotation as? UserLocationAnnotation {
            let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "UserLocationAnnotationView") ?? UserLocationAnnotationView(annotation: annotation, reuseIdentifier: "UserLocationAnnotationView")
            return annotationView
        } else {
            return MKPinAnnotationView(annotation: annotation, reuseIdentifier: nil)
        }
    }

}

ここでは、マップ ビューの基本的なセットアップを行い、ユーザーの位置とCLLocationManager.

UserLocationAnnotation.swift

import UIKit
import MapKit

class UserLocationAnnotation: MKPointAnnotation {
    public init(withCoordinate coordinate: CLLocationCoordinate2D, heading: CLLocationDirection) {
        self.heading = heading

        super.init()
        self.coordinate = coordinate
    }

    dynamic public var heading: CLLocationDirection
}

MKPointAnnotation進行方向を格納できる非常に単純なサブクラス。dynamicキーワードがここで重要です。headingこれにより、KVOを使用してプロパティの変化を観察できます。

UserLocationAnnotationView.swift

import UIKit
import MapKit

class UserLocationAnnotationView: MKAnnotationView {

    var arrowImageView: UIImageView!

    private var kvoContext: UInt8 = 13

    override public init(annotation: MKAnnotation?, reuseIdentifier: String?) {
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)

        arrowImageView = UIImageView(image: #imageLiteral(resourceName: "Black_Arrow_Up.svg"))
        addSubview(arrowImageView)
        setupObserver()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        arrowImageView = UIImageView(image: #imageLiteral(resourceName: "Black_Arrow_Up.svg"))
        addSubview(arrowImageView)
        setupObserver()
    }

    func setupObserver() {
        (annotation as? UserLocationAnnotation)?.addObserver(self, forKeyPath: "heading", options: [.initial, .new], context: &kvoContext)
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if context == &kvoContext {
            let userLocationAnnotation = annotation as! UserLocationAnnotation
            UIView.animate(withDuration: 0.2, animations: { [unowned self] in
                self.arrowImageView.transform = CGAffineTransform(rotationAngle: CGFloat(userLocationAnnotation.heading / 180 * M_PI))
            })
        }
    }

    deinit {
        (annotation as? UserLocationAnnotation)?.removeObserver(self, forKeyPath: "heading")
    }
}

MKAnnotationViewプロパティの監視を行いheading、適切な回転変換をそのサブビューに設定するサブクラス (私の場合、それは矢印の付いた単なる画像です。より洗練された注釈ビューを作成し、ビュー全体ではなくその一部のみを回転させることができます。 )

UIView.animateオプションです。回転を滑らかにするために追加されます。CLLocationManagerは 1 秒あたり 60 回のヘディング値を観測することができないため、高速で回転すると、アニメーションが少し途切れる可能性があります。UIView.animatecall はこの小さな問題を解決します。

coordinate値の更新の適切な処理はMKPointAnnotationMKAnnotationViewおよびMKMapViewクラスで既に実装されているため、自分で行う必要はありません。

于 2016-10-07T14:20:57.330 に答える
3

サブビューをMKUserLocationannotationViewに追加することでこれを解決しました。

func mapView(mapView: MKMapView, didAddAnnotationViews views: [MKAnnotationView]) {
if annotationView.annotation is MKUserLocation {
    addHeadingViewToAnnotationView(annotationView)
    }
}

func addHeadingViewToAnnotationView(annotationView: MKAnnotationView) {
    if headingImageView == nil {
        if let image = UIImage(named: "icon-location-heading-arrow") {
            let headingImageView = UIImageView()
            headingImageView.image = image
            headingImageView.frame = CGRectMake((annotationView.frame.size.width - image.size.width)/2, (annotationView.frame.size.height - image.size.height)/2, image.size.width, image.size.height)
            self.headingImageView = headingImageView
        }
    }

    headingImageView?.removeFromSuperview()
    if let headingImageView = headingImageView {
        annotationView.insertSubview(headingImageView, atIndex: 0)
    }

    //use CoreLocation to monitor heading here, and rotate headingImageView as required
}
于 2016-10-10T05:47:00.893 に答える