I am creating an app, using Swift 5, Xcode 11.3.1. Originally I created the app using storyboard within Swift, however I am now working through the app implementing labels, textfields, pickers, switches, buttons etc programmatically. However, I have come across a snag as I try an programmatically code one of my scenes that is using MKMapView that I created originally with storyboard.
When I created using storyboard, my screen/scene is divided into 3 sections/areas with the first 100pts (CGFloat pts) being used to display some text. Then I have the MKMapView from pt 106 with a height of 413pts. The MKMapView stretches to the edges and so is pretty much a square (ish)... The lower part of the screen / scene is used to display some text, a couple of UISwitches and a custom button. Within the MKMapView I also have a marker that displays the centre point and the users location so that the user can scroll the map to another point on the map which is denoted by the marker in the middle of the map.
So, here's where its going wrong... I am trying to define the same type of look for the MKMapView programmatically, however it keeps showing the map full-screen and therefore overwrites the upper and lower areas that I am using for displaying text and buttons. I have not been able to work out why the MKMapView is not sticking to the frame or constraints I have set. My first attempt was to define the frame, however then I did some reading and I figured I needed to code the constraints too. However, now when the app runs the constraints (first one) is causing the app to bomb out with an error....
2020-03-14 21:43:57.509878+1100 MyGarageApp[17060:95627] * Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with anchors and because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal.' * First throw call stack:
Here is my code for displaying the map:
[This piece of code is before my viewDidLoad]
// Early IBOutlets required to define the scene
var theMapView: MKMapView!
@IBOutlet weak var addressLabelOutlet: UILabel!
let centerMapButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(UIImage(named: "MapPointer.png"), for: .normal)
button.addTarget(self, action: #selector(handleCenterOnUserLocation), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
The next piece of code is a method after my viewDidLoad section (ie. down within my body methods)...
func configureMapView() {
theMapView = MKMapView(frame: CGRect(x: 0, y: 106, width: self.view.frame.width, height:413))
theMapView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
theMapView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 106).isActive = true
theMapView.heightAnchor.constraint(equalToConstant: 413).isActive = true
theMapView.widthAnchor.constraint(equalToConstant: 355).isActive = true
theMapView.showsUserLocation = true
theMapView.userTrackingMode = .follow
theMapView.frame = view.frame
view.addSubview(theMapView)
}
Any guidance that anyone can give to solving this one programmatically would be greatly appreciated as I am keen to get away from the limitations of the storyboard and have the control of my code to cater for devices properly.
Thanking you all in advance.
First of all, using constraints requires translatesAutoresizingMaskIntoConstraints
to be set to false
. Secondly, you set the frame of the map to begin from x = 0, but then set a leading constraint to x = 10, meaning those two are going to be conflicting with each other.
Also, constraints can only be added once the view has been added to the view hierarchy, so move your constraints below the view.addSubview()
line.
Once you have added your constraints, try calling theMapView.layoutIfNeeded()
to actually trigger a re-layout of the map view.
A last comment I have is that the constraints you are defining here could be better defined in relative terms, as right now moving to for instance a larger screen will mess up your layout completely.