0

I want my view to have the following properties (the numbers are arbitrarily chosen):

  • width is equal to height divided by 1.2
  • stays at the bottom right of the screen
  • height is 1/7 of the screen's height when in portrait
  • width and height does not change when the orientation changes

The first three requirements can be easily translated into UILayoutConstraints. I have done them with SnapKit just because it reads more clearly. You should see what I mean even if you have never used SnapKit before.

let myView = UIView(frame: .zero)
myView.backgroundColor = .green
view.addSubview(myView)
myView.snp.makeConstraints { (make) in
    make.right.equalToSuperview().offset(-8)
    make.bottom.equalToSuperview().offset(-8)
    make.width.equalTo(myView.snp.height).dividedBy(1.2)
    make.height.equalTo(view.snp.height).dividedBy(7) // *
}

The problem is the last bullet point. When I rotate the device from portrait to landscape, what was originally the width before the rotation, becomes the height after the rotation. This causes my view to become smaller as a result.

Basically, I want to replace the constraint marked with * with something like this:

make.height.equalTo(max(view.snp.height, view.snp.width)).dividedBy(7)

but I don't think max(a, b) is a thing in SnapKit or the UILayoutConstraint API.

Surely there is some other way of expressing "equal to whichever length is longer", right?

P.S. I didn't tag this with because I would also accept an answer that uses the UILayoutConstraint API.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • iPhone only or iPad and iPhone? The reason I ask is that the iPad has the same size class in both orientations, so you have to do some extra work, but the short answer to your question is to use size classes – Paulw11 Jan 06 '19 at 10:07
  • @Paulw11 iPhone _and_ iPad. – Sweeper Jan 06 '19 at 10:08
  • @Paulw11 You mean I should detect when size classes change in something like `viewWillTransition(to:with:)`, and call `snp.remakeConstraints` depending on the new size classes? – Sweeper Jan 06 '19 at 10:11
  • Yes, you can have both constraints in place and then activate/deactivate as required when the size class changes. That way you can animate the constraint activation alongside the rotation – Paulw11 Jan 06 '19 at 10:13
  • Since the iPad has the same size classes in both orientations you need to override the size class using [this technique](https://stackoverflow.com/questions/26633172/sizing-class-for-ipad-portrait-and-landscape-modes) – Paulw11 Jan 06 '19 at 10:19
  • @Paulw11 Thanks. And I am correct in that I can get the new size classes in `viewWillTransition(to:with:)`, right? – Sweeper Jan 06 '19 at 10:22
  • https://developer.apple.com/documentation/uikit/uicontentcontainer/1621511-willtransition Yes and you use the `animateAlongside...` method of the supplied coordinator to activate/deactivate the relevant constraints in a block – Paulw11 Jan 06 '19 at 10:23

1 Answers1

1

Looks like you have 2 options:

  1. Hardcode the height value.
  2. Try to use nativeBounds:

This rectangle is based on the device in a portrait-up orientation. This value does not change as the device rotates.

In this case the height is always be for portrait mode.

myView.snp.makeConstraints { make in
    make.right.bottom.equalToSuperview().offset(-8)
    let screenHeight = UIScreen.main.nativeBounds.height / UIScreen.main.nativeScale
    let height = screenHeight / 7
    make.width.equalTo(height).dividedBy(1.2)
    make.height.equalTo(height)
}
pacification
  • 5,838
  • 4
  • 29
  • 51
  • If hardcode the height value, what's the point of using autolayout constraints? How can you add a constraint that uses the `nativeBounds.height` as the second item? – Sweeper Jan 06 '19 at 10:18
  • Have you tried this code yourself? I tried this and `myView`'s height is about a third (instead of a seventh) of the screen height. – Sweeper Jan 06 '19 at 10:42
  • @Sweeper, yeah, sorry, i forget to get the real device height. See another update. – pacification Jan 06 '19 at 10:46