iOS 横竖屏旋转

2,151 阅读2分钟

1. 屏幕旋转相关知识

UIDeviceOrientation: 设备方向

typedef NS_ENUM(NSInteger, UIDeviceOrientation) {
    UIDeviceOrientationUnknown,
    UIDeviceOrientationPortrait,            // Device oriented vertically, home button on the bottom
    UIDeviceOrientationPortraitUpsideDown,  // Device oriented vertically, home button on the top
    UIDeviceOrientationLandscapeLeft,       // Device oriented horizontally, home button on the right
    UIDeviceOrientationLandscapeRight,      // Device oriented horizontally, home button on the left
    UIDeviceOrientationFaceUp,              // Device oriented flat, face up
    UIDeviceOrientationFaceDown             // Device oriented flat, face down
} __TVOS_PROHIBITED;

UIInterfaceOrientation: 界面方向

// Note that UIInterfaceOrientationLandscapeLeft is equal to UIDeviceOrientationLandscapeRight (and vice versa).
// This is because rotating the device to the left requires rotating the content to the right.
typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
    UIInterfaceOrientationUnknown            = UIDeviceOrientationUnknown,
    UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,
    UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
} __TVOS_PROHIBITED;

UIInterfaceOrientationMask: 是用来控制允许转向的方向

typedef NS_OPTIONS(NSUInteger, UIInterfaceOrientationMask) {
    UIInterfaceOrientationMaskPortrait = (1 << UIInterfaceOrientationPortrait),
    UIInterfaceOrientationMaskLandscapeLeft = (1 << UIInterfaceOrientationLandscapeLeft),
    UIInterfaceOrientationMaskLandscapeRight = (1 << UIInterfaceOrientationLandscapeRight),
    UIInterfaceOrientationMaskPortraitUpsideDown = (1 << UIInterfaceOrientationPortraitUpsideDown),
    UIInterfaceOrientationMaskLandscape = (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
    UIInterfaceOrientationMaskAll = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortraitUpsideDown),
    UIInterfaceOrientationMaskAllButUpsideDown = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
} __TVOS_PROHIBITED;

2. 观察屏幕旋转

if (![UIDevice currentDevice].isGeneratingDeviceOrientationNotifications) {
   [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
}

// 锁定竖屏仍然有效
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];

// Handle device orientation changes
- (void)deviceOrientationDidChange: (NSNotification *)notification
{
    // If dialog is attached to the parent view, it probably wants to handle the orientation change itself
    if (parentView != NULL) {
        return;
    }
    
    if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1) {
        [self changeOrientationForIOS7];
    } else {
        [self changeOrientationForIOS8:notification];
    }
}

// Rotation changed, on iOS8
- (void)changeOrientationForIOS8: (NSNotification *)notification {
    
    CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
    CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
    
    [UIView animateWithDuration:0.2f delay:0.0 options:UIViewAnimationOptionTransitionNone
                     animations:^{
                         CGSize dialogSize = [self countDialogSize];
                         CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
                         self.frame = CGRectMake(0, 0, screenWidth, screenHeight);
                         dialogView.frame = CGRectMake((screenWidth - dialogSize.width) / 2, (screenHeight - keyboardSize.height - dialogSize.height) / 2, dialogSize.width, dialogSize.height);
                     }
                     completion:nil
     ];
}
- (void)dealloc
{
    [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
    
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
}

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarOrientationChange:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];

- (void)statusBarOrientationChange:(NSNotification *)noti{

    [self resetBGView];
    [self updateMainView];
    [self updateChatListView];
    [self updateCircleListView];
    [self updateShareCircleDetailView];
    
}

  • 监听设备方向,会返回多个方向,不冲突。
  • 监听设备方向,先是设备旋转,然后才是界面旋转,在操作界面时,可能界面还没有旋转。

3. 控制屏幕旋转的函数

// 是否支持旋转
- (BOOL)shouldAutorotate {
    return YES;
}

// 支持的方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskPortrait;
}

// 模态时默认方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return UIInterfaceOrientationPortrait;
}

4. 不同根控制器情况下的解决

  • UIViewController根控制器
// 是否支持旋转
- (BOOL)shouldAutorotate {
    return NO;
}

// 支持的方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
}

// 默认方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
}
  • UINavigationController根控制器
// 是否支持旋转
- (BOOL)shouldAutorotate {
    return self.viewControllers.lastObject.shouldAutorotate ? YES : NO;
}

// 支持的方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    
    return self.viewControllers.lastObject.supportedInterfaceOrientations ? self.viewControllers.lastObject.supportedInterfaceOrientations : UIInterfaceOrientationMaskPortrait;
}

// 默认方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    
    return self.viewControllers.lastObject.preferredInterfaceOrientationForPresentation ? self.viewControllers.lastObject.preferredInterfaceOrientationForPresentation : UIInterfaceOrientationPortrait;
}
  • UITabBarController根控制器
// 是否支持旋转
- (BOOL)shouldAutorotate {
   return self.selectedViewController.shouldAutorotate ? YES : NO;
}

// 支持的方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    
    return self.selectedViewController.supportedInterfaceOrientations ? self.selectedViewController.supportedInterfaceOrientations : UIInterfaceOrientationMaskPortrait;
}

// 默认方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {

    return self.selectedViewController.preferredInterfaceOrientationForPresentation ? self.selectedViewController.preferredInterfaceOrientationForPresentation : UIInterfaceOrientationPortrait;
}
  • 更简单的实现方案 来个Swift版本的
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) 
-> UIInterfaceOrientationMask {

}

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) 
-> UIInterfaceOrientationMask {
    if isAllowAutorotate {
        return [.portrait, .landscapeLeft, .landscapeRight]
    }
    else {
        return .portrait
    }
}

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        isAllowAutorotate = false
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        isAllowAutorotate = true
    }
}

横屏跳竖屏 竖屏跳横屏

  • Present情况
override var preferredInterfaceOrientationForPresentation:
UIInterfaceOrientation {
    return .portrait
}
  • Push情况
override var shouldAutorotate: Bool {
    return true
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    // 切换到你想要的方向
    driveScreen(to: .portrait)
}

func driveScreen(to direction: UIInterfaceOrientation) {
    UIDevice.current.setValue(direction.rawValue, forKey: "orientation")
}

补充:iOS 获取屏幕方向

UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
    
if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
        
}else {
        
}

不推荐使用UIDevice来获取屏幕方向,因为第一次获取的时候会获得UIDeviceOrientationUnknown UIDeviceOrientation duration = [[UIDevice currentDevice] orientation];

强制屏幕旋转

- (void)interfaceOrientation:(UIInterfaceOrientation)orientation {
    if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
        SEL selector             = NSSelectorFromString(@"setOrientation:");
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
        [invocation setSelector:selector];
        [invocation setTarget:[UIDevice currentDevice]];
        int val                  = orientation;
        [invocation setArgument:&val atIndex:2];
        [invocation invoke];
    }
}