Foreword
Hi, friends , at the beginning I will immediately mark the purpose of the article:
saving your time if you need to
update or embed Yandex cards into a mobile client on iOS, plus the desire to share experiences.
Once we built in Yandexmappit application (approximately in October 2017) instead of e-cards (nothing personal - only business). After 3 months on a beautiful winter day, Androyd version of the cards failed for 2 days because of the keys, the card simply turned into a pumpkin) What the reader missed: “they broke the card on the android, they don’t know how to fix it” At that time as an iOS client it did not touch. The poor fellows on the android ... this time the guys had nothing to do with it. In those days
, many applications fell : Russian post, Utkonos, can you remember the guys?
')

This is to me that when your application is tied to third-party services, it would be nice to have a plan “B” for this case, for example, switch to the previous version of the implementation of cards from Apple, and not replace one with another ...
After another 3 months, somewhere in March, a letter arrived from Yandex that they finally updated sdk, (quite a bit of time passed, 4-5 years from the previous update):
"-Update, after a year the old one will be disconnected," in brief. Before that, there was just an old version 1.0

Well, we, of course, after such a warning, did not pull and immediately began the transition ... after 3 months)) in August.
Stage “commenting”! (Disable functionality)
You tell me, ha ... what is there to update, pod updated, corrected a couple of places and that's all. So, no, guys, the new API of maps is absolutely incompatible with the old one, and moreover, as it turned out later, there is even a lot of vital information missing from the box in the old version and other card libraries!
So, mapkit 3.0 (while version 3.1 has been written), a
link to the documentation .
And why besides the warning from Yandex? Meanwhile, on the Xcode 10 beta version, the project with the old whale is not going to be stupid, since C ++ is being used somewhere inside, which is depricated in the new version. It is necessary to rename it in the sleeper, to update it there, etc., in short, I didn’t do it, because in the end you still need to be updated
1) We update sdk, instead of version 1.0 immediately 3.0, of course the API has changed, but so much so ....
So the old YMKAnnotation Protocol is simply missing.

In a swift, it looks like a forcing-up sheared (: which is not good, and then apply the dragging “!” Sign to yourself ... Example of the protocol implementation:

Well, let's write again) our own, but instead of the coordinate () method we’ll make properties, everything is simple, the method is redundant; title! () is replaced by unforced title (), on the other hand, you can make a property, well, a lot, where you’ll have to change it in the project, so just removed the exclamation mark.
In our project, it was necessary to replace it in 3 controllers a couple of times, in another one to import CoreLocation, since now it is not imported in any of the mapkit headers from Yandex.
So as not to make the usual YMKMapCoordinate (old whale) make typealias for YMKPoint (new whale) throughout the project

2) Let's also declare a few properties that we will need in the future to work with the map, mostly getters:

3) The first thing to create is the YMKMapView (everything is OK with it, such an object is still available). Earlier, I initialized it immediately, now it is impossible to do so, there will be a crash, because you first need to put the key! The current key will not work and you need to ask for a new one. Add to AppDelegate, according to the documentation. Only after installing the key, we can create a YMKMapView and configure it in the manner necessary for us in the setupMap () method.

What happens here, we will analyze in detail later in the course of the need for appropriate settings
4) What's next ?, and then we had the initial location of CLLocation, but now to use, you need to add the CoreLocation framework manually, or ... or replace it with YMKPoint from Yandex Mapkit

5) Next, the map was centered on this coordinate by a very simple method, but now there is no simple

But there is a little more complicated and a little deeper, at the map object) ... mapView.mapWindow.map! .Move. Here we learn about the existence of such an object as YMKCameraPosition.

6) Further comment the config of the map, since there is no such api / properties anymore. Now we just omit it to run at least minimally.
Commenting on the addition of annotations, (note that this is a standard functionality), also comment on the display of the nearest points (this is already some of our functionality).
And the whole YMKMapViewDelegate is also a commentary, which I did not find in the new framework and a similar analogue too.

The implementation dropped, only the methods themselves:
- whether to display the user's location,
- which views to use for pins,
- reaction to pressing the annotation,
- what are the kalauts for annotation,
- Reaction to pressing Callaut, that is, all that we usually use. In the end there was some of our method, which also used a certain API of cards.
EVERYTHING , with MapVC figured out - this is the main class where Mapkit was used
7) A little comment out the custom callaut, the more he does not inherit YMKCalloutView, there is no such thing in the new whale.
Hurray, now the project is going, I was able to start everything and ... see the notebook in the box, because after it starts, you need to give time to “warm up”)), but I did not know about it and thought that something wasn’t so, although suggested that it takes time to activate the key. It turns out that the assumption was true. You need to wait about an hour (maybe something has changed now).
With another attempt to modify the old api in a new way, the map is displayed.
Stage two - “search” (how to implement the old functionality in a new way)
Let's start to restore the lost functionality. So, it is necessary to display user allocation, but as before, simply changing the property and setting the delegate cannot be done.
Now this is done through the layer, see the setupMap () method, point 3.
We'll see an example in the demo (
download from Yandex github ), the benefit is that it is there. By the way, you need to pay attention to setAnchorWith. Later I will say why, due to the zoom. Okay location works.
2) What's next, well, of course, annotations. The old way to add can not, look at the demo again. There is a class - MapObjectsViewController. In the new version, to add pins to the map, the delegate is not needed, for this you need to access the mapObjects property, call the addPlacemark method on the object, well, pass the coordinate to it (there are other overloads) Example:

We iterate over the collection of annotations (for example, after receiving from the server) and add one to the map. The method, by the way, returns a “placeMark” (place mark), which can be used to make additional settings, for example, change the display order through the zIndex property.
Here, however, I missed the point that before this I’m looking for a delegate and I didn’t find him safely and not a single delegate at all (in fact, they just started to call themselves differently, now these are listeners). I know from my previous experience with the whale of whales, and of the old whale of Yandex, that annotations are reused, everything is like with cells, but in the demo there is only the addPlacemark. On the question of Yandex cards for the lead (a little personal acquaintance helped here) “- How to optimize memory usage, reuse objects?” Answer: “Why, so it works fine” ... well, like yes, it works.
Note: 1) It is important to note that Yandex.Maps use mapkit, and do not develop it. This makes the mapkit team (Nick Nikolai's nickname on Habré is likhogrud @).
2) Explanation of why objects are not reused:
In the old whale, the annotationViews were views, they were created by the user, and of course the views must be reused, because their creation is not cheap. In the new whale, the playmarks are created by the mapkit right in the open GL. And maybe they are reused there, but that is inaccurate. In any case, it is much more efficient than creating views.
3) From the new one, by the way, it is possible to modify the annotation icon for the user. Implemented as follows: you need to add a listener (analog delegate), implement the appropriate protocol - 1 of 3 methods, just leave 2 of them empty.
At the same time, we will reload the placemark with our icons.

Also, I will draw your attention to the property anchor. By pressing the user location button on the map, the camera moves the focus to the center of the location. But here's the trouble, re-pressing the action does not produce. What? Comment on the anchor method and everything works.
4) Now you need to show Callaut, respectively, to work out the press. There are several methods in the interfaces, the correct one is YMKMapObjectTapListener. There is one important interesting method that I had to end up with later, it returns true, so as not to be further integrated if a subscriber is found. Pay attention, you must subscribe first, mapObjects will subscribe (line 149).


So pressing fulfills.
Hooray . Still, however, there were attempts to display the pins only in the visible area, this is superfluous, show everything at once, so leave (just because it does not slow down)
5) Then I wanted to make zoom in / out buttons for convenience. A little copy-paste and edits by analogy with the location button, and it's ready.
Further, as we know about the camera, using the move method and, accordingly, the current zoom + - 1 or 0.5, as much as you need. It's all ok.

6) We proceed to the main functional - the collapse (this is such a rectangle with additional info, with a triangle below). It turns out that there is no API (“intoxicates in the summer” - Yandex recognized my speech when I read notes from a piece of paper so as not to manually type this article).

How are you guys? 100 500 applications use a collage.
Well we write in “those support” (Kolya) how to do it manually, I find out. What are your options?
Convert a view into a picture, because you cannot directly add a view (add a fl in 3.1), change the icon ...

Here such crutches turn out on an even, it seemed place.
In fact, it is not crutches, of course, but I simply consider the absence of basic functionality.
7) Ok, let's add test callaut to start with, use the red square as such. So, by clicking on our pin, the delegate / listener method is called, where the click point and the object are transmitted. Without expecting a trick, we take the “point” as the point where you need to add a collage. (Attention, was everything done correctly: “Clicked, took a point, tied”? Ok, 80% answered correctly, 20% did not)

And call the showCallout helper method in the method body:

line 544
Inside we will create a test twist of red forty by forty, wrapping it in a picture, declare a constant x with a value of 0.5, will be used for the position of the triangle collage in the middle of the point. From the idea of changing his position, he later refused, chose to move the camera so that the selected callaut was displayed in the middle of the phone screen
Next, we declare the “pushed” area tappableArea, there is such a property in the style of the icon for the pin. Oh well, you can limit the area you press, so do. The zone ranges from 0.0 to 1.1. we need the lower part, where there is supposedly a button that was transformed into a picture before (remember). Okay, that means the zone (0.0.5 - 1.1) since the button is at the bottom.
Restriction zone works, but there is a nuance, but such that it reduces everything to nothing. If there is another pin under the non-pressed area, then pressing will work on it. The meaning of this area? Made a flag or something, so that the press does not pass. Okay…
550 line
Let's create a style for the icon, you can immediately specify the position of the anchor in the first parameter, for example, I made 557 lines below. The position of y is 1.05 to raise the triangle above the pin again, vertically
559 line
create our custom kollaut of a certain size,
we configure the fields we need using the information from the selected annotation selectedAnnotation, in particular the title and subtitle, the inscription on the button of this collage. Here you yourself can do what you want. The selected annotation is defined earlier in the delegate. But on the map we add the red square created earlier.
Next we add a pin to the mapObjects collection, the method will return the added placemark to us, save it to a variable,
By clicking on the collapse itself, a detailed controller opens, so here’s the nuance, if you hit another pin under the popup, the delegate will work again, so you need to change the order in the hierarchy via zIndex. Set up the visibility and shift our collage to the center on line 564
Nuances : The variable placemark is a pointer to callaut.
First we do not have it, after clicking on the pin, it appears, after clicking on the next pin, we need to remove our first collage and add a new one. Therefore, if the variable placemark! = Nil, you need to delete the old collage from the mapObjects collection)

Also in the case of tapa on the map, you need to hide the collapse, so in the delegate method we assign a placemark, the observer works, the collapse is deleted, and we clear the selected annotation
To do this, we subscribed earlier to YMKMapInputListener

In turn, the method of converting the view is as follows. (In version 3.1, added the ability to add views to the map)
How to do a view I do not describe), but if there are many problems (with a triangle is possible) with this, then write, I will add this stage

The magic 20-ka, added to the height, is needed for the space under the triangle below, which will also have to be drawn
We also want the popup to pop up (was tied) to a pin in a certain place (left, right, middle), for this there is an anchor property. Defined as follows:
The visible area of the map is divided into 3 zones vertically and we determine what we are in, depending on this, we change the position of the binding. In the sample code we check whether we are on the left side, by analogy we do for the middle, if not the left and not in the middle, respectively, the point on the right is

An auxiliary function to check whether a point falls into a region:

Start. Works. But there is a nuance here, it would seem, we try to zoom in, one, two, three, and the collapse flies away from the pin. What? How?

7) Let's start the debug, the coordinates are the same

Then there were attempts to understand what is happening and how it works, the next return to the demo, an even more careful search for differences ...

I notice that the coordinate is transmitted directly, and not the one that is tap! But I’m debugging, you can see that the coordinates are the same, that is, the circle and the square have the same coordinate.
That is why the method did not immediately refer to the object, but passed the point, which is incorrect.

Do you need to cast an object, take a property (everywhere everything is called differently, coordinate, point, here now geometry) is it such a creative or how)? 493 line

Since we need to process two options of pressing: the first one on the pin, the second one on the collapse, and not process the clicks, if we repeatedly clicked on the same pin, the
first thing we do is find the pin we clicked on in the pin collection, comparing coordinates 495 line, otherwise we return true, thereby saying that we processed the click and then we should not go further along the hierarchy
Secondly : we define this by clicking on a pin or on a collage, we also compare the coordinates of the 499 lines. Equality Check:
Further, if it is a collapse and we want to react to pressing the button (or imitate, since it is now a picture), and not to the whole area, then we need to do some calculations with pens :)
- Convert the world coordinate to screen 501 line
We consider it ourselves: we convert map coordinates into screen ones, we know where the anchor is, then by adding the width and height of the view, we get corner points, but for some reason they don’t match and I manually multiplied by three, in my case for the 10th iPhone)). As it turned out, I forgot and did not take into account the number of pixels per point. Which we can have 1x, (one point 1 pixel), 2x, 3x one point is three pixels. - Calculate the height of the button - the height of the collapse + the height of the triangle, multiplied by the scale, information about the scale (line 498). Next, we divide all this by two, since the height of the button is half the height of the collapse
- Then we calculate the coordinates of the corners, based on the fact that the anchor (x: 0.5, y: 1), taking into account the scale and area of the triangle))
- Then these screen coordinates are convertible into world
- Create a visible area based on them, is a zone of the button
- And we check if we hit the button in the zone or not. If we hit, then we check the type of annotation, depending on the type we call some of our methods: go to the detailed screen or choose this store for delivery to StorePoint cases
Otherwise, this is a click on the pin and we need to add a collage, which we actually did above.
That's all, on this initial acquaintance with the new whale is over.
What else would you like to say, in the current implementation, the mapkit of cards contains a lot of functionality that is not used, including the size of the resulting binary. Are you ready for such sacrifices, in the future the guys should be smashed into modules after all . Also from colleagues from the shop with androyd heard that there are problems in compatibility with Kotlin.
P.S. So far I have decided and began to write an article, update 3.1 has been released, where the following problems have been solved and implemented:
Added by
For Android, arm64 and x86 builds appeared.
You can add any View object to the map.
There was a cycle route.
Added nullable annotations for Android.
Changed
MapKit is divided into parts:
MapKit - map only;
MapKit Directions - automotive routing;
MapKit Transport - Pedestrian Routing, Public Transport Routing and Bicycle Routing;
MapKit Search - search and geocoding;
MapKit Places - panoramas.
For iOS, nullable annotations have become more restrictive.
Fixed
Fixed some bugs.
Improved performance.
tech.yandex.ru/maps/doc/mapkit/3.x/concepts/versions-docpageWrite your comments, questions.