Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
Mapview help
-
If anyone could help that would be great I am trying to implement a mkmapviewdelegate to change the way pins look, now I have based this of @omz map view demo from a while ago.
Here is the code
# coding: utf-8 # This has been derived for mapview example by Ole Zorn @omz url to come soon import ui import location from objc_util import * MKUserLocation = ObjCClass('MKUserLocation') MKAnnotationView = ObjCClass('MKPinAnnotationView') MKPointAnnotation = ObjCClass('MKPointAnnotation') MKPinAnnotationView = ObjCClass('MKPinAnnotationView') UIColor = ObjCClass('UIColor') def mapView_viewForAnnotation_(self, cmd, mk_mapview, annotation): try: anno = ObjCInstance(annotation) mapView = ObjCInstance(mk_mapview) if anno.isKindOfClass_(MKPointAnnotation): pinView = mapView.dequeueReusableAnnotationViewWithIdentifier_('annoview') if not pinView: pinView = MKPinAnnotationView.alloc().initWithAnnotation_reuseIdentifier_(anno, 'annoview') pinView.canShowCallout = False else: pinView.annotation = anno return pinView return None except Exception as e: print e methods = [mapView_viewForAnnotation_] protocols = ['MKMapViewDelegate'] try: MyMapViewDelegate = ObjCClass('MyMapViewDelegate') except: MyMapViewDelegate = create_objc_class('MyMapViewDelegate', NSObject, methods=methods, protocols=protocols) class CLLocationCoordinate2D (Structure): _fields_ = [('latitude', c_double), ('longitude', c_double)] class MapView (ui.View): @on_main_thread def __init__(self, *args, **kwargs): try: ui.View.__init__(self, *args, **kwargs) MKMapView = ObjCClass('MKMapView') frame = CGRect(CGPoint(0, 0), CGSize(self.width, self.height)) self.mk_map_view = MKMapView.alloc().initWithFrame_(frame) flex_width, flex_height = (1<<1), (1<<4) self.mk_map_view.setAutoresizingMask_(flex_width|flex_height) self_objc = ObjCInstance(self) self_objc.addSubview_(self.mk_map_view) self.mk_map_view.release() self.map_delegate = MyMapViewDelegate.alloc().init().autorelease() self.mk_map_view.setDelegate_(self.map_delegate) except Exception as e: print e @on_main_thread def add_pin(self, lat, lon, title, subtitle=None, select=False): '''Add a pin annotation to the map''' MKPointAnnotation = ObjCClass('MKPointAnnotation') coord = CLLocationCoordinate2D(lat, lon) annotation = MKPointAnnotation.alloc().init().autorelease() annotation.setTitle_(title) if subtitle: annotation.setSubtitle_(subtitle) annotation.setCoordinate_(coord, restype=None, argtypes=[CLLocationCoordinate2D]) self.mk_map_view.addAnnotation_(annotation) if select: self.mk_map_view.selectAnnotation_animated_(annotation, True) @on_main_thread def remove_all_pins(self): '''Remove all annotations (pins) from the map''' self.mk_map_view.removeAnnotations_(self.mk_map_view.annotations()) if __name__ == '__main__': m = MapView() location.start_updates() loc = location.get_location() location.stop_updates() m.add_pin(lat = loc['latitude'], lon = loc['longitude'],title='Test') m.mk_map_view.setShowsUserLocation_(False) m.present()
If I change return pinView to return None in mapView_viewForAnnotation_ then it doesn't crash and works happily, any advice would be great.
Thanks
-
if not pinView: pinView = MKPinAnnotationView.alloc().initWithAnnotation_reuseIdentifier_(anno, 'annoview') pinView.canShowCallout = False return None else: pinView.annotation = anno return pinView
Don't know if this makes sense (because I don't know anything about objc_util).
-
Returning None will show the standard annotation view which I don't want to do, as referenced here
It seems to crash when it tries to use the annotation view that I create, well that is what I think is the problem anyway.
-
Have you tried retain_global(pinView)? That would fix a problem if it is falling out of scope.
Alternatively, you might need to return pinView.ptr. I forget how callbacks work in objc, and whether that conversion is automatic. -
Yes, you need to return raw pointers from ObjC callbacks (as @JonB suggested), returning an
ObjCInstance
won't work. -
-
I have ran in to another problem now. I have changed the code to try and use my own image for the mkannotationview see below
# coding: utf-8 # This has been derived for mapview example by Ole Zorn @omz url to come soon import ui import location from objc_util import * MKUserLocation = ObjCClass('MKUserLocation') MKAnnotationView = ObjCClass('MKAnnotationView') MKPointAnnotation = ObjCClass('MKPointAnnotation') MKPinAnnotationView = ObjCClass('MKPinAnnotationView') UIColor = ObjCClass('UIColor') UIImage = ObjCClass('UIImage') def mapView_viewForAnnotation_(self, cmd, mk_mapview, annotation): try: anno = ObjCInstance(annotation) mapView = ObjCInstance(mk_mapview) if anno.isKindOfClass_(MKPointAnnotation): pinView = mapView.dequeueReusableAnnotationViewWithIdentifier_('annoview') if not pinView: pinView = MKAnnotationView.alloc().initWithAnnotation_reuseIdentifier_(anno, 'annoview') pinView.canShowCallout = False pinView.image = ui.Image.named('iob:alert_24') else: pinView.annotation = anno return pinView.ptr return None except Exception as e: print 'exception: ' print e methods = [mapView_viewForAnnotation_] protocols = ['MKMapViewDelegate'] try: MyMapViewDelegate = ObjCClass('MyMapViewDelegate') except: MyMapViewDelegate = create_objc_class('MyMapViewDelegate', NSObject, methods=methods, protocols=protocols) class CLLocationCoordinate2D (Structure): _fields_ = [('latitude', c_double), ('longitude', c_double)] class MapView (ui.View): @on_main_thread def __init__(self, *args, **kwargs): try: ui.View.__init__(self, *args, **kwargs) MKMapView = ObjCClass('MKMapView') frame = CGRect(CGPoint(0, 0), CGSize(self.width, self.height)) self.mk_map_view = MKMapView.alloc().initWithFrame_(frame) flex_width, flex_height = (1<<1), (1<<4) self.mk_map_view.setAutoresizingMask_(flex_width|flex_height) self_objc = ObjCInstance(self) self_objc.addSubview_(self.mk_map_view) self.mk_map_view.release() self.map_delegate = MyMapViewDelegate.alloc().init().autorelease() self.mk_map_view.setDelegate_(self.map_delegate) except Exception as e: print e @on_main_thread def add_pin(self, lat, lon, title, subtitle=None, select=False): '''Add a pin annotation to the map''' MKPointAnnotation = ObjCClass('MKPointAnnotation') coord = CLLocationCoordinate2D(lat, lon) annotation = MKPointAnnotation.alloc().init().autorelease() annotation.setTitle_(title) if subtitle: annotation.setSubtitle_(subtitle) annotation.setCoordinate_(coord, restype=None, argtypes=[CLLocationCoordinate2D]) self.mk_map_view.addAnnotation_(annotation) if select: self.mk_map_view.selectAnnotation_animated_(annotation, True) @on_main_thread def remove_all_pins(self): '''Remove all annotations (pins) from the map''' self.mk_map_view.removeAnnotations_(self.mk_map_view.annotations()) if __name__ == '__main__': m = MapView() location.start_updates() loc = location.get_location() location.stop_updates() m.add_pin(lat = loc['latitude'], lon = loc['longitude'],title='Test') m.mk_map_view.setShowsUserLocation_(False) m.present()
I get this exception thrown
exception:
Expected "}" (at char 9), (line:1, col:10)Any help would be appreciated again
-
This seems to be a bug in the
objc_util
module. For some reason, the type encoding for thesetImage:
method is not what I'd expect, andobjc_util
isn't able to parse it correctly.I'll have to look into this in more depth, but here's a quick workaround:
# Instead of pinView.image = ... use: setImage = pinView.setImage_ setImage.encoding = 'v24@0:8@16' setImage(ui.Image.named('iob:alert_24'))
-
Thanks @omz that's works a treat, if you didn't know or guessed I tried it pythonista 3 and the same issue exists.