5

TL;DR

Need to keep autorotation, but exclude one UIView from autorotating on orientation change, how?

Back story

I need to keep a UIView stationary during the animation accompanied by autorotation (which happens on orientation change). Similar to how the iOS camera app handles the rotation (i.e controls rotate in their place).

Things I've tried

  • Returning false from shouldAutorotate(), subscribing to UIDeviceOrientationDidChangeNotification, and trying to manually handle the rotation event for each view separately.

    Works well if you don't need to change any of your UIViews' places, otherwise it's a pain figuring out where it should end up and how to get it there

  • Placing a non rotating UIWindow under the main UIWindow, and setting the main UIWindow background colour to clear.

    This works well if it's only one item, but I don't want to manage a bunch of UIWindows

  • Inverse rotation I.e rotating the UIView in the opposite direction to the rotation. Not reliable, and looks weird, it's also vertigo inducing

  • Overriding the animation in the viewWillTransitionToSize method. Failed

  • And a bunch of other things that would be difficult to list here, but they all failed.

Question

Can this be done? if so, how?

I'm supporting iOS8+


Update This is how the views should layout/orient given @Casey's example:

enter image description here

Community
  • 1
  • 1
MrHaze
  • 3,786
  • 3
  • 26
  • 47
  • 1
    How about hooking into the `orientationDidChangeNotificationReceived:` notification and rotating the UIView in the opposite direction to effectively negate the device rotation? – Michael Feb 04 '16 at 03:02
  • That's noticeably janky, the view would jerk around. I've tried that as well (will update answer now). Good suggestion though. – MrHaze Feb 04 '16 at 03:04
  • 1
    The only other suggestion I can think of would be to try a UIContainerViewController where one of the contained ViewControllers contained the view that you didn't want to rotate. And then that ViewController would return false for shouldAutorotate(). No idea if it would work, and it still sounds a bit "janky". – Michael Feb 04 '16 at 03:09
  • This (http://stackoverflow.com/questions/7353789/ios-disable-autorotation-for-a-subview) suggests an option to add a ViewController as a subview to do it, but a comment says it doesn't work with/after iOS 8. – Michael Feb 04 '16 at 03:11
  • I've attempted that as well, the comment is right. This is also another example that works on < iOS 8 https://gist.github.com/ffraenz/5945301 I didn't try it, but I trust the comment. – MrHaze Feb 04 '16 at 03:14

3 Answers3

2

I have faced with same problem and found example from Apple, which helps to prevent UIView from rotation: https://developer.apple.com/library/ios/qa/qa1890/_index.html

However, if UIView is not placed in the center of the screen, you should handle new position manually.

Ponf
  • 1,190
  • 1
  • 12
  • 28
0

i think part of the reason this is so hard to answer is because in practice it doesn't really make sense.

say i make a view that uses autolayout to look like this in portrait and landscape:

standard layout

if you wanted to prevent c from rotating like you are asking, what would you expect the final view to look like? would it be one of these 3 options?

wonky layout

without graphics of the portrait/landscape view you are trying to achieve and a description of the animation you are hoping for it'll be very hard to answer your question.

are you using NSLayoutConstraint, storyboard or frame based math to layout your views? any code you can provide would be great too

Casey
  • 6,531
  • 24
  • 43
  • https://gist.github.com/justAnotherDev/51917652d9f18e533535 is the code used to produce the sample view – Casey Feb 04 '16 at 05:22
  • It wouldn't make sense if you put it like that, but there are cases when you want to apply what I'm trying to achieve. Take for example some view(s) that rotate normally, and they make up the background, and you wanted to keep the item in the foreground static and rotate instead of moving. An example would be the iOS Camera app, if you rotate the device, the controls stay in place, and rotate instead of move. – MrHaze Feb 07 '16 at 07:24
  • You're making a mountain out a molehill here— what the OP needs is none of the options you've provided.  **Instead, the OP wants this: http://imgur.com/a/Dk7hO .**  Notice that subviews A & B have autorotated as expected, but subview C hasn't— _it has the exact same orientation relative to the home button, rendering using the exact same pixels on the physical display_ (except those now obscured).  Your example is also obtuse in that there's really no use-case for it _except_ when subview C is full-screen-sized. – Slipp D. Thompson Sep 21 '16 at 03:23
0

If you're wanting to have the same effect as the camera app, use size classes (see here and here).

If not, what is wrong with creating a UIWindow containing a view controller that doesn't rotate? The following code seems to work for me (where the UILabel represents the view you don't want to rotate).

class ViewController: UIViewController {
    var staticWindow: UIWindow!

    override func viewDidLoad() {
        super.viewDidLoad()
        showWindow()
    }

    func showWindow() {
        let frame = CGRect(x: 10, y: 10, width: 100, height: 100)
        let vc = MyViewController()
        let label = UILabel(frame: frame)
        label.text = "Hi there"
        vc.view.addSubview(label)
        staticWindow = UIWindow(frame: frame)
        staticWindow.rootViewController = MyViewController()
        staticWindow.windowLevel = UIWindowLevelAlert + 1;
        staticWindow.makeKeyAndVisible()
        staticWindow.rootViewController?.presentViewController(vc, animated: false, completion: nil)
    }
}

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

    override func shouldAutomaticallyForwardRotationMethods() -> Bool {
        return false
    }

    override func shouldAutomaticallyForwardAppearanceMethods() -> Bool {
        return false
    }

    override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return UIInterfaceOrientationMask.Portrait
    }
}
Michael
  • 8,891
  • 3
  • 29
  • 42
  • Good answer, however I did mention that I've tried that, the problem is you'll have to maintain a bunch of UIWindows for different screens. – MrHaze Feb 07 '16 at 07:22
  • Are you saying you have one UIView that should not be rotated, but that it's on multiple view controllers? Is it a logo or something? What is the actual use case? – Michael Feb 07 '16 at 22:43
  • It's a bunch of views, the logo is one of the items, but not the only one. There is simply a video layer, and a bunch of controls on top of that. the layer should be persisted throughout the flow, and only some of the controls rotate in place. An example I've given before, but I'll mention again is the Camera app on iOS, only some of the views rotate, others just act normally. – MrHaze Feb 14 '16 at 22:24
  • Although all of the views on the camera app rotate in place, it's just the words that don't reposition. If it was rewritten today, I imagine they would use size classes, which they didn't have back then. – Michael Feb 15 '16 at 10:04