Могу ли я назначить разные изображения каждому пину на карте?

Я немного новичок с mapkit, но мне интересно, могу ли я установить разные изображения для каждой булавки на карте. Например, в словаре есть пользовательская информация, и вместо обычного изображения булавки должны быть их собственные изображения. Как я должен установить метод аннотации viewFor для следующего вывода.

[{
email = "user1@gmail.com";
id = jqvDgcBoV9Y4sx1BHCmir5k90dr1;
name = User1;
profileImageUrl = "<null>";
}, {
email = "user2@gmail.com";
id = bqvDmcBoV9Y4sx1BqCmirnk90drz;
name = User2;
profileImageUrl = "https://firebasestorage.googleapis.com/v0/";
}, {
email = "user3@gmail.com";
id = axmDgcB5V9m4sx1nHC5ir5kn1dn3;
name = User3;
profileImageUrl = "https://firebasestorage.googleapis.com/v0/";
}]

Кстати, у меня есть функция для преобразования URL в UIImageView, но не UIImage, это одна из моих больших проблем.

Мой делегат viewForAnnotation на данный момент.

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

    var annotationView: MKAnnotationView?
    var annotationViewx: MKAnnotationView?
    let annotationIdentifier = "AnnotationIdentifier"

    guard !annotation.isKind(of: MKUserLocation.self) else {
        var annotationViewq: MKAnnotationView?
        annotationViewq = MKAnnotationView(annotation: annotation, reuseIdentifier: "userLocation")
        annotationViewq?.image = UIImage(named: "myLocation.png")


        let size = CGSize(width: 17, height: 17)
        UIGraphicsBeginImageContext(size)
        annotationViewq?.image!.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))

        let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
        annotationViewq?.image = resizedImage
        annotationViewq?.isEnabled = true
        annotationViewq?.isUserInteractionEnabled = true
        return annotationViewq

    }


    if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) {
        annotationView = dequeuedAnnotationView
        annotationView?.annotation = annotation
        annotationView?.canShowCallout = true
        annotationView?.image = UIImage(named: "emptyPhoto.png")

        let size = CGSize(width: 17, height: 17)
        UIGraphicsBeginImageContext(size)
        annotationView?.image!.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))

        let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
        annotationView?.image = resizedImage

        return annotationView
    }
        //This annotation not working. but not problem
        let av = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
        av.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
        annotationViewx?.canShowCallout = true
        annotationViewx?.image = UIImage(named: "trafficIcon.png")

        let size = CGSize(width: 17, height: 17)
        UIGraphicsBeginImageContext(size)
        annotationViewx?.image!.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))

        let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
        annotationViewx?.image = resizedImage
        annotationViewx = av


    return annotationViewx

}

2 ответа

Решение

Во-первых, позвольте мне сказать, что я думаю, что Мэтт прибил корень проблемы, а именно, что если у вас есть аннотации с их собственными изображениями, вы должны определить свой собственный тип аннотации, который захватывает URL-адрес изображения, например:

class MyAnnotation: NSObject, MKAnnotation {
    dynamic var coordinate: CLLocationCoordinate2D
    dynamic var title: String?
    dynamic var subtitle: String?

    var imageURL: URL?

    init(coordinate: CLLocationCoordinate2D) {
        self.coordinate = coordinate
        super.init()
    }
}

Когда вы добавляете свои аннотации, убедитесь, что вы указали URL, и вы готовы к гонкам. Единственное, что я хотел бы отметить, это то, что вы действительно хотите отслеживать, какой сетевой запрос связан с какой аннотацией (так что вы можете отменить его, если вам нужно). Так что я бы добавил URLSessionTask свойство класса представления аннотации:

class MyAnnotationView: MKAnnotationView {
    weak var task: URLSessionTask?    // keep track of this in case we need to cancel it when the annotation view is re-used
}

Определив эти два основных класса, вы можете получить viewForAnnotation взять imageURL из пользовательской аннотации, и используйте ее для асинхронного обновления image имущество:

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
    var annotationView: MyAnnotationView?
    let size = CGSize(width: 17, height: 17)

    if annotation is MKUserLocation {
        let annotationIdentifier = "UserLocation"
        annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) as? MyAnnotationView
        if annotationView == nil {
            annotationView = MyAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
            annotationView?.image = UIImage(named: "myLocation.png")?.resized(to: size)
            annotationView?.isEnabled = true                  // generally not necessary
            annotationView?.isUserInteractionEnabled = true   // generally not necessary
        } else {
            annotationView?.annotation = annotation
        }
        return annotationView
    }

    let annotationIdentifier = "AnnotationIdentifier"

    annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) as? MyAnnotationView
    if annotationView == nil {
        annotationView = MyAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
        annotationView?.canShowCallout = true
        annotationView?.image = UIImage(named: "emptyPhoto.png")?.resized(to: size)

        annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
        annotationView?.canShowCallout = true
    } else {
        annotationView?.task?.cancel()          // note, if we're about to reuse annotation view, you might as well cancel pending image retrievals, if any
        annotationView?.annotation = annotation
    }

    // if you want customized image for each annotation view, do that here, not inside the above `if` statement
    if let customAnnotation = annotation as? MyAnnotation, let url = customAnnotation.imageURL {
        let task = URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data, let image = UIImage(data: data)?.resized(to: size), error == nil else {
                print("\(error)")
                return
            }

            DispatchQueue.main.async { annotationView?.image = image }
        }
        annotationView?.task = task
        task.resume()
    }

    return annotationView
}

Обратите внимание, я пытался исправить целый ряд других проблем, скрытых в вашем viewForAnnotation метод, в частности:

  1. Логика изменения размера вашего изображения (а) была повторена пару раз; и (б) не звонил UIGraphicsEndImageContext, Итак, я бы предложил вытащить это (например, в этом UIImage расширение):

    extension UIImage {
        func resized(to size: CGSize) -> UIImage? {
            UIGraphicsBeginImageContextWithOptions(size, false, 0)   // note, using `WithOptions` rendition with `scale` of `0`
            draw(in: CGRect(origin: .zero, size: size))
            let image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()                              // note, if you call `UIGraphicsBeginImageContext`, you must end it, too
            return image
        }
    }
    

    Вы можете подумать, хотите ли вы заполнить аспект или что-то в этом роде, но вот несколько других вариантов этой идеи: /questions/18545729/kak-izmenit-razmer-rastrovogo-izobrazheniya-na-ios/18545737#18545737. Или, может быть, лучше взглянуть на процедуры изменения размера AlamofireImage или Kingfisher.

    Но главная идея заключается в том, что вы должны вытащить излишнюю логику viewForAnnotation и в свою собственную рутину / библиотеку.

  2. Вы действительно должны использовать логику удаления для местоположения пользователя.

  3. Логика удаления должна быть в псевдокоде:

    var annotationView = mapView.dequeueReusableAnnotationView(...)
    
    if annotationView == nil {
        annotationView = MyAnnotionView(...)
        // do stuff only for first-time configuration here
    } else {
        annotationView.annotation = annotation
        // do stuff to reset a re-used annotation view here
    }
    
    // stuff that you do whether dequeued or not should be done here
    

    В вашем примере этот шаблон не всегда соблюдался правильно.

  4. Обратите внимание, я просто делаю просто URLSession.shared.dataTask без поиска кешей и т. д. Очевидно, что здесь вы можете получить более сложные сведения (например, кеширование изображений с измененным размером и т. д.).

  5. Это не имеет большого значения, но я считаю эту конструкцию немного громоздкой:

    guard !annotation.isKind(of: MKUserLocation.self) else { ... }
    

    Так что я упростил это до

    if annotation is MKUserLocation { ... }
    

    Это в значительной степени то же самое, но немного более интуитивно.

  6. Обратите внимание, что вышеприведенная подпрограмма, как и ваша, использует измененное изображение заполнителя для представления аннотации. image имущество. Ради будущих читателей позвольте мне сказать, что это важно, потому что стандартное представление аннотации не изящно обрабатывает асинхронные изменения в размере image, Вы можете либо изменить этот класс для этого, либо, проще, как у нас здесь, использовать заполнитель изображения стандартного размера.

но мне интересно, могу ли я установить разные изображения для каждой булавки на карте

Конечно. Вам необходимо создать пользовательский тип аннотации. Ваш пользовательский тип аннотации будет содержать информацию об изображении в свойстве экземпляра, которое вы передадите. Таким образом, когда вы добираетесь до mapView(_:viewFor:), вы будете знать, какое изображение нужно для этой аннотации, и сможете назначить его.

Пример:

class MyAnnotation : NSObject, MKAnnotation {
    dynamic var coordinate : CLLocationCoordinate2D
    var title: String?
    var subtitle: String?
    var imageName: String?

    init(location coord:CLLocationCoordinate2D) {
        self.coordinate = coord
        super.init()
    }
}

Когда вы создаете аннотацию, присвойте ее imageName а также, прежде чем прикрепить его к карте. Теперь карта вызывает делегата с просьбой о просмотре изображения, вы можете прочитать imageName собственности, и вы будете знать, что делать.

Другие вопросы по тегам