Child View Controller to Rotate While Parent View Controller Does Not

Child View Controller to Rotate While Parent View Controller Does Not

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!

Parent View Controller doesn't rotate while Child View Controller is presented

From Apple's docs:

If a view controller is not visible when an orientation change occurs, then the rotation methods are never called. However, the viewWillLayoutSubviews method is called when the view becomes visible. Your implementation of this method can call the statusBarOrientation method to determine the device orientation.

I added a BOOL to track when it is not the top viewController, then:

- (void)viewWillLayoutSubviews {

if (notTopController) {

[self willRotateToInterfaceOrientation:[[UIApplication sharedApplication] statusBarOrientation] duration:0];
[self willAnimateRotationToInterfaceOrientation:[[UIApplication sharedApplication] statusBarOrientation] duration:0];

notTopController = NO;

}

Works good for me so far.

how to rotate parent view controller when child view controller orientation changes in iOS

For < iOS 6.0

in childView1

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {

[[NSNotificationCenter defaultCenter] postNotificationName:@"RotateParent"object:nil];
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||interfaceOrientation == UIInterfaceOrientationLandscapeRight );

}

For > iOS 6.0

 - (BOOL)shouldAutorotate
{
[[NSNotificationCenter defaultCenter] postNotificationName:@"RotateParent" object:nil];
return YES;
}

in Parent View

Add observer for notification, and based on current orientation rotate parent view with proper angle.

-(void)rotateParent:(NSNotification *)note{

UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
CGFloat rotationAngle = 0;

if (orientation == UIDeviceOrientationPortraitUpsideDown) rotationAngle = M_PI;
else if (orientation == UIDeviceOrientationLandscapeLeft) rotationAngle = M_PI_2;
else if (orientation == UIDeviceOrientationLandscapeRight) rotationAngle = -M_PI_2;

[UIView animateWithDuration:0.5 animations:^{
self.view.transform = CGAffineTransformMakeRotation(rotationAngle);
self.view.transform = CGAffineTransformMakeRotation(rotationAngle);
self.view.transform = CGAffineTransformMakeRotation(rotationAngle);
} completion:nil];

//adjust view frame based on screen size

if(orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight)
{
self.view.bounds = CGRectMake(0.0, 0.0, 480, 320);
}
else
{
self.view.bounds = CGRectMake(0.0, 0.0, 320, 480);
}
}

let me know if you face any issue.

how to avoid parent view controller rotating

When You back from child view controller you should call
Create a method in your child view controller and implement this

- (void)setScreenOrientation:(UIInterfaceOrientation)orientation
{
UIDeviceOrientation deviceOrientation = (UIDeviceOrientation) orientation;
[[UIDevice currentDevice] setValue: [NSNumber numberWithInteger: deviceOrientation] forKey:@"orientation"];
}

Call this from child view before dismiss:

    [self setScreenOrientation:UIInterfaceOrientationPortrait];

[self dismissViewControllerAnimated:YES completion:nil];

Force iOS view to not rotate, while still allowing child to rotate

I had this exact problem, and found out quickly there's a lot of bad advice floating around about autorotation, especially because iOS 8 handles it differently than previous versions.

First of all, you don't want to apply a counterrotation manually or subscribe to UIDevice orientation changes. Doing a counterrotation will still result in an unsightly animation, and device orientation isn't always the same as interface orientation. Ideally you want the camera preview to stay truly frozen, and your app UI to match the status bar orientation and size as they change, exactly like the native Camera app.

During an orientation change in iOS 8, the window itself rotates rather than the view(s) it contains. You can add the views of multiple view controllers to a single UIWindow, but only the rootViewController will get an opportunity to respond via shouldAutorotate(). Even though you make the rotation decision at the view controller level, it's the parent window that actually rotates, thus rotating all of its subviews (including ones from other view controllers).

The solution is two UIWindow stacked on top of each other, each rotating (or not) with its own root view controller. Most apps only have one, but there's no reason you can't have two and overlay them just like any other UIView subclass.

Here's a working proof-of-concept, which I've also put on GitHub here. Your particular case is a little more complicated because you have a stack of containing view controllers, but the basic idea is the same. I'll touch on some specific points below.

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var cameraWindow: UIWindow!
var interfaceWindow: UIWindow!

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
let screenBounds = UIScreen.mainScreen().bounds
let inset: CGFloat = fabs(screenBounds.width - screenBounds.height)

cameraWindow = UIWindow(frame: screenBounds)
cameraWindow.rootViewController = CameraViewController()
cameraWindow.backgroundColor = UIColor.blackColor()
cameraWindow.hidden = false

interfaceWindow = UIWindow(frame: CGRectInset(screenBounds, -inset, -inset))
interfaceWindow.rootViewController = InterfaceViewController()
interfaceWindow.backgroundColor = UIColor.clearColor()
interfaceWindow.opaque = false
interfaceWindow.makeKeyAndVisible()

return true
}
}

Setting a negative inset on interfaceWindow makes it slightly larger than the screen bounds, effectively hiding the black rectangular mask you'd see otherwise. Normally you wouldn't notice because the mask rotates with the window, but since the camera window is fixed the mask becomes visible in the corners during rotation.

class CameraViewController: UIViewController {
override func shouldAutorotate() -> Bool {
return false
}
}

Exactly what you'd expect here, just add your own setup for AVCapturePreviewLayer.

class InterfaceViewController: UIViewController {
var contentView: UIView!

override func viewDidLoad() {
super.viewDidLoad()

contentView = UIView(frame: CGRectZero)
contentView.backgroundColor = UIColor.clearColor()
contentView.opaque = false

view.backgroundColor = UIColor.clearColor()
view.opaque = false
view.addSubview(contentView)
}

override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()

let screenBounds = UIScreen.mainScreen().bounds
let offset: CGFloat = fabs(screenBounds.width - screenBounds.height)

view.frame = CGRectOffset(view.bounds, offset, offset)
contentView.frame = view.bounds
}

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

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

The last trick is undoing the negative inset we applied to the window, which we achieve by offsetting view the same amount and treating contentView as the main view.

For your app, interfaceWindow.rootViewController would be your tab bar controller, which in turn contains a navigation controller, etc. All of these views need to be transparent when your camera controller appears so the camera window can show through beneath it. For performance reasons you might consider leaving them opaque and only setting everything to transparent when the camera is actually in use, and set the camera window to hidden when it's not (while also shutting down the capture session).

Sorry to post a novel; I haven't seen this addressed anywhere else and it took me a while to figure out, hopefully it helps you and anyone else who's trying to get the same behavior. Even Apple's AVCam sample app doesn't handle it quite right.

The example repo I posted also includes a version with the camera already set up. Good luck!

Why are my constraints not working when rotating a modally presented child view controller?

Okay. I finally found a solution:
- First I noticed that one of my presented view controllers was working as expected (auto-rotation would resize the views and auto-layout would do its job).
- Looking at the differences, I noticed that the only significant difference was the presentation style: the working VC was being presented with the UIModalPresentationFullScreen style.
- I tried changing the presentation style to full screen in my other case and, sure enough, auto layout worked as expected, only problem, the presenting view controller no longer appeared behind the transparent parts of my model VC.
- Playing with the different presentation styles I found that the style that does what I want is UIModalPresentationOverFullScreen. It's the same as full screen but shows the presenting view controller behind the presented one!

I'm still not sure why this works (couldn't find a shred of documentation describing this particular issue, if anyone does know, please share!), but what I'm baffled about is that this presentation style (UIModalPresentationOverFullScreen) was created in iOS 8.0, meaning that I just don't know what I would have done to make this work in iOS < 8.0...

UINavigationController doesn't rotate when presenting child view controller

Calling UIViewController.attemptRotationToDeviceOrientation() in the viewWillAppear of the 2nd view controller did the trick.

Autorotate parent view to portrait when child view is dismissed ios6

type these two methods in your login ViewController

(BOOL)shouldAutorotate { return YES;
}

(NSUInteger)supportedInterfaceOrientations {
return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown); }

and

(BOOL)shouldAutorotate { return YES;
}

(NSUInteger)supportedInterfaceOrientations {
return (UIInterfaceOrientationLandscapeRight | UIInterfaceOrientationLandscapeLeft); }

in your childcontroller

required to add category autorotation subclassed navigation controller which would call topcontroller shouldautorotate and supported interface orientation.

#import "UINavigationController+Autorotation.h"

@implementation UINavigationController (Autorotation)
-(BOOL)shouldAutorotate
{
for (UIViewController * viewController in self.viewControllers) {
if (![viewController isEqual:self.topViewController]) {
[viewController shouldAutorotate];
}
}

return [self.topViewController shouldAutorotate];

}

-(NSUInteger)supportedInterfaceOrientations
{
for (UIViewController * viewController in self.viewControllers) {
if (![viewController isEqual:self.topViewController]) {
[viewController supportedInterfaceOrientations];
}
}

return [self.topViewController supportedInterfaceOrientations];

}


Related Topics



Leave a reply



Submit