I'd like to a-synchronically load images for my custom MKAnnotationView; I'm already using the EGOImageView-framework (it goes very well with UITableViews), but I fail to make it work on MKMapView. Images seem to be loaded, but I cannot refresh them on the map - [myMap setNeedsDisplay]
does nothing.
I haven't tried it myself, but it might work:
Get a view for annotation using MKMapView - (MKAnnotationView *)viewForAnnotation:(id <MKAnnotation>)annotation
method and set new image to it.
Edit: Tried to do that myself and this approach worked fine for me:
// Function where you got new image and want to set it to annotation view
for (MyAnnotationType* myAnnot in myAnnotationContainer){
MKAnnotationView* aView = [mapView viewForAnnotation: myAnnot];
aView.image = [UIImage imageNamed:@"myJustDownloadedImage.png"];
}
After calling this method all annotations images were updated.
I found only one reliable way to update annotation, others methods didn't work for me: just removing and adding annotation again:
id <MKAnnotation> annotation;
// ...
[mapView removeAnnotation:annotation];
[mapView addAnnotation:annotation];
This code will make -[mapView:viewForAnnotation:] to be called again and so your annotation view will be created again (or maybe reused if you use dequeueReusableAnnotationViewWithIdentifier) with correct data.
This is the only way I've found to force redraw of MKAnnotationViews:
// force update of map view (otherwise annotation views won't redraw)
CLLocationCoordinate2D center = mapView.centerCoordinate;
mapView.centerCoordinate = center;
Seems useless, but it works.
This worked for me
#import "EGOImageView.h"
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation
{
//ENTER_METHOD;
if([annotation isKindOfClass:[MKUserLocation class]]) return nil;
MKPinAnnotationView *annView;
static NSString *reuseIdentifier = @"reusedAnnView";
annView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
annView.canShowCallout = YES;
annView.calloutOffset = CGPointMake(-5.0f, 0.0f);
annView.opaque = NO;
annView.image = [[UIImage imageNamed:@"tempImage.png"] retain];
YourModel *p = annotation; // make this conform to <MKAnnotation>
EGOImageView *egoIV = [[EGOImageView alloc] initWithPlaceholderImage:[UIImage imageNamed:@"tempImage.png"]];
[egoIV setDelegate:self];
egoIV.imageURL = [NSURL URLWithString:p.theUrlToDownloadFrom];
[annView addSubview:egoIV];
[egoIV release];
return [annView autorelease];
}
Thank you @Vladimir for the help! Your answer inspired my answer. I made some changes to make it clearer.
func addAnnotation(coordinate: CLLocationCoordinate2D) {
let annotation = Annotation()
annotation.coordinate = coordinate
annotations.append(annotation)
mapView.addAnnotation(annotation)
for index in 0..<annotations.count {
if let view = mapView.view(for: annotations[index]) as? AnnotationView {
if index == 0 {
view.imageView.image = NSImage(named: "icon_positioning")
} else {
view.imageView.image = NSImage(named: "icon_positioning")
}
}
}
}
Also the protocol in MKMapview needs to implement:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let view = mapView.dequeueReusable(withClass: AnnotationView.self, annotation: annotation)
if index == 0 {
view.imageView.image = NSImage(named: "icon_positioning")
} else {
view.imageView.image = NSImage(named: "icon_positioning")
}
return view
}
By the way, the AnnotationView initialization here is:
public extension MKMapView {
func dequeueReusable<T: MKAnnotationView>(withClass name: T.Type,annotation: MKAnnotation?) -> T {
if let view = dequeueReusableAnnotationView(withIdentifier: T.identifier()) as? T {
return view
}
let view = T.init(annotation: annotation, reuseIdentifier: T.identifier())
return view
}
}
Thanks again @Vladimir for the answer
(I don't see how to indicate that this post pertains to zerodiv's answer.) I was able to get zerodiv's method to work by adding a kickstart of sorts, forcing a meaningless but different coordinate, then restoring the prior, correct one. The screen doesn't flash at all because of the first location.
CLLocationCoordinate2D center = mapView.centerCoordinate;
CLLocationCoordinate2D forceChg;
forceChg.latitude = 0;
forceChg.longitude = curLongNum;
mapView.centerCoordinate = forceChg;
mapView.centerCoordinate = center;
I wanted to refresh user location pin with new selected avatar. I found another fast solution for that case. I have 2 screens. The first one is for selecting avatar and the second is for displaying user on map.
swift 5 You can change user location image by creating MKAnnotationView
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
let userIdentifier = "UserLocation"
var userView = mapView.dequeueReusableAnnotationView(withIdentifier: userIdentifier)
if userView == nil {
userView = MKAnnotationView(annotation: annotation, reuseIdentifier: userIdentifier)
} else {
userView?.annotation = annotation
}
userView!.image = UIImage(named: "avatar1") //your image
return userView
}
return nil
}
So if user will change avatar to something else, for example to "avatar2" it won't refresh.
In order to refresh user location image i added this code in viewWillAppear()
self.mapView.showsUserLocation = false
self.mapView.showsUserLocation = true
© 2022 - 2024 — McMap. All rights reserved.