Silver Challenge Solution with UILayoutGuides only

To solve the challenge with UILayoutGuides only the following two methods must be changed to be like that:

func updateOffScreenLabel() {
    let layoutGuide = UILayoutGuide()
    view.addLayoutGuide(layoutGuide)
    layoutGuide.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
    layoutGuide.trailingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
    nextQuestionLabel.centerXAnchor.constraint(equalTo: layoutGuide.centerXAnchor).isActive = true
}

func animateLabelTransitions() {
    view.layoutIfNeeded() //Forces any outstanding layout changes to occur

    nextQuestionLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    let layoutGuide = UILayoutGuide()
    view.addLayoutGuide(layoutGuide)
    layoutGuide.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
    layoutGuide.leadingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
    currentQuestionLabel.centerXAnchor.constraint(equalTo: layoutGuide.centerXAnchor).isActive = true
    
    UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.2, initialSpringVelocity: 0.1, options: [.curveLinear], animations: {
        self.currentQuestionLabel.alpha = 0
        self.nextQuestionLabel.alpha = 1
        self.view.layoutIfNeeded()
    }) { _ in
        swap(&self.currentQuestionLabel, &self.nextQuestionLabel)
        swap(&self.currentQuestionLabelCenterXConstraint, &self.nextQuestionLabelCenterXConstraint)
        self.updateOffScreenLabel()
    }
}

Anyway the old way works fine too since iOS 10. Maybe the problem mentioned by the authors was in the previous SDKs. Even changing nextQuestionLabel.alpha = 1 in viewWillAppear() didn’t show the nextQuestionLabel on the screen. The value of the view.bounds.width property must change as soon as the screen changes including the rotation.

1 Like

im so lost i am trying to get to know the forum on how it works

You post here your questions and check for solutions.

I’d caution against this approach. Every time you declare and add an instance of UILayoutGuide you are adding another instance to the collection. Running this repeatedly would balloon memory usage.

Here is my approach:
@IBOutlet var answerLabel: UILabel!

var layoutGuide: UILayoutGuide! = UILayoutGuide()

override func viewDidLoad() {
    super.viewDidLoad()
    
    currentQuestionLabel.text = questions[currentQuestionIndex]
    view.addLayoutGuide(layoutGuide)
    nextQuestionLabelCenterXConstraint.isActive = false
    layoutGuide.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
    layoutGuide.trailingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
    updateOffScreenLabel()
}

func updateOffScreenLabel() {
    
    nextQuestionLabelCenterXConstraint = nextQuestionLabel.centerXAnchor.constraint(equalTo: layoutGuide.centerXAnchor)
    nextQuestionLabelCenterXConstraint.isActive = true
}

func animateLabelTransitions() {
    
    // Force any outstanding layout changes to occur
    view.layoutIfNeeded()
    
    // Animate the alpha and center the X constraints
    let screenWidth = view.frame.width
    currentQuestionLabelCenterXConstraint.constant += screenWidth
    nextQuestionLabelCenterXConstraint.isActive = false
    nextQuestionLabelCenterXConstraint = nextQuestionLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor)
    nextQuestionLabelCenterXConstraint.isActive = true
    
    view.isUserInteractionEnabled = false
    
    UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 1, options: [.curveLinear], animations: {
        self.currentQuestionLabel.alpha = 0
        self.nextQuestionLabel.alpha = 1
        
        self.view.layoutIfNeeded()
    }, completion: { _ in
        self.currentQuestionLabelCenterXConstraint.isActive = false
        swap(&self.currentQuestionLabel, &self.nextQuestionLabel)
        swap(&self.currentQuestionLabelCenterXConstraint, &self.nextQuestionLabelCenterXConstraint)
        
        self.updateOffScreenLabel()
        self.view.isUserInteractionEnabled = true
    })
}

Setting a constraint’s isActive flag to false allows you to update the value without a constraint conflict arising. Now we just create one instance of layout guide and reuse it.