简体   繁体   中英

Different View/Layout for Landscape Orientation

I am building a calculator app and have it working in portrait mode. However, I want the user to have more options/functions when they switch to landscape mode. In other words, the entire layout would have to change as there will be more buttons in landscape mode compared to portrait mode.

I have managed to do this using the installed property under the attribute inspector. Here are the problems I am running into:

  1. When I run the app in portrait mode and rotate it to landscape, I will show the buttons but they are overlapped. Here is a demo(Just wait for the gif to restart):

在此处输入图片说明

  1. When I run the app in landscape mode, it displays all the buttons correctly, but as soon as I rotate it again to portrait and then back to landscape, the same problem as #1 happens. Here is a demo (Just wait for the gif to restart):

在此处输入图片说明

What am I doing wrong? How can I resolve this?

Here's what I've been doing since iOS9. Complete code, does not assume you are using IB (I actually think you should not) and should help you with how auto layout works. BEWARE, this does assume some knowledge of auto layout and Swift. This also deals with a very simple example of a UIButton .

First, declare two arrays of constraints, typically in the view controller:

var landscape = [NSLayoutConstraint]()
var portrait = [NSLayoutConstraint]()
var myButton = UIButton()

Next, set up your constant constraints and set the active:

myButton.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(myButton)
myButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10).isActive = true
myButton.heightAnchor.constraint(equalToConstant: 40).isActive = true
myButton.widthAnchor.constraint(equalToConstant: 200).isActive = true

So far, so good. You've created a button, added it to the VC view, declared it's size as 40x200. You've also declared it to be anchored to the parent's safeAreaLayoutGuide top anchor.

Now, it's time for the "magic". You wish to move this UIButton from the bottom to the right depending on orientation. (Please note, while certain overrides will work on iPhones, iPads, when full screen, will always have a "Regular" size class - in full screen - no matter the orientation!)

The first thing you need is to put the remaining constraints into the arrays you declared.

portrait.append(myButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10))
landscape.append(myButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10))

Now you have s single constraint to activate. It can be more, remember we are talking arrays. This is simple, just deactivate and activate as needed:

func changeConstraints() {
    if isInPortrait {
        NSLayoutConstraint.deactivate(landscape)
        NSLayoutConstraint.activate(portrait)
    } else {
        NSLayoutConstraint.deactivate(portrait)
        NSLayoutConstraint.activate(landscape)
    }
}

You can even animate things is you wish! Finally, let's tap into the VC life cycle. I'm very sure there are other (and better) ways, but keep in mind what I mentioned earlier - in full screen, an iPad will always have a Regular size class. So I prefer to dig just a bit deeper. I tend to use three variables - you probably need less.

var initialOrientation = true
var isInPortrait = false
var orientationDidChange = false

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    if initialOrientation {
        initialOrientation = false
        if view.frame.width > view.frame.height {
            isInPortrait = false
        } else {
            isInPortrait = true
        }
        changeConstraints()
    } else {
        if view.orientationHasChanged(&isInPortrait) {
            changeConstraints()
        }
    }
}

I also have an extension to UIView :

extension UIView {
    func orientationHasChanged(_ isInPortrait:inout Bool) -> Bool {
        if self.frame.width > self.frame.height {
            if isInPortrait {
                isInPortrait = false
                return true
            }
        } else {
            if !isInPortrait {
                isInPortrait = true
                return true
            }
        }
        return false
    }
}

I think this should describe most of my code. Basically, (a) do not use UIDevice nor (b) use viewWillTransition(toSize:) and check a size class when working with a iPad. (They will have a size class change - albeit differently on devices depending on size, but only when in split screen mode. At least for now!)

Pretty much, use (a) view willLayoutSubviews - you can use view DidLayoutSubviews but I prefer doing things at the earliest point possible - and then (b) check the actual screen size.

You can use iOS orientation change method in view controller class life cycle.

First you have to make all the appropriate UIs using stackViews and views UI objects.

Then you need to manage your constraints (width, height, ratio of the calculator UI buttons/views using iOS size classes.

Also add this method in your view controller and manage your views visibility

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)

    if UIDevice.current.orientation.isLandscape {
        print("Landscape")
        // Show your additional UIs elements views for Landscape
    } else {
        print("Portrait")
        // Hide your additional UIs elements / views for Portrait
    }
}

If you want any assistance for making UI sizes, please follow the option below. So you have to use iOS size classes.

  1. You can add your constraints to a view and edit it at 'Size Inspector'.
  2. Double click the appropriate set constraint and add more required sizes classes using + button just before the the label.
  3. Make sure to uncheck the 'Installed' checkbox and check the required sizes (wC,hC / wR, hR / wC, hR / wR, hC) for the selected constraints respectively (Constraints for portrait mode).
  4. Add new constraints for the UIs of landscape mode as well and do the same this as above no. 3 by checking only the size classes for iPhone landscape mode.

Please review the checking options in Xcode here

Helpful links:

https://medium.com/swlh/ios-adaptive-layout-i-size-class-c561bd730e1 https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/adaptivity-and-layout/

I made a sample work for you and added to My GitHub repositories.

Please have a look on main.storyboard UI setup and get your work done.

Repo: https://github.com/ryra-circuit/DynamicUICalculator

iPhone 11 Portrait

iPhone 11 Landscape

iPhone SE Gen 1 Portrait

iPhone SE Gen 1 Landscape

Thanks

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM