I had this exact problem, and I quickly found that there were a lot of bad advice around autorotation, especially because iOS 8 handles it differently from previous versions.
First of all, you do not want to apply counter-rotation manually or subscribe to changes in the orientation of UIDevice . Performing counter-rotation will still lead to unsightly animation, and the orientation of the device will not always be the same as the orientation of the interface. Ideally, you want the camera preview to remain truly frozen, and your app user interface to match the orientation and size of the status bar as they change, just like your own camera app.
During orientation changes in iOS 8, the window itself rotates, and does not contain the view (s) that it contains. You can add views from multiple view controllers to a single UIWindow , but only the rootViewController will be able to respond through shouldAutorotate() . Despite the fact that you make a decision about rotation at the level of the view controller, it actually rotates the parent window, thereby rotating all its subzones (including other view controllers).
The solution consists of two UIWindow , located one above the other, each of which (or not) has its own root view controller. Most applications have only one, but there is no reason why you cannot have two and impose them just like any other UIView subclass.
Here's a working proof of concept that I also added to GitHub here . Your particular case is a little more complicated because you have a stack of contained view controllers, but the basic idea is the same. I will 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 insertion on an interfaceWindow makes it slightly larger than the borders of the screen, effectively hiding the black rectangular mask that you saw otherwise. Usually you will not 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 expect here, just add your own customization 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 cancels the negative insertion that we applied to the window, which we achieve by shifting the view the same amount and processing the contentView as the main view.
For your application, interfaceWindow.rootViewController will be your tab bar controller, which in turn contains a navigation controller, etc. All of these views should be transparent when your camera controller appears so that the camera window appears below it. For performance reasons, you can think about leaving them opaque and only set everything to transparency when the camera is actually in use, and set the camera window to hidden when it was not (when you turn off the capture session).
Sorry to write a novel; I did not see this address elsewhere, and it took me a while to understand, I hope this helps you and everyone who is trying to achieve the same behavior. Even the Apple AVCam example application does not handle this completely correctly.
An example repo I posted also includes a version with an already configured camera. Good luck