Customize the MKAnnotationView callout - iphone

I want to create a custom MKAnnotationView callout as shown in this image. I have tested several solutions but they only allow customization of the left/right images and title/subtitle. Can anybody please give me some source code or tutorial link for it?
Currently I am clueless. Please help.

I understand you want a pin with a custom callout.
We can't create a custom callout, but we can create an annotation with a completely customized view. So the trick is to add a second annotation when the first is selected, and make the 2nd annotation view look like a callout bubble.
This is the solution posted by users djibouti33 and jacob-jennings in the answer: MKAnnotationView - Lock custom annotation view to pin on location updates, which in turn is based in a blog post from Asynchrony Solutions. For explanation purposes, here is some UML from a forked project:
This is a big hack, but also the cleanest way I've seen to implement custom annotations.
Start with a NSObject "Content" class which has a coordinate, the class of the callout view to use (in the UML is AnnotationView, but you can create more and set them here), and a dictionary of random values with the title, photo url, etc. Use this class to initialize a MKAnnotation "Annotation" object.
#import <MapKit/MapKit.h>
#interface Content : NSObject
#property (nonatomic,assign) CLLocationCoordinate2D coordinate;
// ...
#interface Annotation : NSObject <MKAnnotation, AnnotationProtocol>
-(id) initWithContent:(Content*)content;
// ...
The Annotation implements AnnotationProtocol to announce it wants to handle the creation of its own MKAnnotationView. That is, your MKMapViewDelegate should have code like this:
- (MKAnnotationView *)mapView:(MKMapView *)aMapView viewForAnnotation:(id<MKAnnotation>)annotation
{
// if this is a custom annotation, delegate the implementation of the view
if ([annotation conformsToProtocol:#protocol(AnnotationProtocol)]) {
return [((NSObject<AnnotationProtocol>*)annotation) annotationViewInMap:mapView];
} else {
// else, return a standard annotation view
// ...
}
}
The view returned will be of type AnnotationView, which implements AnnotationViewProtocol to announce that it wants to handle selection/deselection. Therefore, in your map view controller, the methods mapView:didSelectAnnotationView: and mapView:didDeselectAnnotationView: should delegate in a similar way to what we saw before.
When the annotation is selected, a second annotation (CalloutAnnotation) is added, which follows the same behaviour, but this time the view returned (CalloutView) is initialized from a XIB, and contains Core Graphics code (in BaseCalloutView) to animate and replicate a callout.
The initializer of the CalloutView class:
- (id)initWithAnnotation:(CalloutAnnotation*)annotation
{
NSString *identifier = NSStringFromClass([self class]);
self = [super initWithAnnotation:annotation reuseIdentifier:identifier];
if (self!=nil){
[[NSBundle mainBundle] loadNibNamed:identifier owner:self options:nil];
// prevent the tap and double tap from reaching views underneath
UITapGestureRecognizer *tapGestureRecognizer = ...
}
return self;
}
To be able to push another view controller from the callout view I used notifications.
The SO answer I linked at the top contains two complete projects implementing this code (class names may differ). I have another project using the UML above at https://github.com/j4n0/callout.

I added custom UIButton in MKAnnotationView. And on click of that button I have shown popOver with rootViewController with the view similar as you have shown above.

I know this question is from 2011 but for people who still find it in a search:
In iOS 9 you have MKAnnotationView.detailCalloutAccessoryView which entirely replaces the standard callout.

Related

iOS dynamically override subtitle method in id<MKAnnotation> subclass

I'm in a situation where in the
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
method, I need to dynamically change the implementation of the subtitle method for the annotation object. The reason I need to do this is because I'm doing some computations based on latitudes and longitudes that are changing frequently (which I wish to display as the subtitle) ...so when I first create the id object, it doesn't make sense to do that computation at that time.
How would I dynamically override the subtitle method for my custom id object? Can someone point me in the direction of doing that? Or are there any other approaches I could take?
EDIT:
To be a bit more clear... I want to add the annotation custom object to the map BEFORE figuring out what the title and subtitle should be for that annotation object. I want to wait until the user touches on the annotation on the map..and when it shows the popup, that's where I want to calculate what to show as the subtitle. That's why I thought of dynamically overriding the subtitle method of the custom id object.
Thanks!
If you need to dynamically change the implementation of a method at run time, that might call for an application of strategy pattern.
With C blocks, we can do it in a flexible and quick way. Have your custom annotation delegate its implementation of subtitle to the return value of a block property. Then, in your map view's delegate, define blocks that calculate the subtitle based on your requirements, and assign them to the annotation's property.
Sketch of a custom annotation implementation that delegates its subtitle implementation:
typedef NSString* (^AnnotationImplementationSubtitleBlock)();
#interface AnnotationImplementation : NSObject <MKAnnotation>
#property (nonatomic, copy) AnnotationImplementationSubtitleBlock *subtitleBlock;
#end
#implementation AnnotationImplementation
- (NSString *)subtitle
{
return self.subtitleBlock();
}
// Rest of MKAnnotation methods
#end
Also a sketch of the implementation of the map view delegate method where the blocks are created and assigned:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
AnnotationImplementation *customAnnotation = (AnnotationImplementation *)annotation;
if (/* some condition causing you to do it one way */) {
customAnnotation.subtitleBlock = ^{
//calculate the subtitle some way
return calculatedSubtitle;
}
}
else if (/* some condition causing you to do it another way */) {
customAnnotation.subtitleBlock = ^{
//calculate the subtitle the other way
return calculatedSubtitle;
}
}
... rest of method
}

How to obtain the container class of an Actionsheet?

I am a green hand of iPhone development, and I just get confused about a method UIActionsheet,which is the "showInView". So what is the relation between the view who called the actionsheet and the actionsheet it self.
Actually, I wannt to customize the button in an actionsheet, so I create a class for it and overide the methods, and I want really call the methods in the superview, anybody got a solution?
Thank you!
(btw, I tried the following code, but it doesn't work.)
- (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated
{........
else if (buttonIndex==sharers.count+1)
{
AddCommentViewController *parentController=(AddCommentViewController *)[self.superview nextResponder];
}
There is no public API for accessing the container, or owner of a UIActionSheet.
You should not use the superview property to try to get to the owner, the internal view layout for the action sheet is private and can/will change between OS updates.
If you need to get hold of the owner then add a proper property to your UIActionSheet subclass to do this. For example:
#protocol MYActionSheetChoiceDelegate;
#interface MYActionSheet : UIActionSheet <UIActionSheetDelegate> {}
#property(nonatomic, assign) id<MYActionSheetChoiceDelegate> choiceDelegate;
#end
Notice that I name the property choiceDelegate since the delegate property is already taken. Now assuming your subclass is also your it's own UIActionSheetDelegate this can be done:
-(void)actionSheet:(UIActionSheet*)sheet willDismissWithButtonIndex:(NSInteger)index;
{
if (index == SOME_INDEX) {
[self.choiceDelegate actionSheet:self didChooseSomething:index];
}
}
Change and fill the gaps to your own needs.

Annotation Sub Class custom initWithCoordinate

I've subclassed MKAnnotation so that i can assign objects to each annotation, this is so that when the rightCalloutAccessoryView is clicked i can push a navigation controller with the object passed to it and display the objects data in another view.
This all works great apart from one thing, i've extended upon initWithCoordinate like so:
-(id)initWithCoordinate:(CLLocationCoordinate2D)coord andObject:(NSManagedObject *)object {
[self setPlace:object];
coordinate = coord;
title = [place valueForKey:#"name"];
subtitle = [place valueForKey:#"address"];
return self;
}
Although everything is working great i'm recieving the warning:
NO '-initWithCoordinate:andObject:' method found
Which means i'm doing something wrong somewhere, what is the correct way to go about adding upon initWithCoorinate?
Put the prototype -(id)initWithCoordinate:(CLLocationCoordinate2D)coord andObject:(NSManagedObject *)object in .h file.

how to open OpenStreetmap in mkmapview? -iphone

I have gone through certain related answers but dont seem to get correct answer or the answer I need.
As we open googlemap in mkmap view I want to open openstreet map in mkmapview.
If there is any link or sample code showing it please pass it on.
Thanks in advance
MKMapView conforms to Google Map terms and conditions so it uses only google map. You cant integrate OpenStreetMap just like that into MKMapView. Google code has a API RouteME which renders OpenStreetMap in iphone.
RouteMe also provide a good documentation how to include into our project. So feel free to use that.
Import these Frameworks:
#import <MapKit/MapKit.h>
Include this delegate
#interface ViewController () <MKMapViewDelegate>
Add this code to your preexisting viewDidLoad method
(void)viewDidLoad
{
// Tile system URL template goes here
NSString *template = #"http://tile.openstreetmap.org/{z}/{x}/{y}.png";
// Associating overlay with URL template
MKTileOverlay *overlay = [[MKTileOverlay alloc] initWithURLTemplate:template];
// Allowing overlays on MKMapView & disabling Apple map data
overlay.canReplaceMapContent = YES;
// Adding overlay to MKMapView above Apple lables
[self.mapView addOverlay:overlay level:MKOverlayLevelAboveLabels];
// Linking the delegate to allow the next method to be called
self.mapView.delegate = self;
}
And this somewhere in your class that’s mapView’s delegate (most likely a view controller).
(MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
if ([overlay isKindOfClass:[MKTileOverlay class]]) {
// Overlay the tile with the new template
return [[MKTileOverlayRenderer alloc] initWithTileOverlay:overlay];
}
return nil;
}

Creating the Phone App's in-call view in iPhone

I'm trying to create this sort of "pop up action sheet" view similar to the in-call view in iPhone's phone app.
I believe this is a custom view, since I can't seem to find this in any apple references. But somehow the Google app and Discover app both have this view and look awfully similar (I've attached the images below).
So is there some kind of library/tutorial/sample code out there that can help me make something like this?
Thanks.
alt text http://a1.phobos.apple.com/us/r1000/018/Purple/e1/23/02/mzl.uiueoawz.480x480-75.jpg
(source: macblogz.com)
alt text http://ployer.com/archives/2008/02/29/iPhone%20infringes%20call%20display%20patent-thumb-480x799.png
They all look suitably different to be custom views to me. If you just want a control like this for a single view (i.e. not a more flexible configurable container type control) it should be relatively quick & easy to knock it up in xcode & IB. I've done similar things in my apps. Steps I would take are as follows:
1) create an empty NIB file and design your control there by using UIView, UIImageView, UIButton controls etc.
2) Create a new ObjC class derived from UIView
3) Ensure the 'root' UIView object in the NIB has class type matching your ObjC UIView derived class
4) Attach IBOutlets & IBAction event handlers to your class and wire up all the button events ('Touch up inside') to your class event handler methods in IB.
5) Add a static factory function to your class to create itself from the NIB. e.g.
// Factory method - loads a NavBarView from NavBarView.xib
+ (MyCustomView*) myViewFromNib;
{
MyCustomView* myView = nil;
NSArray* nib = [[NSBundle mainBundle] loadNibNamed:#"MyCustomViewNib" owner:nil options:nil];
// The behavior here changed between SDK 2.0 and 2.1. In 2.1+, loadNibNamed:owner:options: does not
// include an entry in the array for File's Owner. In 2.0, it does. This means that if you're on
// 2.2 or 2.1, you have to grab the object at index 0, but if you're running against SDK 2.0, you
// have to grab the object at index:1.
#ifdef __IPHONE_2_1
myView = (MyCustomView *)[nib objectAtIndex:0];
#else
myView = (MyCustomView *)[nib objectAtIndex:1];
#endif
return myView;
}
6) Create and place onto your parent view as normal:
MyCustomView* myView = [MyCustomView myViewFromNib];
[parentView addSubview:myView];
myView.center = parentView.center;
With regard to the event handling, I tend to create just one button event handler, and use the passed id param to determine which button is pressed by comparing against IBOutlet members or UIView tags. I also often create a delegate protocol for my custom view class and call back through that delegate from the button's event handler.
e.g.
MyCustomViewDelegate.h:
#protocol MyCustomViewDelegate
- (void) doStuffForButton1;
// etc
#end
ParentView.m:
myView.delegate = self;
- (void) doStuffForButton1
{
}
MyCustomView.m:
- (IBAction) onButtonPressed:(id)button
{
if (button == self.button1 && delegate)
{
[delegate doStuffForButton1];
}
// or
UIView* view = (UIView*)button;
if (view.tag == 1 && delegate)
{
[delegate doStuffForButton1];
}
}
Hope that helps