Chapter 7 : IOS Animations : Wrong result in landscape mode

Hello,
I’m studying all information in IOS Animations. I’m very happy with the examples and the way this is teached.

However I see a problem in Chapter 7 when I rotate the device. The showItem function moves the icon to high.
I don’t understand why this is happening.

I have added a picture with the examples. I did not change anything on the final code in de ZIP file.

image

Greetings,

Luc Lannoo

@luclannoo Do you still have issues with this?

Yes, I still have it but I did not look further in it.
So you can close this question.
In fact I got no answers.

Luc Lannoo

I just saw this post, and went back and took another look at it. The code in the book does have an issue with the landscape view. It is making the image bigger because of the much bigger width, but that also moves it up higher. With some of the taller phones, it goes too high.

The best solution I found is to set the size of the image based on the smaller of the height and width, so it is the same size in either rotation. This means using “optional” constraints, meaning the priority is lower than 1000. You can then set one for height and width, and auto layout will use which ever one works best. You also need a required constraint that the height and width of the image be equal.

Here is my revised version of showItem, tested on the simulator for SE, 8, and XR:

  func showItem(_ index: Int) {
    
    removeLastItemView()
    
    let imageView = UIImageView(image: UIImage(named: "summericons_100px_0\(index).png"))
    imageView.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.5)
    imageView.layer.cornerRadius = 5.0
    imageView.layer.masksToBounds = true
    imageView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(imageView)
    lastItemView = imageView

    imageView.isUserInteractionEnabled = true
    let tap = UITapGestureRecognizer(target: self, action: #selector(ViewController.didTapItemImage(_:)))
    
    //required constraints (required is the default)
    let conX = imageView.centerXAnchor.constraint(
      equalTo: view.centerXAnchor)
    let conAspect = imageView.heightAnchor.constraint(
      equalTo: imageView.widthAnchor)
    let conBottom = imageView.bottomAnchor.constraint(
      equalTo: view.bottomAnchor)
    conBottom.identifier = "ConBottom"
    
    //lower priority optional constraints
    //orientation will determine which one can be fullfilled
    let conHeight = imageView.heightAnchor.constraint(
      equalTo: view.heightAnchor, multiplier: 0.33)
    conHeight.priority = UILayoutPriority(750)
    
    let conWidth = imageView.widthAnchor.constraint(
      equalTo: view.widthAnchor, multiplier: 0.33)
    conWidth.priority = UILayoutPriority(750)
    conWidth.identifier = "ConWidth"
    
    //activate and lay it all out
    NSLayoutConstraint.activate([conX, conAspect, conBottom, conWidth, conHeight])
    view.layoutIfNeeded()
    
    //capture target value for conBottom (after doing layout)
    let imgOffset = -imageView.frame.height/2
    
    //then setup for animation
    conWidth.constant = -view.frame.width // large enough to get minimum size
    conBottom.constant = imageView.frame.height // off bottom of screen
    view.layoutIfNeeded()
    
    // do the animation
    UIView.animate(
      withDuration: 0.8, delay: 0.0,
      usingSpringWithDamping:  0.4, initialSpringVelocity: 0.0,
      animations: {
        conBottom.constant = imgOffset
        conWidth.constant = 0.0
        self.view.layoutIfNeeded()
    },
      completion: { _ in
        imageView.addGestureRecognizer(tap)
        self.lastItemView = imageView
    })
    
  }

You also need to tweak removeLastItemView, if you want the width shrinking animation to work in landscape rotation. It needs the large ending value for the width constant to get a consistent result:

  func removeLastItemView() {
    guard
      let lastView = lastItemView,
      let conBottom = view.constraints.first(where:{$0.identifier == "ConBottom"}),
      let conWidth = view.constraints.first(where:{$0.identifier == "ConWidth"})
      else { return }
    
    lastItemView = nil
    
    let widthConst = -view.frame.width
    UIView.animate(
      withDuration: 0.5, delay: 0.0,
      animations: {
        conBottom.constant = lastView.frame.height
        conWidth.constant = widthConst
        lastView.alpha = 0.0
        self.view.layoutIfNeeded()
    },
      completion: { _ in
        lastView.removeFromSuperview()
    })
  }

Then you are in business!

This topic was automatically closed after 166 days. New replies are no longer allowed.