首先系统设置只能竖屏
1、UITabBarController 继承类实现
//是否自动旋转
-(BOOL)shouldAutorotate{
return self.selectedViewController.shouldAutorotate;
}
//支持哪些屏幕方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return [self.selectedViewController supportedInterfaceOrientations];
}
// persent出页面时的默认方向,
// 网上很多文章认为是进入页面时的默认方向, 这个是不对的, 这个方法只针对present出来的vc, 注意看方法名后面 ForPresentation
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
return [self.selectedViewController preferredInterfaceOrientationForPresentation];
}
2、UINavigationController 继承类实现
/是否自动旋转
-(BOOL)shouldAutorotate{
return self.topViewController.shouldAutorotate;
}
//支持哪些屏幕方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return [self.topViewController supportedInterfaceOrientations];
}
// persent出页面时的默认方向,
// 网上很多文章认为是进入页面时的默认方向, 这个是不对的, 这个方法只针对present出来的vc, 注意看方法名后面 ForPresentation
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
return [self.topViewController preferredInterfaceOrientationForPresentation];
}
3、AppDelegate.m实现
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
//避免导航栏导致bug
[[UIApplication sharedApplication] setStatusBarHidden:NO];
//_allowRotate 自己定义的属性,随便可以定义bool值,目的就是在某个viewController让window先支持屏幕旋转
if (_allowRotate == 1) {
return UIInterfaceOrientationMaskAll;
}else{
return (UIInterfaceOrientationMaskPortrait);
}
}
4、viewController 页面
//1.决定当前界面是否开启自动转屏,如果返回NO,后面两个方法也不会被调用,只是会支持默认的方向
- (BOOL)shouldAutorotate {
return YES;
}
//2.返回支持的旋转方向
//iPad设备上,默认返回值UIInterfaceOrientationMaskAllButUpSideDwon
//iPad设备上,默认返回值是UIInterfaceOrientationMaskAll
- (UIInterfaceOrientationMask)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortrait;
}
// persent出页面时的默认方向,
// 网上很多文章认为是进入页面时的默认方向, 这个是不对的, 这个方法只针对present出来的vc, 注意看方法名后面 ForPresentation
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortraitUpsideDown;
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES animated:animated];
//在视图出现的时候,将allowRotate改为1
AppDelegate * delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
delegate.allowRotate = 1;
}
到此为止,屏幕就可以根据你设置支持的方向进行旋转了
//当发生转屏事件时,系统的回调方法是:
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
// 即将开始转屏
[self viewWillBeginTransitionWithSize:size];
WS(weakSelf)
[coordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
[weakSelf viewDidEndTransitionWithSize:size];
}];
}
/// 子视图即将开始旋转
- (void)viewWillBeginTransitionWithSize:(CGSize)size {
if (size.width > size.height) { // 横屏
// 横屏布局 balabala
[self.view.layer setNeedsLayout];
[self.view.layer layoutIfNeeded];
} else {
// 竖屏布局 balabala
[self.view.layer setNeedsLayout];
[self.view.layer layoutIfNeeded];
}
}
/// 子视图旋转完成
- (void)viewDidEndTransitionWithSize:(CGSize)size {
if (size.width > size.height) { // 横屏
// 横屏布局 balabala
[self.view.layer setNeedsLayout];
[self.view.layer layoutIfNeeded];
} else {
// 竖屏布局 balabala
[self.view.layer setNeedsLayout];
[self.view.layer layoutIfNeeded];
}
}
//最好刷新下view
[self.view.layer setNeedsLayout];
[self.view.layer layoutIfNeeded];
我项目里用的是监听的方法,监听屏幕发声变化的时候
//在viewDidLoad 监听屏幕变化,但是这个方法不能判断屏幕到底向左还是向右,如果支持多个方向的话,遇到刘海屏还得适配,不然刘海屏有可能遮住UI,还有一个方法监听屏幕方向的
//屏幕方向通知
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(onDeviceOrientationDidChange) name:UIDeviceOrientationDidChangeNotification
object:nil];
//屏幕尺寸变化的时候
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(videoListdetectOrientation) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
#pragma mark - Notification:
- (void)videoListdetectOrientation{
[self.view.layer setNeedsLayout];
[self.view.layer layoutIfNeeded];
if(self.view.frame.size.width > self.view.frame.size.height){
//横屏
}else{
}
}
#pragma mark - 屏幕方向通知
- (BOOL)onDeviceOrientationDidChange{
//获取当前设备Device
UIDevice *device = [UIDevice currentDevice];
//识别当前设备的旋转方向
switch (device.orientation) {
case UIDeviceOrientationFaceUp:
DLog(@"屏幕幕朝上平躺");
break;
case UIDeviceOrientationFaceDown:
DLog(@"屏幕朝下平躺");
break;
case UIDeviceOrientationUnknown:
//系统当前无法识别设备朝向,可能是倾斜
DLog(@"未知方向");
break;
case UIDeviceOrientationLandscapeLeft:{
DLog(@"屏幕向左橫置"); //只支持的方向
break;
}
case UIDeviceOrientationLandscapeRight:
DLog(@"屏幕向右橫置");
break;
case UIDeviceOrientationPortrait:{
DLog(@"屏幕直立");
break;
}
case UIDeviceOrientationPortraitUpsideDown:
DLog(@"屏幕直立,上下顛倒");
break;
default:
DLog(@"無法识别");
break;
}
return YES;
}
//屏幕适配问题,横屏的时候获取不到状态栏高度,如果竖屏的时候self.view.layer layoutIfNeeded 不刷新view也获取不到状态栏高度,所以最好是竖屏的时候先用个属性赋值下,用Masonry 和 frame 都没切换 的时候都没问题,横屏的时候在调横屏子view的约束,一开始可以在-(instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self initsubviews];
}
return self;
}
只初始化添加,而不加约束
5、重点,竖屏的时候点击按钮push跳转没问题,横屏的时候push跳转过去之后,下个页面布局错乱
解决办法,一定要在跳转之前先把横屏变成竖屏在跳转。
注意点:如果只是写在-(void)viewWillDisappear:(BOOL)animated 方法里是不管用的,因为他会先执行下个页面的viewDidLoad 在执行旋转,这个时候下个页面的view的宽高是横屏状态,导致页面错乱,显示的是竖屏的样式,实际宽高是横屏
- (void)orientationPortrait{
AppDelegate * delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
delegate.allowRotate = 0;
if (@available(iOS 16.0, *)) {
// iOS 16新增加了一个方法setNeedsUpdateOfSupportedInterfaceOrientations 方法是 UIViewController 的方法。这和更新状态栏的方法有点像,简单点说就是你想转屏可以,需要通知UIViewController 你已经准备好要转屏的方向了,然后再调用转屏方法即可(转屏方法后面会讲到)
[self setNeedsUpdateOfSupportedInterfaceOrientations];
NSArray *array = [[[UIApplication sharedApplication] connectedScenes] allObjects];
UIWindowScene *scene = [array firstObject];
// 屏幕方向
UIInterfaceOrientationMaskorientation = UIInterfaceOrientationMaskPortrait;
UIWindowSceneGeometryPreferencesIOS *geometryPreferencesIOS = [[UIWindowSceneGeometryPreferencesIOS alloc] initWithInterfaceOrientations:orientation];
// 开始切换
[scenerequestGeometryUpdateWithPreferences:geometryPreferencesIOS errorHandler:^(NSError * _Nonnull error) {
}];
}else{
//这个方法只有在- (BOOL)shouldAutorotate( return YES; )时,才会生效。并且请注意使用的枚举值的不同。
if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
SEL selector = NSSelectorFromString(@"setOrientation:");
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
[invocationsetSelector:selector];
[invocationsetTarget:[UIDevice currentDevice]];
int val = UIInterfaceOrientationPortrait;
[invocationsetArgument:&val atIndex:2];
[invocationinvoke];
}
//会强制系统尝试将界面旋转到新的方向(必须加,不然横屏的时候跳到下个页面,下个页面会错乱)
[UIViewController attemptRotationToDeviceOrientation];
}