Different layouts in portrait and landscape mode

后端 未结 9 1625
别跟我提以往
别跟我提以往 2020-12-28 14:16

Let\'s assume I have this layout design on an iPad Portrait.

But I would like to have it this way when the iPad is in landscape:

Is it possible to

相关标签:
9条回答
  • 2020-12-28 15:01

    Note - the answers here are good and do address the problem, but on older versions of iOS.

    For iOS11 (Xcode 9) you should consider Adaptive Layouts as referenced here: https://www.raywenderlich.com/162311/adaptive-layout-tutorial-ios-11-getting-started

    0 讨论(0)
  • 2020-12-28 15:01

    I Implemented this with Obj-C and published on my github The solution involves a little amount of code, and most of the work is focused in AutoLayout and naming conventions... The README file explains how I did it. The code I used on the ViewController is basically this method:

    - (void)setUpViewConstraintsForInterfaceOrientation:(InterfaceOrientation)interfaceOrientation {
        self.lastOrientation = interfaceOrientation;
        if (interfaceOrientation == Landscape) {
            [NSLayoutConstraint deactivateConstraints:self.portraitConstraintsCollection];
            [NSLayoutConstraint activateConstraints:self.landscapeConstraintsCollection];
        } else if(interfaceOrientation == Portrait){
            [NSLayoutConstraint deactivateConstraints:self.landscapeConstraintsCollection];
            [NSLayoutConstraint activateConstraints:self.portraitConstraintsCollection];
        }
        [self.view layoutIfNeeded];
    }
    

    portraitConstraintsCollection and landscapeConstraintsCollection are IBOutletCollection properties to manage the orientation's specific constraints.

    And the autolayout solution only works with installing and uninstalling constraints (activate and deactivate), no need to add or remove constraints.

    0 讨论(0)
  • 2020-12-28 15:02

    The only way I have easily achieved this, so it works on iPads and iPhones is by doing it programmatically. This is done with Swift 5.0 in Xcode 10.2.

    In your ViewController, define the two views you want to change depending on orientation:

    @IBOutlet weak var raceInfoView: UIStackView!
    @IBOutlet weak var raceListView: UITableView!
    

    Then define the constraints which will always stay the same in your storyboard and define the ones which will change in your ViewController.

    private var portraitRaceInfoViewTrailing: NSLayoutConstraint!
    private var portraitRaceInfoViewBottom: NSLayoutConstraint!
    private var portraitRaceListViewLeading: NSLayoutConstraint!
    private var landscapeRaceInfoViewTrailing: NSLayoutConstraint!
    private var landscapeRaceInfoViewBottom: NSLayoutConstraint!
    private var landscapeRaceListViewTop: NSLayoutConstraint!
    

    Next, initialise the constraints, I put it in viewDidLoad but it can probably be put somewhere else.

    override func viewDidLoad() {
        super.viewDidLoad()
    
        portraitRaceInfoViewTrailing = NSLayoutConstraint(
            item: racesView as Any, attribute: NSLayoutConstraint.Attribute.trailing,
            relatedBy: NSLayoutConstraint.Relation.equal,
            toItem: raceInfoView, attribute: NSLayoutConstraint.Attribute.trailing,
            multiplier: 1, constant: 0)
        portraitRaceInfoViewBottom = NSLayoutConstraint(
            item: raceListView as Any, attribute: NSLayoutConstraint.Attribute.top,
            relatedBy: NSLayoutConstraint.Relation.equal,
            toItem: raceInfoView, attribute: NSLayoutConstraint.Attribute.bottom,
            multiplier: 1, constant: 0)
        portraitRaceListViewLeading = NSLayoutConstraint(
            item: raceListView as Any, attribute: NSLayoutConstraint.Attribute.leading,
            relatedBy: NSLayoutConstraint.Relation.equal,
            toItem: racesView, attribute: NSLayoutConstraint.Attribute.leading,
            multiplier: 1, constant: 0)
    
        landscapeRaceInfoViewTrailing = NSLayoutConstraint(
            item: raceListView as Any, attribute: NSLayoutConstraint.Attribute.leading,
            relatedBy: NSLayoutConstraint.Relation.equal,
            toItem: raceInfoView, attribute: NSLayoutConstraint.Attribute.trailing,
            multiplier: 1, constant: 0)
        landscapeRaceInfoViewBottom = NSLayoutConstraint(
            item: raceInfoView as Any, attribute: NSLayoutConstraint.Attribute.bottom,
            relatedBy: NSLayoutConstraint.Relation.equal,
            toItem: racesView, attribute: NSLayoutConstraint.Attribute.bottom,
            multiplier: 1, constant: 0)
        landscapeRaceListViewTop = NSLayoutConstraint(
            item: raceListView as Any, attribute: NSLayoutConstraint.Attribute.top,
            relatedBy: NSLayoutConstraint.Relation.equal,
            toItem: racesView, attribute: NSLayoutConstraint.Attribute.top,
            multiplier: 1, constant: 0)
    
        applyOrientationConstraints()
    }
    

    Declaring constraints programmatically looks a bit scary, but its actually quite easy. You can create the constraint in your storyboard and view all the values and copy the right ones to the right place in the code.

    And finally in viewDidLoad apply the constraints with applyOrientationConstraints().

    func applyOrientationConstraints() {
        let orient = UIApplication.shared.statusBarOrientation
        switch orient {
        case .portrait:
            NSLayoutConstraint.activate([portraitRaceInfoViewTrailing, portraitRaceInfoViewBottom, portraitRaceListViewLeading])
            NSLayoutConstraint.deactivate([landscapeRaceInfoViewTrailing, landscapeRaceInfoViewBottom, landscapeRaceListViewTop])
            break
        default:
            NSLayoutConstraint.deactivate([portraitRaceInfoViewTrailing, portraitRaceInfoViewBottom, portraitRaceListViewLeading])
            NSLayoutConstraint.activate([landscapeRaceInfoViewTrailing, landscapeRaceInfoViewBottom, landscapeRaceListViewTop])
            break
        }
    }
    

    And lastly override viewWillTransition to apply the constraints when the orientation changes.

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        coordinator.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) -> Void in
            self.applyOrientationConstraints()
        }, completion: { (UIViewControllerTransitionCoordinatorContext) -> Void in
            print("rotation completed")
        })
        super.viewWillTransition(to: size, with: coordinator)
    }
    
    0 讨论(0)
  • 2020-12-28 15:03

    Is it possible to do that with auto layout? Or with a small amount of code?

    You will need both to do these layout for an iPad.

    1. Define your layout constraints for each view, do not set the width or hight constraints for these views.
    2. Wire up IBOutlets for each constraint on view one and two.
    3. Implement the UIContentContainer protocol in your view controller.

      viewWillTransitionToSize(_ size: CGSize,withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)

    Discussion UIKit calls this method before changing the size of a presented view controller’s view. You can override this method in your own objects and use it to perform additional tasks related to the size change. For example, a container view controller might use this method to override the traits of its embedded child view controllers. Use the provided coordinator object to animate any changes you make.

    If you override this method in your custom view controllers, always call super at some point in your implementation so that UIKit can forward the size change message appropriately. View controllers forward the size change message to their views and child view controllers. Presentation controllers forward the size change to their presented view controller.

    Is the method you need to implement. Inside this method you will need to inspect the size's properties width and height to work out how your layout should change - i.e. landscape or portrait layout. Note that this method tell's that it WILL change to the passed in size.

    1. Adjust your constraints based on if the device is going to rotate to portrait or landscape.
    0 讨论(0)
  • 2020-12-28 15:06

    it is much easier to put those two views in a stack view and change the axis orientation for the stackview.

    1. create a IBOulet reference to the stackview
    2. implement viewWillTransitionToSize
    3. change the axis for each orientation by doing self.stackView.axis = .vertical or .horizontal
    0 讨论(0)
  • 2020-12-28 15:09

    You can achieve this through code First of all you have to make IBoutlet of your dynamic constraints

    Constant Constraint: // these constraint will remain same in both orientations

    1- RedView top Space to Superview

    2- RedView Trailing Space to Superview

    3- BlueView Leading Space to Superview

    4- BlueView bottom Space to SuperView

    Dynamic Constraint

    Portrait Constraint:

    1- RedView height

    2- RedView Vertical Space to BlueView

    3- RedView Leading Space to Superview

    4- BlueView Trailing Space to Superview

    LandScape Constraint:

    1- RedView Width

    2- RedView Horizontal Space to BlueView

    3- RedView bottom Space to Superview

    4- BlueView Top Space to Superview

    Now You have to override method which is called on Orientation change

    override func viewWillTransitionToSize(size: CGSize,   withTransitionCoordinator coordinator:    UIViewControllerTransitionCoordinator) {
    
        coordinator.animateAlongsideTransition({ (UIViewControllerTransitionCoordinatorContext) -> Void in
    
            let orient = UIApplication.sharedApplication().statusBarOrientation
    
            switch orient {
            case .Portrait:
                print("Portrait")
                self.ApplyportraitConstraint()
                break
                // Do something
            default:
                print("LandScape")
                // Do something else
                self.applyLandScapeConstraint()
                break
            }
            }, completion: { (UIViewControllerTransitionCoordinatorContext) -> Void in
                print("rotation completed")
        })
        super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
    }
    

    And call these 2 functions

    Portrait Orientation Function

    func ApplyportraitConstraint(){
    
     self.view.addConstraint(self.RedViewHeight)
     self.view.addConstraint(self.RedView_VerticalSpace_To_BlueView)
     self.view.addConstraint(self.RedView_LeadingSpace_To_SuperView)
     self.view.addConstraint(self.BlueView_TrailingSpace_To_SuperView)
    
     self.view.removeConstraint(self.RedViewWidth)
     self.view.removeConstraint(self.RedView_HorizontalSpace_To_BlueView)
     self.view.removeConstraint(self.RedView_BottomSpace_To_SuperView)          
     self.view.removeConstraint(self.BlueView_TopSpace_To_SuperView)
    
    
    }
    

    LandScape Orientation Function

        func applyLandScapeConstraint(){
    
        self.view.removeConstraint(self.RedViewHeight)
        self.view.removeConstraint(self.RedView_VerticalSpace_To_BlueView)
        self.view.removeConstraint(self.RedView_LeadingSpace_To_SuperView)
       self.view.removeConstraint(self.BlueView_TrailingSpace_To_SuperView)
    
        self.view.addConstraint(self.RedViewWidth)
        self.view.addConstraint(self.RedView_HorizontalSpace_To_BlueView)
        self.view.addConstraint(self.RedView_BottomSpace_To_SuperView)
        self.view.addConstraint(self.BlueView_TopSpace_To_SuperView)
    
    }
    

    Portrait ScreenShot: LandScape ScreenShot:

    Hope it will Help to Understand it through Layout Managment through coding. If you still not able to Understand then Please Check my Code on

    Github:

    If you have warnings just set height and width's constraint priority to 999.

    0 讨论(0)
提交回复
热议问题