Silver Challenge Solution: Layout Guides

Changes:
Delete function updateOffScreenLabel() and any statements that reference it.

override func viewDidLoad() {
  super.viewDidLoad()
  currentQuestionLabel.text = questions[currentQuestionIndex]
  
  // Disable current centerX constraint of nextQuestionLabel
  nextQuestionLabelCenterXConstraint.isActive = false
  
  // Create a UILayoutGuide instance between the centerXs of the two labels
  let spaceBetweenLabels = UILayoutGuide()
  self.view.addLayoutGuide(spaceBetweenLabels)
  spaceBetweenLabels.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
  nextQuestionLabel.centerXAnchor.constraint(equalTo: spaceBetweenLabels.leadingAnchor).isActive = true
  currentQuestionLabel.centerXAnchor.constraint(equalTo: spaceBetweenLabels.trailingAnchor).isActive = true
}

The x centers of the two labels are linked to the leading and trailing edges of the UILayoutGuide instance. When the currentLabel has moved from center to right outside the view, the nextLabel has moved from left side outside the view to center of the view.

When the animation is completed, the constant of the currentLabel x constraint is set to zero. The currentLabel will be at center of the view and nextLabel will be at one screenwidth left of currentLabel.

func animateLabelTransitions() {
  // Force any outstanding layout changes to occur
  view.layoutIfNeeded()
  
  // Animate the alpha
  // and the center X constraints
  let screenWidth = view.frame.width
        
  self.currentQuestionLabelCenterXConstraint.constant += screenWidth

  UIView.animate(withDuration: 0.5, delay: 0, options: [.curveLinear],
    animations: {
      self.currentQuestionLabel.alpha = 0
      self.nextQuestionLabel.alpha = 1
      self.view.layoutIfNeeded()
    },
    completion: { _ in
      self.currentQuestionLabel.text = self.nextQuestionLabel.text
      self.currentQuestionLabel.alpha = 1
      self.currentQuestionLabelCenterXConstraint.constant = 0
      self.view.layoutIfNeeded()
      self.nextQuestionLabel.alpha = 0
    })
}

Another way to achieve the same effect, replace the part creating the UILayoutGuide instance with the following:

// Create new centerX constraint for nextQuestionLabel. Always one screenWidth left of currentQuestionLabel.
let screenWidth = view.frame.width
nextQuestionLabel.centerXAnchor.constraint(equalTo: currentQuestionLabel.centerXAnchor, constant: -screenWidth).isActive = true

Did anyone else find that when rotating into landscape, their nextQuestionLabel does not become visible? I tried different screen sizes and longer questions but never ran into this problem whether in portrait or landscape.

2 Likes

Yes - I also found that nextQuestionLabel did not become visible.

1 Like

I realized the problem is that the alpha value of the nextQuestionLabel is 0 when it is off screen. If you change it to 1 and rotate the device, you will see the nextQuestionLabel until you press the next question button. Then the screenWidth variable adjusts to the landscape width.

1 Like

Yes. Same thing, not visible when rotated into landscape.

screenWidth = view.frame.width. view.frame.width must automatically adjust when the device is rotated into landscape.

1 Like

I did change it to 1, but still can’t see

No, this apparently breaks the animation when changing the device orientation.