Disabling auto rotation for a UIView
Asked Answered
M

5

21

My application is composed of a toolbar and an AVCaptureVideoPreviewLayer in a background UIView. I'd like to see that toolbar rotate regarding the device orientation, so in my main ViewController, I implemented the function :

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationPortrait
            || interfaceOrientation == UIInterfaceOrientationLandscapeLeft
            || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}

This works fine, but when the interface orientation is changed, I see that the background UIView (with a video layer) is also rotating, so my video view is now playing 90 degrees left or right... Which is not really cool !
So my question is : is there any way to disable the auto-rotation animation on a specific UIView ?

I have seen one similar question to this one : Disabling autorotate for a single UIView, but the answers doesn't fit my problem, I really need the background view to do not move, not to get around the problem with a kind of "counter animation". So I decided to bring up this topic.

Any ideas ?

Thanks !

Momentary answered 21/7, 2010 at 16:5 Comment(1)
I have the same problem. Did you solve it? does it works with ios 6? Could you put a complete answer? Thanks a lot.Edmondo
I
-2

Try this:

myVideoCaptureLayer.orientationSupported = NO;
Invariant answered 21/7, 2010 at 17:27 Comment(1)
As I understood, the orientationSupported property is readonly and tells if the device is able to change video orientation. If it is true, then one can change the "orientation" property to the correct value (AVCaptureVideoOrientationPortrait, AVCaptureVideoOrientationLandscapeLeft, ...). Thanks for putting me in the good direction !Momentary
M
11

This post pointed in the right direction. To get an AVCaptureVideoPreviewLayer to not rotate with the device I wrapped it in a UIView and animated that view in the opposite direction:

- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {

    float rotation;

    if (toInterfaceOrientation==UIInterfaceOrientationPortrait) {
        rotation = 0;
    } else
    if (toInterfaceOrientation==UIInterfaceOrientationLandscapeLeft) {
        rotation = M_PI/2;
    } else
    if (toInterfaceOrientation==UIInterfaceOrientationLandscapeRight) {
        rotation = -M_PI/2;
    }

    [UIView animateWithDuration:duration animations:^{
        cameraPreview.transform = CGAffineTransformMakeRotation(rotation);
        cameraPreview.frame = self.view.frame;
    }];
}

It seems a little roundabout but it animates smoothly. Other views on top of the preview layer automatically rotate as they should.

Mauchi answered 2/9, 2011 at 13:24 Comment(4)
I changed the frame code a little: CGRect f = cameraPreview.frame; cameraPreview.transform = CGAffineTransformMakeRotation(rotation); cameraPreview.frame = f;Muzzleloader
Hi, Thanks for that, will not self.view.frame return the rotated view frame? so if it's landscape, it will return landscape frame, if portrait, so portrait frame, etc. Please correct me if I am wrong.Rhine
@Dimitri Given some time, this whole thing seems very fishy. Was this really the best way to deal with whatever problem I had? I sincerely doubt it. In iOS 8 the willAnimateRotationTo... sort of methods no longer exist. Try doing a similar thing within layoutSubviews of the super view. No need to add an animation block as layoutSubviews will already be invoked within an animation during screen rotation. The interface orientation should be accessible through [UIApplication sharedApplication].statusBarOrientation, but make sure the value matches your expectations during the layout.Mauchi
There's also a very strange thing here that @Rhine might be getting at. Assuming cameraPreview is a subview of self.view, cameraPreview.frame = self.view.frame; should probably be cameraPreview.frame = self.view.bounds;. The desired behavior probably happened accidentally because the view was filling the entire screen. Perhaps the width and height of the frame should also be swapped for landscape? Setting the frame of a transformed view is not recommended anyway, so I'm not sure I fully understand why that may have worked.Mauchi
L
2
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
  return NO;
}

Or were you saying to disable it only when a certain view is showing? If so, you can check for this using UIView's isDescendantOfView method.

Large answered 21/7, 2010 at 16:6 Comment(2)
Well, I don't want to disable the whole interface rotation, just to disable it on a specific UIView in the view hierarchy of the main viewController. It means when I rotate the device, I see the toolbar rotating, but not the video view.Momentary
This is not a method on UIView - it's on UIViewController. So it would disable the rotation for the entire view hierarchy.Muzzleloader
E
2

I finally found a sane answer for this. No more messing around with "anti-rotation animations" and such!

The key is to use a UIViewController for your preview layer (the non-rotating view) and another for the actual interface. Configure the rotation options for both using shouldAutorotateToInterfaceOrientation: (the code is in the answers above):

  • The preview view controller should be set to not rotate
  • The interface view controller should be set to rotate to the orientations you need

The important bit is to keep your views separate by adding them directly to the window. I do this in the AppDelegate:

[self.window addSubview:previewViewController.view];
[self.window addSubview:interfaceViewController.view];
self.window.rootViewController = interfaceViewController;
[self.window makeKeyAndVisible];

That puts your video preview in the background, and then adds the interface view over it. The interface is free to autorotate without affecting the preview at all.

One important thing to note: the background of your interface view should be transparent, otherwise it blocks the preview.

Erinn answered 26/2, 2013 at 16:28 Comment(1)
This approach works. I ended up doing something a bit different though, which is having a "background" UIWindow for the preview layer, and another "main" UIWindow with the rest of the UI (and with a transparent background, so that the preview is visible).Montgomery
A
1

I ran into the same issue, though simply setting the orientation property on AVCaptureVideoPreviewLayer was not sufficient. The additional problem is that CALayer does not support layout managers on the iPhone, so the frame of the video layer may be incorrect when the device is rotated.

I fixed this by using a custom UIView to contain the AVCaptureVideoPreviewLayer and then I overrode the layoutSubviews method for this view to ensure that the video layer's frame is always set correctly. This is also a good place to set the orientation property. That is, my UIView looks like:

@implementation VideoPreview

@synthesize previewLayer;

- (void) layoutSubviews {
    if (previewLayer) {
        // set the correct orientation for the video layer
        previewLayer.orientation = [[UIDevice currentDevice] orientation];

        // and set its frame to that of its parent UIView
        previewLayer.frame = self.bounds;
    }
}

- (void) addPreviewLayer: (AVCaptureVideoPreviewLayer *) videoLayer {
    previewLayer = videoLayer;
    [self.layer addSublayer:videoLayer];
}

@end
Apprehensive answered 22/4, 2011 at 11:19 Comment(0)
I
-2

Try this:

myVideoCaptureLayer.orientationSupported = NO;
Invariant answered 21/7, 2010 at 17:27 Comment(1)
As I understood, the orientationSupported property is readonly and tells if the device is able to change video orientation. If it is true, then one can change the "orientation" property to the correct value (AVCaptureVideoOrientationPortrait, AVCaptureVideoOrientationLandscapeLeft, ...). Thanks for putting me in the good direction !Momentary

© 2022 - 2024 — McMap. All rights reserved.