Search code examples
swiftxcodeuiviewcontrollerlandscape-portraitchildviewcontroller

Child View Controller to Rotate While Parent View Controller Does Not


What I am Trying to Do:

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

Child View that is managed by Child View Controller SHOULD ROTATE to all orientations.


enter image description here

enter image description here


What I Have Tried:

ParentViewController

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return .Portrait
}

override func shouldAutorotate() -> Bool {
    return true
}

fun addChildViewController() {
    let storyBoard = UIStoryboard(name: "Main", bundle: nil)
    self.childViewController = storyBoard("Child View Controller") as? ChildViewController
    self .addChildViewController(self.childViewController!)
    self.childViewController! .didMoveToParentViewController(self)
    self.view .addSubview(self.childViewController!.view)
    self.childViewController!.view.frame = CGRectMake (40, 200, 400, 250)
}

ChildViewController

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return  .All
}

override func shouldAutorotate() -> Bool {
    return true
}

Supported orientations in Xcode Deployment Info are set to all four.

What I Get:

None of the View's rotate. If I set the parent's rotation to all, all views rotate together. So it's all or nothing.


UPDATE

When I try putting an observer for UIDeviceOrientationDidChangeNotification and use UIDevice.currentDevice().orientation to rotate childView using CGAffaineTransformMakeRotate , I get desired results. HOWEVER, i.e., if I rotate to landscape, and try to pull down the notification center (or if I get a system notification), which is still in the portrait orientation (because the app is natively still left in Portrait), rotated childView rotates back to portrait to honor the status bar/notification/notification center.

Stock iOS Camera App is the best example I can present. The main canvas does not rotate in to different orientations, yet the status bar, behind the scenes, rotates honoring device rotation. Also the subviews rotate within them selves to honor different orientations. I am trying to achieve this behavior....


Solution

  • 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!