Reputation: 73
I'm doing an iOS application. In Xcode 9.1 I create a MKMapView by
let mapView = MKMapView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.height))
mapView.isUserInteractionEnabled = false
mapView.mapType = .satellite
mapView.showsCompass = false
mapView.showsScale = true
view.addSubview(mapView)
but when I run it in the simulator the scale is not shown and I get three messages in the log:
Could not inset compass from edges 9
Could not inset scale from edge 9
Could not inset legal attribution from corner 4
The compass is not shown (as expected) but it's not shown if I change mapView.showsCompass
to true
either. However, the Legal link is shown. What am I missing here? I'm guessing it's something about the new safe areas introduced with iOS 11, but I fail to see how that is important for a view I want to be covering the whole screen.
Upvotes: 7
Views: 7377
Reputation: 12582
import MapKit
///Repaired map view, includes scale
class BetterMKMapView: MKMapView {
///Ideally, get the frame of the legal labels, else a reasonable guess
var guessLabelsPosition: CGRect {
for v in subviews {
if String(describing: type(of: v)) == "MKAttributionLabel" {
return v.frame }
}
return CGRect(x: 10, y: bounds.height - 100, width: 100, height: 20)
}
///Why oh why don't Apple just do this?
lazy var scale: MKScaleView = {
let v = MKScaleView(mapView: self)
v.scaleVisibility = .visible
addSubview(v)
return v
}()
let spaceBetweenScaleAndLegalLabels = 22.0 // your choice
override func layoutSubviews() {
super.layoutSubviews()
// Position the scale nicely
scale.frame = CGRect(
origin: CGPoint(x: 8,
y: guessLabelsPosition.minY - scale.bounds.height
- spaceBetweenScaleAndLegalLabels),
size: scale.bounds.size)
// Other repairs, eg, change absurd default compass position
}
}
Regarding the "8" it's not so easy to get the minX of the overall legal labels, but it's close to 7 or 8.
There's never a case where you use MKMapView without subclassing it, so, that's it.
Upvotes: 0
Reputation: 386
Objective c equivalent:-
if (@available(iOS 11.0, *)) {
// switch OFF the standard scale (otherwise both will be visible when zoom in/out)
self.map.showsScale = false;
// build the view
MKScaleView* scale = [MKScaleView scaleViewWithMapView:self.map];
// we want to use autolayout
scale.translatesAutoresizingMaskIntoConstraints = false;
// scale should be visible all the time
scale.scaleVisibility = MKFeatureVisibilityVisible;// always visible
// add it to the map
[self.view addSubview:scale];
// get the current safe area of the map
UILayoutGuide * guide = self.view.safeAreaLayoutGuide;
// Activate this array of constraints, which at the time removes leftovers if any
[NSLayoutConstraint activateConstraints:
@[
// LEFT (I do not want a change if right-to-left language) margin with an offset to safe area
// alternative would be ".leadingAnchor", which switches to the right margin, if right-to-left language is used
//[scale.leftAnchor constraintEqualToAnchor: guide.centerXAnchor constant: -(scale.frame.size.width/2.0)],
// right edge will be the middle of the map
[scale.rightAnchor constraintEqualToAnchor: guide.centerXAnchor constant: (scale.frame.size.width/2.0)],
// top margin is the top safe area
[scale.bottomAnchor constraintEqualToAnchor: guide.bottomAnchor constant:-self.toolBar.frame.size.height],
// view will be 20 points high
[scale.heightAnchor constraintEqualToConstant: 50.0]
]
];
[self.view bringSubviewToFront:scale];
}
Upvotes: 0
Reputation: 1279
had the same problem with the scale today. I want that scale visible all the time. Cost me several hours to solve it. So I add the code here, just in case, someone run into the same issue.
Got some hints:
from this thread: Use Safe Area Layout programmatically
and this website: Pain Free Constraints with Layout Anchors
Happy coding ...
Hardy
// "self.MapOnScreen" refers to the map currently displayed
// check if we have to deal with the scale
if #available(iOS 11.0, *) {
// as we will change the UI, ensure it's on main thread
DispatchQueue.main.async(execute: {
// switch OFF the standard scale (otherwise both will be visible when zoom in/out)
self.MapOnScreen.showsScale = false
// build the view
let scale = MKScaleView(mapView: self.MapOnScreen)
// we want to use autolayout
scale.translatesAutoresizingMaskIntoConstraints = false
// scale should be visible all the time
scale.scaleVisibility = .visible // always visible
// add it to the map
self.MapOnScreen.addSubview(scale)
// get the current safe area of the map
let guide = self.MapOnScreen.safeAreaLayoutGuide
// Activate this array of constraints, which at the time removes leftovers if any
NSLayoutConstraint.activate(
[
// LEFT (I do not want a change if right-to-left language) margin with an offset to safe area
// alternative would be ".leadingAnchor", which switches to the right margin, if right-to-left language is used
scale.leftAnchor.constraint(equalTo: guide.leftAnchor, constant: 16.0),
// right edge will be the middle of the map
scale.rightAnchor.constraint(equalTo: guide.centerXAnchor),
// top margin is the top safe area
scale.topAnchor.constraint(equalTo: guide.topAnchor),
// view will be 20 points high
scale.heightAnchor.constraint(equalToConstant: 20.0)
]
)
})
}
Upvotes: 8
Reputation: 7451
In iOS 10 or lower
As @Paulw11 says, the scale is only shown while zooming by default.
In iOS 11
You can use scaleVisibility
.
https://developer.apple.com/documentation/mapkit/mkscaleview/2890254-scalevisibility
let scale = MKScaleView(mapView: mapView)
scale.scaleVisibility = .visible // always visible
view.addSubview(scale)
Upvotes: 11