IOS 10のマップアプリでは、MKUserLocation
MKAnnotationView
の上に方向矢印が表示されるようになりました。これを自分のアプリのMKMapView
に追加する方法はありますか?
編集:手動でこれを実行したいのですが、それが可能かどうかわかりません。マップに注釈を追加して、アニメーション化された動きを含めて、ユーザーの位置を追跡させることはできますか?
MKUserLocation
annotationViewにサブビューを追加して、これを解決しました。
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
}
私もこの同じ問題を経験しました(Apple Mapsアプリと同様に、マップを回転させずに方向インジケーターが必要です)。残念ながらAppleはまだ作成されていません「見出しの青いアイコン」APIが利用可能です。
@ alku83の実装から派生した次のソリューションを作成しました。
デリゲートメソッドを追加して、青い矢印アイコンをマップの場所のドットに追加します
_func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) {
if views.last?.annotation is MKUserLocation {
addHeadingView(toAnnotationView: views.last!)
}
}
_
「青い矢印アイコン」を作成するメソッドを追加します。
_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
}
}
_
クラスに_var headingImageView: UIImageView?
_を追加します。これは主に青い矢印の画像を変換/回転するために必要です。
(アーキテクチャに応じて異なるクラス/オブジェクト内)CLLocationManagerDelegate
プロトコルに準拠するクラスを使用して、ロケーションマネージャーインスタンスを作成します。
_lazy var locationManager: CLLocationManager = {
let manager = CLLocationManager()
// Set up your manager properties here
manager.delegate = self
return manager
}()
_
ロケーションマネージャーがユーザーの見出しデータlocationManager.startUpdatingHeading()
を追跡していること、および適切なときに追跡を停止していることを確認してくださいlocationManager.stopUpdatingHeading()
方向の値を保持する_var userHeading: CLLocationDirection?
_を追加します
見出し値が変更されたときに通知されるデリゲートメソッドを追加し、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)
}
_
次に、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)
}
}
_
はい、手動で行うことができます。
基本的な考え方は、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
での方向の追跡を開始します。
serLocationAnnotation.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
キーワードがここで重要です。これにより、KVOでheading
プロパティへの変更を監視できます。
serLocationAnnotationView.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
は、毎秒60回見出し値を監視できないため、高速で回転すると、アニメーションが少し途切れる場合があります。 UIView.animate
呼び出しはこの小さな問題を解決します。
coordinate
値の更新の適切な処理は、MKPointAnnotation
、MKAnnotationView
、およびMKMapView
クラスですでに実装されているため、自分で行う必要はありません。