Search code examples
swiftuikitnslayoutconstraintswift5ios-autolayout

Swift 5: centerXAnchor won't center my frame in the view


I'm stuck at the Consolidation IV challenge from hackingwithswift.com.

Right now I'm trying to create a hangman game. I thought to place placeholder labels based on the length of the answer word. These placeholder labels would be placed inside a frame, which then would be placed in the center of the main view.

Unfortunately, the leading edge of the frame is placed centered. In my opinion, this is not a problem of constraints, but rather a problem of me creating the frame wrong.

My current code is

import UIKit

class ViewController: UIViewController {

    var answer: String!

    override func viewDidLoad() {
        super.viewDidLoad()

        // MARK: - declare all the labels here
        let letterView = UIView()
        letterView.translatesAutoresizingMaskIntoConstraints =  false
        view.addSubview(letterView)

        // MARK: - set constraints to all labels, buttons etc.

        NSLayoutConstraint.activate([

            letterView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
            letterView.centerXAnchor.constraint(equalTo: view.layoutMarginsGuide.centerXAnchor)

        ])

        // MARK: - populate the letterView

        // set the size of the placeholder
        answer = "Atmosphäre"
        let height = 60
        let width = 25
        // var width: Int

        for placeholder in 0..<answer.count {
            // create new label and give it a big font size
            let placeholderLabel = UILabel()
            placeholderLabel.text = "_"
            placeholderLabel.font = UIFont.systemFont(ofSize: 36)

            // calculate frame of this label
            let frame = CGRect(x: placeholder * width, y: height, width: width, height: height)
            placeholderLabel.frame = frame

            // add label to the label view
            letterView.addSubview(placeholderLabel)

        }



    }


}

The simulator screen looks just like this:

enter image description here

I already searched for answers on stackoverflow, but wasn't successful. I think I don't know what I'm exactly looking for.


Solution

  • The main problem, is that the letterView has no size, because no width or height constraints are applied to it.

    To fix your code make the letterView big enough to contain the labels you've added as subviews by adding height and width constraints after the for loop:

    for placeholder in 0..<answer.count {
       ...         
    }
    
    NSLayoutConstraint.activate([
       letterView.widthAnchor.constraint(equalToConstant: CGFloat(width * answer.count)),
       letterView.heightAnchor.constraint(equalToConstant: CGFloat(height))
    ])
    
    

    I'm not sure if you've covered this in your course yet, but a better way to go about this (which would take much less code), is to use a UIStackView as your letterView instead.

    An extra thing to consider:

    If you give the letterView a background color, you'll see that the labels are actually aligned outside of its bounds:

    enter image description here

    That's because you're setting each label's y position to be height, when it should probably be zero:

    let frame = CGRect(x: placeholder * width, y: 0, width: width, height: height)
    

    Correcting this places the labels within the bounds of the letterView:

    enter image description here