Bronze Challenge - what am I doing wrong?

I can’t set the switch next to my label. Not sure why. At one point, I had set the switch’s leading and trailing constraints up the same way as I did my label. While I could set .top and .bottom attributes up in poiSwitchConstraint so that they appeared on on top of one another, I can’t identify the right attributes to set them up next to each other. If I go .left and .right, the switch does not appear at all.

// Bronze Challenge: Add a UILabel and UISwitch to the MapViewController interface
    
let poiLabel = UILabel()
poiLabel.text = "Points of Interest"
    
let poiSwitch = UISwitch()
poiSwitch.isOn = false

poiLabel.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(poiLabel)
    
poiSwitch.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(poiSwitch)

let poiLabelLeadingConstraint = poiLabel.leadingAnchor.constraint(equalTo:     margins.leadingAnchor)
let poiLabelTrailingConstraint = poiLabel.trailingAnchor.constraint(equalTo: margins.trailingAnchor)
poiLabelLeadingConstraint.isActive = true
poiLabelTrailingConstraint.isActive = true

// explicit constraint for Label
let poiLabelConstraint = NSLayoutConstraint(item: poiLabel, attribute: .top, relatedBy:  .equal, toItem: segmentedControl, attribute: .bottom, multiplier: 1.0, constant: 8.0)
poiLabelConstraint.isActive = true
    
// explicit constraint for switch
let poiSwitchConstraint = NSLayoutConstraint(item: poiSwitch, attribute: .left, relatedBy: .equal, toItem: poiLabel, attribute: .right, multiplier: 1.0, constant: 8.0)
poiSwitchConstraint.isActive = true

The trailing edge of your label should not be anchored to the trailing margin. When you put that together with the leading edge being constrained to the leading margin that’s going to stretch the label across the entire screen, and the switch winds up getting pushed off screen. I think that’s what’s messing things up for you, try removing the trailing anchor constraint.

You’re also going to want a constraint connecting the vertical center of the switch to the vertical center of the label. Right now there’s no constraint controlling the vertical position of the switch, so it’s being put wherever the switch’s init function sets it by default.

I added this at the end, but placed the switch underneath the label, not next to it:

 let poiSwitchConstraint = NSLayoutConstraint(item: poiSwitch, attribute: .centerY,    relatedBy: .equal, toItem: segmentedControl, attribute: .centerY, multiplier: 1.0, constant: 8.0)
poiSwitchConstraint.isActive = true

When you mention a constraint connecting the vertical centers of the two objects, did you mean that I need to add a
.centerYAnchor.constraint
for my switch instead?

If you’re placing the switch beneath the label, then you’d want to add a constraint connecting the top anchor of the switch to the bottom anchor of the label. I don’t see how the constraint you added puts the switch underneath the label.

Ha! My fault – that’s because I originally set the multiplier to 5.0, not 1.0. Set to the latter, the switch virtually imposes on top of the label.

No, I’d really like to match the screens in the book. Back to my previous question to that effect: do I need add .centerYAnchor.constraint for my switch?

Yes. Since you have a constraint positioning the label below the segmented control, you should add one equating the centerYanchor of the switch to the centerYanchor of the label.

Thanks - finally got it to work

    let poiSwitchCenterConstraint = poiSwitch.centerYAnchor.constraint(equalTo: poiLabel.centerYAnchor)
    poiSwitchCenterConstraint.isActive = true
    
    let poiSwitchConstraint = NSLayoutConstraint(item: poiSwitch, attribute: .centerX, relatedBy: .equal, toItem: segmentedControl, attribute: .centerX, multiplier: 1.0, constant: 8.0)
poiSwitchConstraint.isActive = true

@Caps5150 Thanks for sharing your solution, it was very helpful to see your approach. I found an easier way to add constraints that didn’t require manually setting isActive = true for each constraint.

Here is my solution:

import UIKit
import MapKit

class MapViewController: UIViewController {
    var mapView: MKMapView!
    let poiLabel = UILabel()
    let poiSwitch = UISwitch()
    let segmentedControl = UISegmentedControl(items: ["Standard", "Hybrid", "Satellite"])
    
    override func loadView() {
        mapView = MKMapView()

        view = mapView

        segmentedControl.backgroundColor = UIColor.systemBackground
        segmentedControl.selectedSegmentIndex = 0
        segmentedControl.addTarget(self, action: #selector(mapTypeChanged(_:)), for: .valueChanged)

        segmentedControl.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(segmentedControl)

        poiLabel.text = "Points of Interest"
        poiLabel.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(poiLabel)

        poiSwitch.translatesAutoresizingMaskIntoConstraints = false
        poiSwitch.isOn = false
        poiSwitch.addTarget(self, action: #selector(poiSelect(_:)), for: .valueChanged)
        
        view.addSubview(poiSwitch)
        
        NSLayoutConstraint.activate([
            segmentedControl.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 1),
            segmentedControl.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor),
            segmentedControl.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor),
            poiLabel.topAnchor.constraint(equalTo: segmentedControl.bottomAnchor, constant: 8),
            poiLabel.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor),
            poiSwitch.topAnchor.constraint(equalTo: segmentedControl.bottomAnchor, constant: 8),
            poiSwitch.leadingAnchor.constraint(equalTo: poiLabel.trailingAnchor, constant: 4),
            poiSwitch.centerYAnchor.constraint(equalTo: poiLabel.centerYAnchor),
        ])
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func poiSelect(_ switchVal: UISwitch) {
        if (switchVal.isOn) {
            mapView.pointOfInterestFilter = MKPointOfInterestFilter.includingAll
        } else {
            mapView.pointOfInterestFilter = MKPointOfInterestFilter.excludingAll
        }
    }
    
    @objc func mapTypeChanged(_ segControl: UISegmentedControl) {
        switch segControl.selectedSegmentIndex {
        case 0:
            mapView.mapType = .standard
            poiLabel.textColor = UIColor.black
        case 1:
            mapView.mapType = .hybrid
            poiLabel.textColor = UIColor.white
        case 2:
            mapView.mapType = .satellite
            poiLabel.textColor = UIColor.white
        default:
            break;
        }
    }
}
1 Like