This post is addressed primarily to novice
(and not so) iOS developers.
Often in applications it is required to place a card with labels on some places. Standard tools can change the picture label in the pop-up window when you click on the label you can change the title, subtitle, picture, and also add a button to the right or something else.
At the same time, all these elements can only be of standard size and will look something like the image on the right.

But what if you want to create a non-standard pop-up window in which you can place anything you like
(well, almost anything) in the image on the left?
If you are too lazy to read further and want to immediately download an example, then below there is a link to the githaba You can download and see for yourself.
Those who have already encountered such a task, probably know about
https://github.com/nfarina/calloutview . Based on this example, my decision was made. The problem with nfarina is that its example shows how to use its own popup window for one label. And how to display a set of labels in the example is not considered. Also, for some reason, he led out 2 cards and placed one label on each and wrote it all in one place. This can be a little confusing. Well, there is no description in Russian.
')
Step-by-step instructions for use
To begin with, two standard frameworks, MapKit and QuartzCore, will need to be added to the project.
(The second is optional, but in my example it is used)
Next, add 2 files to the project - SMCalloutView.h and SMCalloutView.m. This class will form pop-up windows. I borrowed it from nfarina. In it, you most likely will not have to edit anything, so we will not consider it.
The focus will be on the controller displaying the card and the labels on it.
As in the example nfarina, in the file containing the controller that displays the map, there will also be 2 classes MapAnnotation and CustomPinAnnotationView. Maybe this is not very correct, but in this case it seemed to me a more elegant and simple solution, since these classes are inextricably linked with the controller and will hardly be used outside of it.
In the controller file, import the MapKit / MapKit.h library and the SMCalloutView.h file. My controller will inherit from UIViewController and comply with the MKMapViewDelegate and SMCalloutViewDelegate protocols. Also, the class must contain a variable that must store all information about the displayed objects. In my case it will be stocks of type NSArray. There will be something else in yours.
The full code of my header file controller NewMapViewController.h:
#import <UIKit/UIKit.h> #import <MapKit/MapKit.h> #import "SMCalloutView.h" @interface NewMapViewController : UIViewController <MKMapViewDelegate, SMCalloutViewDelegate> @property (nonatomic, strong) NSArray *stocks; @end @interface MapAnnotation : NSObject <MKAnnotation> @property (nonatomic, copy) NSString *title, *subtitle; @property (nonatomic, assign) CLLocationCoordinate2D coordinate; @property int idAnn; @end @interface CustomPinAnnotationView : MKPinAnnotationView @property (strong, nonatomic) SMCalloutView *calloutView; @property int idAnn; @end
The variable idAnn of type int will store the id of the label
(or some other object of yours) , the pop-up window of which is shown at the moment. Those. clicked on the label, a window popped up, idAnn changed. Consider the implementation of the controller in more detail.
In my case, the QuartzCore library is imported in the NewMapViewController.m file and the following local variables are declared for the class:
#import "NewMapViewController.h" #import <QuartzCore/QuartzCore.h> @implementation NewMapViewController{ SMCalloutView *calloutView; MKMapView *mapView; NSMutableArray *pins; int idAnn; }
calloutView is a pop-up window, mapView is a map, pins is an array of displayed labels
(not to be confused with stocks) , idAnn is the id of the label, which shows a pop-up window.
In my case, the init file initializes an array of stocks, which will contain information about the output labels
(image, title, price, coordinates) .
- (id)init { self = [super init]; if (self) { pins = [NSMutableArray array]; self.stocks = @[ @{@"img" : @"1.jpg", @"title" : @", , «DAL's Burger» «Gold'n'Brown». 50%", @"price" : @"50% 99 .", @"lat" : @"43.20138", @"lng" : @"76.90597"},
In your case, this information can be obtained from any other place
(json, xml, other classes, etc.).
My viewDidLoad contains only 2 methods:
- (void)viewDidLoad { [super viewDidLoad]; [self setMap]; [self drawMarkers]; }
setMap - initializes the map and shows the desired area. drawMarkers - draws labels on the map. You will need to change these methods to suit your needs.
(I will not give you all the code below. If necessary, you can always download the finished example below using the link on the githaba and see)
The setMap method also indicates that all pop-up windows will have a button added to the right, upon clicking on which an event associated with this label should occur:
Method goToMarker you have to change to your needs. In my case, a UIAlertView just pops up with information about the pressed label:
- (void)goToMarker {
To change the design of your popup window, you will need to change the popupMapCalloutView function.
- (void)popupMapCalloutView { UIView *customView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 260, 88)]; NSDictionary *item = [self.stocks objectAtIndex:idAnn]; UIImageView *photo = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 88.0, 88.0)]; photo.image = [UIImage imageNamed:[item objectForKey:@"img"]]; photo.contentMode = UIViewContentModeScaleAspectFit; photo.layer.cornerRadius = 15.0; photo.layer.masksToBounds = YES; [customView addSubview:photo];
In my case, the data will be taken from the stocks array. Which element is shown is stored by the idAnn variable.
Conclusion
Of course, I do not think that I did something brilliant and very different from the
nfarina example, but still, my manual is intended for Russian speakers
(and in fact there is very little information about iOS development in Russian) . And my example extends the functionality of the original example.
(I also did not consider some of the differences between my controller methods and the original nfarina methods, which you will not have to edit. Or maybe they will)
I ask the developers to point out my mistakes
(where without them?) , And also to share other solutions to this problem
(if there are any on the network) . And discuss everything in the comments.
You can download my example from this link on github -
https://github.com/IbrahimKZ/CustomCalloutViews