Swift - How to detect orientation changes

后端 未结 13 1765
余生分开走
余生分开走 2020-11-29 19:49

I want to add two images to single image view (i.e for landscape one image and for portrait another image)but i don\'t know how to detect orientation changes using swift lan

相关标签:
13条回答
  • 2020-11-29 20:29

    ⚠️Device Orientation != Interface Orientation⚠️

    Swift 5.* iOS14 and below

    You should really make a difference between:

    • Device Orientation => Indicates the orientation of the physical device
    • Interface Orientation => Indicates the orientation of the interface displayed on the screen

    There are many scenarios where those 2 values are mismatching such as:

    • When you lock your screen orientation
    • When you have your device flat

    In most cases you would want to use the interface orientation and you can get it via the window:

    private var windowInterfaceOrientation: UIInterfaceOrientation? {
        return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
    }
    

    In case you also want to support < iOS 13 (such as iOS 12) you would do the following:

    private var windowInterfaceOrientation: UIInterfaceOrientation? {
        if #available(iOS 13.0, *) {
            return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
        } else {
            return UIApplication.shared.statusBarOrientation
        }
    }
    

    Now you need to define where to react to the window interface orientation change. There are multiple ways to do that but the optimal solution is to do it within willTransition(to newCollection: UITraitCollection.

    This inherited UIViewController method which can be overridden will be trigger every time the interface orientation will be change. Consequently you can do all your modifications in the latter.

    Here is a solution example:

    class ViewController: UIViewController {
        override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
            super.willTransition(to: newCollection, with: coordinator)
            
            coordinator.animate(alongsideTransition: { (context) in
                guard let windowInterfaceOrientation = self.windowInterfaceOrientation else { return }
                
                if windowInterfaceOrientation.isLandscape {
                    // activate landscape changes
                } else {
                    // activate portrait changes
                }
            })
        }
        
        private var windowInterfaceOrientation: UIInterfaceOrientation? {
            return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
        }
    }
    

    By implementing this method you'll then be able to react to any change of orientation to your interface. But keep in mind that it won't be triggered at the opening of the app so you will also have to manually update your interface in viewWillAppear().

    I've created a sample project which underlines the difference between device orientation and interface orientation. Additionally it will help you to understand the different behavior depending on which lifecycle step you decide to update your UI.

    Feel free to clone and run the following repository: https://github.com/wjosset/ReactToOrientation

    0 讨论(0)
  • 2020-11-29 20:32

    I believe the correct answer is actually a combination of both approaches: viewWIllTransition(toSize:) and NotificationCenter's UIDeviceOrientationDidChange.

    viewWillTransition(toSize:) notifies you before the transition.

    NotificationCenter UIDeviceOrientationDidChange notifies you after.

    You have to be very careful. For example, in UISplitViewController when the device rotates into certain orientations, the DetailViewController gets popped off the UISplitViewController's viewcontrollers array, and pushed onto the master's UINavigationController. If you go searching for the detail view controller before the rotation has finished, it may not exist and crash.

    0 讨论(0)
  • 2020-11-29 20:34

    If your are using Swift version >= 3.0 there are some code updates you have to apply as others have already said. Just don't forget to call super:

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    
       super.viewWillTransition(to: size, with: coordinator)
    
       // YOUR CODE OR FUNCTIONS CALL HERE
    
    }
    

    If you are thinking to use a StackView for your images be aware you can do something like the following:

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    
       super.viewWillTransition(to: size, with: coordinator)
    
       if UIDevice.current.orientation.isLandscape {
    
          stackView.axis = .horizontal
    
       } else {
    
          stackView.axis = .vertical
    
       } // else
    
    }
    

    If your are using Interface Builder don't forget to select the custom class for this UIStackView object, in the Identity Inspector section at the right panel. Then just create (also through Interface Builder) the IBOutlet reference to the custom UIStackView instance:

    @IBOutlet weak var stackView: MyStackView!
    

    Take the idea and adapt it to your needs. Hope this can help you!

    0 讨论(0)
  • 2020-11-29 20:36

    Using NotificationCenter and UIDevice's beginGeneratingDeviceOrientationNotifications

    Swift 4.2+

    override func viewDidLoad() {
        super.viewDidLoad()        
    
        NotificationCenter.default.addObserver(self, selector: #selector(ViewController.rotated), name: UIDevice.orientationDidChangeNotification, object: nil)
    }
    
    deinit {
       NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil)         
    }
    
    func rotated() {
        if UIDevice.current.orientation.isLandscape {
            print("Landscape")
        } else {
            print("Portrait")
        }
    }
    

    Swift 3

    override func viewDidLoad() {
        super.viewDidLoad()        
    
        NotificationCenter.default.addObserver(self, selector: #selector(ViewController.rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
    }
    
    deinit {
         NotificationCenter.default.removeObserver(self)
    }
    
    func rotated() {
        if UIDevice.current.orientation.isLandscape {
            print("Landscape")
        } else {
            print("Portrait")
        }
    }
    
    0 讨论(0)
  • 2020-11-29 20:39

    Another way to detect device orientations is with the function traitCollectionDidChange(_:). The system calls this method when the iOS interface environment changes.

    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)
    {
        super.traitCollectionDidChange(previousTraitCollection)
        //...
    }
    

    Furthermore, you can use function willTransition(to:with:) ( which is called before traitCollectionDidChange(_:) ), to get information just before the orientation is applied.

     override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator)
    {
        super.willTransition(to: newCollection, with: coordinator)
        //...
    }
    
    0 讨论(0)
  • 2020-11-29 20:41
    let const = "Background" //image name
    let const2 = "GreyBackground" // image name
        @IBOutlet weak var imageView: UIImageView!
        override func viewDidLoad() {
            super.viewDidLoad()
    
            imageView.image = UIImage(named: const)
            // Do any additional setup after loading the view.
        }
    
        override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
            super.viewWillTransition(to: size, with: coordinator)
            if UIDevice.current.orientation.isLandscape {
                print("Landscape")
                imageView.image = UIImage(named: const2)
            } else {
                print("Portrait")
                imageView.image = UIImage(named: const)
            }
        }
    
    0 讨论(0)
提交回复
热议问题