Child View Controller to Rotate While Parent View Controller Does Not

前端 未结 3 1711
既然无缘
既然无缘 2020-12-12 21:21

What I am Trying to Do:

Parent View that is managed by Parent View Controller SHOULD NOT ROTATE.

Child View that i

相关标签:
3条回答
  • 2020-12-12 21:47

    From iOS developer library Technical Q&A

    Autorotation is implemented by applying a rotation transform to the application's window when the system determines that the interface must rotate. The window then adjusts its bounds for the new orientation and propagates this change down through the view controller hierarchy via calls to each view controller's and presentation controller's -viewWillTransitionToSize:withTransitionCoordinator: method. Invocations of this method are provided a transition coordinator object containing the delta of the window's current transform and new transform, which can be retrieved by sending a -targetTransform message to the transition coordinator. Your view controller or presentation controller can derive the appropriate inverse transform and apply it to the target view. This nullifies the effect of the window transform on that particular view and gives the appearance that the view has remained fixed in its current orientation as the rest of the interface rotates normally.

    i.e. if you have a view named noRotatingView

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
    
        noRotatingView.center = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds))
    }
    
    override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
    
        coordinator.animateAlongsideTransition({ (UIViewControllerTransitionCoordinatorContext) -> Void in
    
            let deltaTransform = coordinator.targetTransform()
            let deltaAngle = atan2f(Float(deltaTransform.b), Float(deltaTransform.a))
    
            var currentRotation = self.noRotatingView.layer.valueForKeyPath("transform.rotation.z")?.floatValue
    
            // Adding a small value to the rotation angle forces the animation to occur in a the desired direction, preventing an issue where the view would appear to rotate 2PI radians during a rotation from LandscapeRight -> LandscapeLeft.
            currentRotation = currentRotation! + (-1 * Float(deltaAngle)) + 0.0001
            self.noRotatingView.layer.setValue(currentRotation, forKeyPath:"transform.rotation.z")
    
            }) { (UIViewControllerTransitionCoordinatorContext) -> Void in
    
                var currentTransform = self.noRotatingView.transform;
                currentTransform.a = round(currentTransform.a);
                currentTransform.b = round(currentTransform.b);
                currentTransform.c = round(currentTransform.c);
                currentTransform.d = round(currentTransform.d);
                self.noRotatingView.transform = currentTransform;
        }
    }
    

    I've tried to make a demo project at https://github.com/rishi420/TestCameraRotation

    0 讨论(0)
  • 2020-12-12 21:53

    After many back and forth's with Apple them selves, and them pointing to the same link Technical Q&A QA1890, I had to do this with the flowing way:

    MotherViewController

    override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
    
            super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
    
            coordinator.animateAlongsideTransition({(context: UIViewControllerTransitionCoordinatorContext) -> Void in
                let deltaTransform: CGAffineTransform = coordinator.targetTransform()
                let deltaAngle: CGFloat = atan2(deltaTransform.b, deltaTransform.a)
                var currentRotation: CGFloat = self.mainView.layer.valueForKeyPath("transform.rotation.z") as! CGFloat
    
                // Adding a small value to the rotation angle forces the animation to occur in a the desired direction, preventing an issue where the view would appear to rotate 2PI radians during a rotation from LandscapeRight -> LandscapeLeft.
                currentRotation += -1 * deltaAngle + 0.0001
                self.mainView.layer.setValue(currentRotation, forKeyPath: "transform.rotation.z")
    
                }, completion: {(
                    context: UIViewControllerTransitionCoordinatorContext) -> Void in
                    // Integralize the transform to undo the extra 0.0001 added to the rotation angle.
                    var currentTransform: CGAffineTransform = self.mainView.transform
                    currentTransform.a = round(currentTransform.a)
                    currentTransform.b = round(currentTransform.b)
                    currentTransform.c = round(currentTransform.c)
                    currentTransform.d = round(currentTransform.d)
                    self.mainView.transform = currentTransform
            })
        }
    

    MainViewController

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "orientationChanged:", name:UIDeviceOrientationDidChangeNotification, object: nil)
    
    func orientationChanged ( notification: NSNotification){
            switch UIDevice.currentDevice().orientation {
            case .Portrait:
                self.rotateChildViewsForOrientation(0)
            case .PortraitUpsideDown:
                self.rotateChildViewsForOrientation(0)
            case .LandscapeLeft:
                self.rotateChildViewsForOrientation(90)
            case .LandscapeRight:
                self.rotateChildViewsForOrientation(-90)
            default:
                print ("Default")
                    }
        }
    

    This seem to rotate the child view's while keeping the main view static. Also the app seem to know the proper orientation when notifications come in (because mother view actually rotates behind the scenes).

    Special thanks to jamesk and Warif Akhand Rishi for all the help!

    0 讨论(0)
  • 2020-12-12 22:01

    The effect achieved in the Camera app is similar to using a combination of:

    • the technique described in Technical Q&A QA1890 for preventing a view from rotating; and
    • the use of child UIStackViews to rearrange subviews on device rotation (see, e.g., the SimpleExampleViewController class in the AdaptiveElements sample code shown in WWDC 2016 Session 233).

    The technical note explains that the underlying implementation of autorotation involves applying a transform directly to the application's window:

    Autorotation is implemented by applying a rotation transform to the application's window when the system determines that the interface must rotate. The window then adjusts its bounds for the new orientation and propagates this change down through the view controller hierarchy via calls to each view controller's and presentation controller's viewWillTransitionToSize:withTransitionCoordinator: method.

    Note that the Camera app does not opt out of autorotation: when using the Camera app, the orientations of Notification Center and Control Center reflect the device orientation. It is only particular views within the Camera app that appear to opt out of autorotation. In fact, this answer suggests that such apps typically utilise the videoGravity and videoOrientation properties provided by AVFoundation classes such as AVCaptureVideoPreviewLayer and AVCaptureConnection.

    The approach you should take depends on whether you only want to stop a parent view from rotating or whether you want to stop a container view controller such as UINavigationController from rotating (i.e. lock device orientation).

    If the former, use the technique described in the technical note to fix the orientation of the parent view, and (for iOS 9) wrap rotating subviews in a UIStackView and use the axis property to rearrange the subviews on device rotation, or (for iOS 8) manually rotate the subviews on device rotation (as to which see this answer and this sample code).

    If the latter, the approach you're taking is on the right track. For sample code, see this answer. You'll need to share your code and more information about your view controller hierarchy for us to diagnose quirks involving Notification Center.

    Addendum: The reason why calls to shouldAutorotate and supportedInterfaceOrientations were not being propagated to child view controllers was explained in Technical Q&A QA1688:

    Beginning with iOS 6, only the topmost view controller (alongside the UIApplication object) participates in deciding whether to rotate in response to a change of the device's orientation. More often than not, the view controller displaying your content is a child of UINavigationController, UITabBarController, or a custom container view controller. You may find yourself needing to subclass a container view controller in order to modify the values returned by its supportedInterfaceOrientations and shouldAutorotate methods.

    In Swift, you can override those methods by using extensions.

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