2

I am trying to make a view like the top and bottom bars on the iPhone Camera app. I can't get the top view and bottom view to stay in portrait.

When i use - (BOOL)shouldAutorotate, and predictably the rotation notifications stop. I have tried updating constraints with setNeedsUpdateConstraints but I still get the animation effect. I want the views to be locked and just the UIButtons to rotate.

How do you get just the buttons in a view to rotate and everything else to remain locked?

Siriss
  • 3,737
  • 4
  • 32
  • 65

2 Answers2

2

I have a UIViewController that only rotates some of it subviews when the device is rotated. (This works fine under iOS7 but breaks under iOS8.) You need to use CGAffineTransform to "hand rotate" your views.

Here's some code:

@interface VVViewController ()
@property (weak, nonatomic) IBOutlet UIView *pinnedControls;
@property (nonatomic, strong) NSMutableArray *pinnedViews;

@end

@implementation VVViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.pinnedViews = [NSMutableArray array];
    [self.pinnedViews addObject:self.pinnedControls];
}

-(void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];

    [UIViewController rotatePinnedViews:self.pinnedViews forOrientation:self.interfaceOrientation];
}

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];

    if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation) && UIInterfaceOrientationIsLandscape(self.interfaceOrientation))  {
        [UIViewController rotatePinnedViews:self.pinnedViews forOrientation:toInterfaceOrientation];
    }
}

@end

We've made a category on UIViewController to handle this behavior. Here's the pertinent code:

@implementation UIViewController (VVSupport)

+ (void)rotatePinnedViews:(NSArray *)views forOrientation:(UIInterfaceOrientation)orientation {
    const CGAffineTransform t1 = [UIViewController pinnedViewTansformForOrientation:orientation counter:YES];
    const CGAffineTransform t2 = [UIViewController pinnedViewTansformForOrientation:orientation counter:NO];
    [views enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) {
        // Rotate the view controller
        view.transform = t1;
        [view.subviews enumerateObjectsUsingBlock:^(UIView *counterView, NSUInteger idx, BOOL *stop) {
            // Counter-rotate the controlsUIin the view controller
            counterView.transform = t2;
        }];
    }];
}

+ (CGAffineTransform)pinnedViewTansformForOrientation:(UIInterfaceOrientation)orientation counter:(BOOL)counter {
    CGAffineTransform t;
    switch ( orientation ) {
        case UIInterfaceOrientationPortrait:
        case UIInterfaceOrientationPortraitUpsideDown:
            t = CGAffineTransformIdentity;
            break;

        case UIInterfaceOrientationLandscapeLeft:
            t = CGAffineTransformMakeRotation(counter ? M_PI_2 : -M_PI_2);
            break;

        case UIInterfaceOrientationLandscapeRight:
            t = CGAffineTransformMakeRotation(counter ? -M_PI_2 : M_PI_2);
            break;
    }

    return t;
}

@end

Now, this doesn't work perfectly under iOS8, See UIView not resizing when rotated with a CGAffineTransform under iOS8 for my question.

Community
  • 1
  • 1
Paul Cezanne
  • 8,629
  • 7
  • 59
  • 90
  • Thank you! This works for the first few rotates, and then the view started getting moved around. What would cause it to stop working? I can't figure it out – Siriss Sep 28 '14 at 17:57
  • Dunno, take care note of your device motions and walk through the debugger while doing so. See where it goes wrong. – Paul Cezanne Sep 29 '14 at 11:13
1

In Swift 5, and using the transform property to manually rotate.

override func viewDidLoad() {
    super.viewDidLoad()
    UIDevice.current.beginGeneratingDeviceOrientationNotifications()
    NotificationCenter.default.addObserver(self, selector: #selector(deviceOrientationDidChange),
        name: UIDevice.orientationDidChangeNotification, object: nil)
}

@objc private func deviceOrientationDidChange() {
    let viewsToRotate = [button1, button2, labelXX, viewXX]
    let rotation = UIDevice.current.orientation.rotationTransform
    viewsToRotate.forEach {
        $0.transform = rotation
    }
}

extension UIDeviceOrientation {
    var rotationTransform: CGAffineTransform {
        switch self {
        case .landscapeLeft: return CGAffineTransform(rotationAngle: .pi/2)
        case .landscapeRight: return CGAffineTransform(rotationAngle: -.pi/2)
        case .portraitUpsideDown: return CGAffineTransform(rotationAngle: .pi)
        default: return .identity
        }
    }
}

I have the details in my blog post here: https://samwize.com/2019/08/06/how-to-rotate-selected-views-like-camera-app/

samwize
  • 25,675
  • 15
  • 141
  • 186