Могу ли я назначить разные изображения каждому пину на карте?
Я немного новичок с 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
метод, в частности:
Логика изменения размера вашего изображения (а) была повторена пару раз; и (б) не звонил
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
и в свою собственную рутину / библиотеку.Вы действительно должны использовать логику удаления для местоположения пользователя.
Логика удаления должна быть в псевдокоде:
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
В вашем примере этот шаблон не всегда соблюдался правильно.
Обратите внимание, я просто делаю просто
URLSession.shared.dataTask
без поиска кешей и т. д. Очевидно, что здесь вы можете получить более сложные сведения (например, кеширование изображений с измененным размером и т. д.).Это не имеет большого значения, но я считаю эту конструкцию немного громоздкой:
guard !annotation.isKind(of: MKUserLocation.self) else { ... }
Так что я упростил это до
if annotation is MKUserLocation { ... }
Это в значительной степени то же самое, но немного более интуитивно.
Обратите внимание, что вышеприведенная подпрограмма, как и ваша, использует измененное изображение заполнителя для представления аннотации.
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
собственности, и вы будете знать, что делать.