web-dev-qa-db-ja.com

iOS Swift MapKitカスタムアノテーション

ピンを配置しようとすると、マップビュー内に読み込むカスタムアノテーションを取得する際に問題が発生します。

import UIKit
import MapKit
import CoreLocation
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate{
@IBAction func ReportBtn(sender: AnyObject) {
    //MARK: Report Date And Time Details
    let ReportTime = NSDate()
    let TimeStamp = NSDateFormatter()
    TimeStamp.timeStyle = NSDateFormatterStyle.ShortStyle
    TimeStamp.dateStyle = NSDateFormatterStyle.ShortStyle
    TimeStamp.stringFromDate(ReportTime)
    //MARK: Default Point Annotation Begins
    let ReportAnnotation = MKPointAnnotation()
    ReportAnnotation.title = "Annotation Created"
    ReportAnnotation.subtitle = ReportTime.description
    ReportAnnotation.coordinate = locationManager.location!.coordinate
    mapView(MainMap, viewForAnnotation: ReportAnnotation)
    MainMap.addAnnotation(ReportAnnotation)
}

@IBOutlet weak var MainMap: MKMapView!
let locationManager = CLLocationManager()

override func viewDidLoad() {
    super.viewDidLoad()
    self.locationManager.requestWhenInUseAuthorization()
    self.locationManager.delegate = self
    self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
    self.locationManager.startUpdatingLocation()
    self.MainMap.showsUserLocation = true
}


//MARK: - Location Delegate Methods
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
 let location = locations.last
 let center = CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
 let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02 ))
    self.MainMap.setRegion(region, animated: true)
    //self.locationManager.stopUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError){
    print(error.localizedDescription)
}
//MARK:Custom Annotation Begins Here
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
    guard !annotation.isKindOfClass(MKUserLocation) else {
        return nil
    }
    /*if annotation.isKindOfClass(MKUserLocation){
        //emty return, guard wasn't cooperating
    }else{
    return nil
    }*/
    let annotationIdentifier = "AnnotationIdentifier"

    var annotationView: MKAnnotationView?
    if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(annotationIdentifier){
        annotationView = dequeuedAnnotationView
        annotationView?.annotation = annotation
    }
    else{
        let av = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
        av.rightCalloutAccessoryView = UIButton(type: .DetailDisclosure)
        annotationView = av
    }
    if let annotationView = annotationView {
        annotationView.canShowCallout = true
        annotationView.image = UIImage(named: "image.png")
    }
    return annotationView

}
}

追加情報

ボタンの機能が完璧に機能することを確信しています。上記のダンプされた現在のコードでは、デフォルトの赤いピン注釈が正しい場所に表示されます。ピンをタップすると、指定した説明も問題なく表示されます。 このコードで抱えている唯一の問題は、退屈なデフォルトの赤いピンの代わりに自分の画像を取得できないことです

10
Ethan

`MKPointAnnotationをサブクラス化することをお勧めします。

ポケモンピン

カスタムマップピンを表示するために必要なコードのみを含めました。 templateと考えてください。

概要

  • ポイント注釈オブジェクトを作成し、CustomPointAnnotationクラスを使用してカスタム画像名を割り当てます。

  • MKPointAnnotationをサブクラス化して画像を設定し、デリゲートプロトコルメソッドviewForAnnotationに割り当てます。

  • ポイントアノテーションの座標をタイトルとサブタイトルで設定した後、マップにアノテーションビューを追加します。

  • ピンをマップ上に表示するために呼び出されるviewForAnnotationプロトコルメソッドであるMKMapViewDelegateメソッドを実装します。 viewForAnnotationプロトコルメソッドは、ピンビューをカスタマイズしてカスタムイメージを割り当てるのに最適な場所です。

  • ピンの画像名にアクセスするために、特定の識別子の再利用可能な注釈をデキューして返し、カスタムCustomPointAnnotationクラスに注釈をキャストします。

  • Assets.xcassetsに新しい画像セットを作成し、それに応じて[email protected][email protected]を配置します。

  • Plistを忘れないでください。

NSLocationAlwaysUsageDescriptionおよびNSLocationWhenInUseUsageDescription

enter image description here

いつものように、実際のデバイスでテストします。

スウィズル????

// 1

CustomPointAnnotation.Swift

import UIKit
import MapKit

class CustomPointAnnotation: MKPointAnnotation {
var pinCustomImageName:String!
}

// 2

ViewController.Swift

import UIKit
import MapKit

class ViewController: UIViewController, MKMapViewDelegate,  CLLocationManagerDelegate {


@IBOutlet weak var pokemonMap: MKMapView!
let locationManager = CLLocationManager()
var pointAnnotation:CustomPointAnnotation!
var pinAnnotationView:MKPinAnnotationView!

override func viewDidLoad() {
    super.viewDidLoad()

    //Mark: - Authorization
    locationManager.delegate = self
    locationManager.desiredAccuracy = kCLLocationAccuracyBest
    locationManager.requestAlwaysAuthorization()
    locationManager.startUpdatingLocation()

    pokemonMap.delegate = self
    pokemonMap.mapType = MKMapType.Standard
    pokemonMap.showsUserLocation = true

}

func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    let location = CLLocationCoordinate2D(latitude: 35.689949, longitude: 139.697576)
    let center = location
    let region = MKCoordinateRegionMake(center, MKCoordinateSpan(latitudeDelta: 0.025, longitudeDelta: 0.025))
    pokemonMap.setRegion(region, animated: true)

    pointAnnotation = CustomPointAnnotation()
    pointAnnotation.pinCustomImageName = "Pokemon Pin"
    pointAnnotation.coordinate = location
    pointAnnotation.title = "POKéSTOP"
    pointAnnotation.subtitle = "Pick up some Poké Balls"

    pinAnnotationView = MKPinAnnotationView(annotation: pointAnnotation, reuseIdentifier: "pin")
    pokemonMap.addAnnotation(pinAnnotationView.annotation!)
}

func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
    print(error.localizedDescription)
}

//MARK: - Custom Annotation
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
    let reuseIdentifier = "pin"
    var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseIdentifier)

    if annotationView == nil {
        annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseIdentifier)
        annotationView?.canShowCallout = true
    } else {
        annotationView?.annotation = annotation
    }

    let customPointAnnotation = annotation as! CustomPointAnnotation
    annotationView?.image = UIImage(named: customPointAnnotation.pinCustomImageName)

    return annotationView
}
}
51
tymac

対処する必要があるいくつかの問題があります。

MKMapViewと注釈

まず、MKMapViewが注釈から注釈ビューを表示する方法を理解する必要があります。がある

  1. MKMapView-マップを表示し、注釈を管理します。
  2. MKMapViewDelegate-特定の関数からMKMapViewにデータを返します。
  3. MKAnnotation-地図上の場所に関するデータが含まれています。
  4. MKAnnotationView-注釈を表示します。

MKAnnotationは、地図上の場所のデータを保持します。このデータを作成し、MKMapViewに渡します。将来のある時点で、マップビューが注釈を表示する準備ができたら、デリゲートにコールバックし、MKAnnotationViewMKAnnotationを作成するように要求します。デリゲートはビューを作成して返し、マップビューに表示されます。ストーリーボードまたはコードでデリゲートを指定します。 mapView.delegate = self

ロケーション

ユーザーの場所の追跡は、次の点で複雑です。

  1. 位置追跡を有効にする前に、ユーザーの許可が必要です。
  2. ユーザーが追跡を許可してから、ユーザーの位置が利用可能になるまでに遅延があります。
  3. 位置情報サービスが有効になっていません。

コードは、CLLocationManager.authorizationStatusを確認し、CLLocationManagerDelegateメソッドを実装することにより、承認を処理する必要があります。

requestWhenInUseAuthorizationを使用するには、Info.plistNSLocationWhenInUseUsageDescriptionのエントリが必要です。

GitHubのサンプルプロジェクト

import UIKit
import MapKit
import CoreLocation

class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {

    // Instance of location manager. 
    // This is is created in viewDidLoad() if location services are available.
    var locationManager: CLLocationManager?

    // Last location made available CoreLocation.
    var currentLocation: MKUserLocation? {
        didSet {
            // Hide the button if no location is available.
            button.hidden = (currentLocation == nil)
        }
    }

    // Date formatter for formatting dates in annotations.
    // We use a lazy instance so that it is only created when needed.
    lazy var formatter: NSDateFormatter = {

        let formatter = NSDateFormatter()
        formatter.timeStyle = NSDateFormatterStyle.ShortStyle
        formatter.dateStyle = NSDateFormatterStyle.ShortStyle
        return formatter
    }()

    @IBOutlet var button: UIButton!
    @IBOutlet var mapView: MKMapView!

    override func viewDidLoad() {

        super.viewDidLoad()

        mapView.delegate = self

        // Track the user's location if location services are enabled.
        if CLLocationManager.locationServicesEnabled() {

            locationManager = CLLocationManager()
            locationManager?.delegate = self
            locationManager?.desiredAccuracy = kCLLocationAccuracyBest

            switch CLLocationManager.authorizationStatus() {

            case .AuthorizedAlways, .AuthorizedWhenInUse:
                // Location services authorised.
                // Start tracking the user.
                locationManager?.startUpdatingLocation()
                mapView.showsUserLocation = true

            default:
                // Request access for location services.
                // This will call didChangeAuthorizationStatus on completion. 
                locationManager?.requestWhenInUseAuthorization()
            }
        }
    }

    //
    // CLLocationManagerDelegate method
    // Called by CLLocationManager when access to authorisation changes.
    //
    func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {

        switch status {

        case .AuthorizedAlways, .AuthorizedWhenInUse:
            // Location services are authorised, track the user.
            locationManager?.startUpdatingLocation()
            mapView.showsUserLocation = true

        case .Denied, .Restricted:
            // Location services not authorised, stop tracking the user.
            locationManager?.stopUpdatingLocation()
            mapView.showsUserLocation = false
            currentLocation = nil

        default:
            // Location services pending authorisation.
            // Alert requesting access is visible at this point.
            currentLocation = nil
        }
    }

    //
    // MKMapViewDelegate method
    // Called when MKMapView updates the user's location.
    //
    func mapView(mapView: MKMapView, didUpdateUserLocation userLocation: MKUserLocation) {

        currentLocation = userLocation
    }

    @IBAction func addButtonTapped(sender: AnyObject) {

        guard let coordinate = currentLocation?.coordinate else {
            return
        }

        let reportTime = NSDate()
        let formattedTime = formatter.stringFromDate(reportTime)

        let annotation = MKPointAnnotation()
        annotation.title = "Annotation Created"
        annotation.subtitle = formattedTime
        annotation.coordinate = coordinate

        mapView.addAnnotation(annotation)
    }

    //
    // From Bhoomi's answer. 
    //
    // MKMapViewDelegate method
    // Called when the map view needs to display the annotation.
    // E.g. If you drag the map so that the annotation goes offscreen, the annotation view will be recycled. When you drag the annotation back on screen this method will be called again to recreate the view for the annotation.
    //
    func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {

        guard !annotation.isKindOfClass(MKUserLocation) else {

            return nil
        }

        let annotationIdentifier = "AnnotationIdentifier"

        var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(annotationIdentifier)

        if annotationView == nil {
            annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
            annotationView!.rightCalloutAccessoryView = UIButton(type: .DetailDisclosure)
            annotationView!.canShowCallout = true
        }
        else {
            annotationView!.annotation = annotation
        }

        annotationView!.image = UIImage(named: "smile")

        return annotationView

    }
}
13
Luke Van In

プロジェクトバンドルまたはAssets.xcassetsのimage.pngを確認します

 func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {

         guard !annotation.isKindOfClass(MKUserLocation) else {
                return nil
            }

            let annotationIdentifier = "AnnotationIdentifier"

            var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(annotationIdentifier)
            if annotationView == nil {
                annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
        annotationView.rightCalloutAccessoryView = UIButton(type: .DetailDisclosure)
                annotationView!.canShowCallout = true
            }
            else {
                annotationView!.annotation = annotation
            }

            annotationView!.image = UIImage(named: "image.png")

            return annotationView

        }
5
Bhoomi Jagani

次のようにしてくださいあなたのために働くかもしれません。

1)注釈ピンのカスタムクラスを作成します。

_class CustomPointAnnotation: MKPointAnnotation {
    var imageName: UIImage!
}
_

2)変数を以下のように定義します。

_var locationManager = CLLocationManager()
_

3)viewDidLoad()の以下のメソッドを呼び出します

_  func checkLocationAuthorizationStatus() {
        if CLLocationManager.authorizationStatus() == .AuthorizedAlways {
            map.showsUserLocation = false
        } else {
            locationManager.requestWhenInUseAuthorization()
        }
    }
_

4)viewWillAppear()のコードの下に配置

_    self.map.delegate = self
    locationManager.desiredAccuracy = kCLLocationAccuracyBest
    locationManager.requestAlwaysAuthorization()
    locationManager.delegate = self
    dispatch_async(dispatch_get_main_queue(),{
        self.locationManager.startUpdatingLocation()
    })
_

5)最も重要な実装方法は以下です。

_ func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
        if !(annotation is CustomPointAnnotation) {
            return nil
        }

        let reuseId = "Location"

        var anView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
        if anView == nil {
            anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
            anView!.canShowCallout = true
        }
        else {
            anView!.annotation = annotation
        }
        let cpa = annotation as! CustomPointAnnotation
        anView!.image = cpa.imageName

        return anView
    } 
_

6)カスタムPINイメージを受け取った場所で以下のコードを実行します

_   let strLat = "YOUR LATITUDE"
   let strLon = "YOUR LONGITUDE"
   let info = CustomPointAnnotation()
   info.coordinate = CLLocationCoordinate2DMake(strLat.toDouble()!,strLon.toDouble()!)
   info.imageName = resizedImage
   info.title = dict!["locationName"]! as? String
   self.map.addAnnotation(info)
_
4
iMHitesh Surani

コードから、MapKitガイドに従って、コードは正しく見えます。私はそれがこの行であると考えていますannotationView.image = UIImage(named: "image.png")

_image.png_が間違ったイメージ名であるか、コンパイル時にプロジェクトに追加されない可能性はありますか?また、単にfyiで、_.xcassets_を使用している場合は、_.png_を追加する必要はありません。

_annotationView.image_はオプションであるため、イメージUIImage(named: "image.png")がnilの場合、クラッシュせず、デフォルトのピンイメージをレンダリングするだけです。

これが問題でない場合は、他の人がよりよく理解してあなたを助けることができるように、あなたが取ったデバッグ手順に関する詳細情報を提供してください。乾杯=)

1
Zac Kwan