I did notice an issue on IOS 11 when using MKMapView.
When I want to click on the button action from the pin dialog and there is another pin behind the button action, it does not get the gesture.
It should be something about zPosition, but I don't know if there is any way to modify just the zPosition of the dialog (MKAnnotationView.annotation) and put it in front of the pins (MKAnnotationView.image) position.
For IOS version < 11 it does work perfectly.
Is someone having the same trouble?
Thank you!
Finally I've found a solution!
Here it comes, if someone needs it:
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
if #available(iOS 11, *) {
for annotationView in mapView.annotations {
if mapView.view(for: annotationView) == view {
mapView.view(for: annotationView)?.isUserInteractionEnabled = true
mapView.view(for: annotationView)?.becomeFirstResponder()
}
else{
mapView.view(for: annotationView)?.isUserInteractionEnabled = false
}
}
}
}
All in order now!
Related
My Current MKMapView is:
But when I zoom in I want to change Pin Image to this:
and again zoom out change to default violet circle.
I just want to change Pin Image when user zoom In/Out MKMapView in iOS Swift 4.
Thanks In Advance!
Happy Coding!
Step 1. First of all you need to get information which pin/annotation display on you map.
Objective c
-(void)getAnotationsInVisibleMapRectangle
{
NSSet *annotationSet = [myMapView annotationsInMapRect:myMapView.annotationVisibleRect];
NSArray *annotationArray = [annotationSet allObjects];
}
Swift
extension MKMapView {
func visibleAnnotations() -> [MKAnnotation] {
return self.annotationsInMapRect(self.visibleMapRect).map { obj -> MKAnnotation in return obj as! MKAnnotation }
}
}
Step 2. You will have delegate method will call on zoom in/out regionDidChangeAnimated. just call the as above function. And update the Your pin.
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
print("call on zoom and zoom out ")
let aryData = mapView.visibleAnnotations()
print(aryData[0].coordinate)
//remove pin from this coordinate
//And add new pin as you want
}
Here you can just find the pin. And remove old pin and add new pin as you want.
I have been stuck on this issue for the past day. I have created a custom MKAnnotation subclass to display various custom pins on an MKMapView. I recursively call a function that keeps animating these pins around the map. My goal is to stop all of these animations in place when the user taps on a button. I have tried
self.view.layer.removeAllAnimations()
and
self.map.layer.removeAllAnimations()
and other hacky solutions, but none seem to work.
Below is the code that creates the animation/pin movement
func animate(duration:Double, newLocation:CLLocationCoordinate2D){
UIView.animate(withDuration: duration, animations: {
self.coordinate = newLocation
}) { (done) in
self.finished_segment()
}
}
Any suggestions are much appreciated.
For anyone stuck on this issue. The problem was that I had to remove the animation from the MKAnnotationView associated with the annotation. I basically created a member variable in the custom class that I set in the mapView annotation delegate method as seen below.
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "annotationView") ?? MKAnnotationView()
if let a = annotation as? Alien{
annotationView.image = a.image
a.annotationView = annotationView
}
return annotationView
}
And to remove the animation from the map. Simply call
self.annotationView.layer.removeAllAnimations()
Currently I have a map that has several Annotations.
For the Annotations I have
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView){
// Do your annotation click work
}
Is it possible to do this for tapping the Map only and not the Annotation and how can I do it?
In ViewWillAppear method :
let gestureRecognizer = UITapGestureRecognizer(target: self, action:"triggerTouchAction:")
mapView.addGestureRecognizer(gestureRecognizer)
And Whatever information you want to show just add code in following method :
func triggerTouchAction(gestureReconizer: UITapGestureRecognizer) {
//Add alert to show it works
}
Hope it going to help you to resolve issue.
I'm trying out mapbox (using the ios sdk) and I've run into a problem that I think I've narrowed down pretty far. This is my code:
func centerMap(location: CLLocationCoordinate2D) {
map.setCenterCoordinate(location,
zoomLevel: 14,
animated: true)
}
func mapView(mapView: MGLMapView, didDeselectAnnotation annotation: MGLAnnotation) {
dealDetails.hidden = false
}
func mapView(mapView: MGLMapView, didUpdateUserLocation userLocation: MGLUserLocation?) {
if let currentLocation = userLocation?.coordinate {
centerMap(currentLocation)
}
}
If I don't re-center the map when the user's location is updated (i.e., just commenting out the centerMap(currentLocation) call) then the annotation remains selected. Re-centering the map calls the didDeselectAnnotation function, and I can't figure out how to keep that annotation selected. Any help is appreciated!
I don't think there's any way around that if you update the center coordinate. You'd have to re-select the annotation. However, you probably don't need to do that. If you set the userTrackingMode on the map view to .Follow, it should re-center automatically.
I have a map on my view controller and I don't know why but the delegate calloutAccessoryControlTapped is also called when I just tap on annotation view, not only when I tap on detail closure. So why this behavior?
import UIKit
import MapKit
extension MapVC: MKMapViewDelegate, CLLocationManagerDelegate
{
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl)
{
...
}
}
As per Apple developer docs:
Accessory views contain custom content and are positioned on either
side of the annotation title text. If a view you specify is a
descendant of the UIControl class, the map view calls this method as a
convenience whenever the user taps your view. You can use this method
to respond to taps and perform any actions associated with that
control. For example, if your control displayed additional information
about the annotation, you could use this method to present a modal
panel with that information.
If your custom accessory views are not descendants of the UIControl
class, the map view does not call this method.
So is your accessory view inherited from UIControl?
Reference:
https://developer.apple.com/library/ios/documentation/MapKit/Reference/MKMapViewDelegate_Protocol/#//apple_ref/occ/intfm/MKMapViewDelegate/mapView:annotationView:calloutAccessoryControlTapped:
Massimo Polimeni seems to be correct. There appears to be a problem with the rightCalloutAccessoryView, but not with the leftCalloutAccessoryView.
The code below (with a leftCalloutAccessoryView) works as expected. If you tap the left accessory, it prints "left accessory selected". If you tap the callout title, it doesn't print anything.
If you use a rightCalloutAccessoryView (commented out, below) and tap the right accessory, it prints "right accessory selected". If you tap the callout title, it also prints "right accessory selected". This does not seem correct.
import UIKit
import MapKit
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2D(latitude: 50.29, longitude: -107.79)
annotation.title = "Swift Current"
mapView.addAnnotation(annotation)
mapView.mapType = .standard
mapView.delegate = self
mapView.region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: annotation.coordinate.latitude,
longitude: annotation.coordinate.longitude),
span: MKCoordinateSpan(latitudeDelta: 1.0, longitudeDelta: 1.0)
)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let reuseId = "Annotation"
var view = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId)
if view == nil {
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
view?.canShowCallout = true
view?.leftCalloutAccessoryView = UIButton(type: .detailDisclosure)
//view?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
} else {
view?.annotation = annotation
}
return view
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.leftCalloutAccessoryView {
print("left accessory selected")
} else if control == view.rightCalloutAccessoryView {
print("right accessory selected")
}
}
}
It depends what kind of button you set on the rightCalloutAccessoryView. E.g. if you use:
[UIButton buttonWithType:UIButtonTypeDetailDisclosure];
Then both tapping the callout and tapping the button both result in calloutAccessoryControlTapped being called with the button. But if you use:
[UIButton systemButtonWithImage:[UIImage systemImageNamed:#"info.circle"] target:nil action:nil];
Then only tapping the button will work, tapping the callout will be disabled.
If you have a custom button and you want the first behavior then you can make a button subclass and do this:
#interface UIButton2 : UIButton
#end
#implementation UIButton2
- (id)_mapkit_accessoryControlToExtendWithCallout{
return self;
}
#end
This private method is how it decides wether a button should also work for tapping the callout (learned this using Hopper). The default implementation checks self.buttonType to decide.
A more sensible way would be to start with the disclosure button and change its image, e.g.
UIButton *button = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
UIImage *image = [UIImage systemImageNamed:#"chevron.right" withConfiguration:[UIImageSymbolConfiguration configurationWithScale:UIImageSymbolScaleSmall];
[button setImage:image forState:UIControlStateNormal];
A lot of this weird behaviour is likely to do with changes in how callout buttons work since iOS 6.