代码控制APP中视图横屏,iPad横竖屏适配

何以解忧,唯有努力

一、监听横竖屏的切换

1、通知方式:

//监听UIApplicationDidChangeStatusBarFrameNotification通知[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(changeRotate:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];- changeRotate:(NSNotification*)noti { UIDeviceOrientation deviceOrientation = [[UIDevice currentDevice] orientation]; NSLog(@"%d -- ", deviceOrientation);}

如果使用这个通知,当iPhone/iPad旋转的时候,你会得到的旋转方向会是所有的UIDeviceOrientationUnknownUIDeviceOrientationFaceUp等,即如下枚举中所有值

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;

如果仅仅是监听横竖屏的话,可以监听UIApplicationDidChangeStatusBarOrientationNotification通知,即:

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

此时会监听下面几种情况的通知:

UIDeviceOrientationPortrait UIDeviceOrientationPortraitUpsideDown UIDeviceOrientationLandscapeLeft UIDeviceOrientationLandscapeRight 

也可以用以下通知:

- viewDidLoad { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doRotateAction:) name:UIDeviceOrientationDidChangeNotification object:nil];}- doRotateAction:(NSNotification *)notification { if ([[UIDevice currentDevice] orientation] == UIDeviceOrientationPortrait || [[UIDevice currentDevice] orientation] == UIDeviceOrientationPortraitUpsideDown) { NSLog; } else if ([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeLeft || [[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeRight) { NSLog; }}

2、通过方法willRotateToInterfaceOrientation: duration来判断

- willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{ if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) {// 横屏 NSLog; } else {//竖屏 }}

其中可以根据UIInterfaceOrientation值来判断横竖屏,UIInterfaceOrientation为:

typedef NS_ENUM(NSInteger, UIInterfaceOrientation) { UIInterfaceOrientationUnknown = UIDeviceOrientationUnknown, UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait, UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown, UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight, UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft} __TVOS_PROHIBITED;

注意:在手机开启竖排方向锁定时,上述方法无效

为了解决即使在手机开启竖排方向锁定时,仍然能知道手机是竖向还是横向的问题,可以使用加速计

加速计的原理:

图片 1图片 2

  • 检测设备在x,y,z轴上的加速度(加速度范围为-1 ~ 1)
  • 根据加速度的数值,判断手机屏幕方向。实现代码:
#import "ViewController.h"#import <CoreMotion/CoreMotion.h>@implementation ViewController- viewDidLoad { [super viewDidLoad]; CMMotionManager *motionManager = [[CMMotionManager alloc] init]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 加速计 if (motionManager.accelerometerAvailable) { motionManager.accelerometerUpdateInterval = 1; [motionManager startAccelerometerUpdatesToQueue:queue withHandler:^(CMAccelerometerData * _Nullable accelerometerData, NSError * _Nullable error) { if  { [motionManager stopAccelerometerUpdates]; NSLog; } else { double x = accelerometerData.acceleration.x; double y = accelerometerData.acceleration.y; if  >= fabs { if (y >= 0){ NSLog(@"upSideDown"); } else{ NSLog(@"Portrait"); } } else{ if (x >= 0){ NSLog; }else{ NSLog; } } } }]; } else { NSLog(@"This device has no accelerometer"); }}

引言

现在做的一个项目,有需求是要单独强制横屏一个页面,而其他页面允许正常旋转横屏和竖屏。查了很多资料,很多写到这个问题,但是基本上都只能解决Present模式下的强制横屏。也有指出是Push模式下的,但是Push模式下的测试过都不行。最终做了一个综合,达到了目标。

很多时候,项目中都有这样的需求:APP中以竖屏为主,个别界面会要求横屏显示,或者要根据用户的手机朝向自动切换横竖屏;下面就来详细讲解,在项目中怎么使用代码来控制APP的界面转换.首先,要想APP支持多个方向,需要在工程进行设置支持的朝向:在General-->Deployment Info-->Device Orientation中进行设置

前言

今天不是个好日子,为一个屏幕强制旋转搞的天昏地暗...一开始,我满怀信心的挪了几行代码过来

-  shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{ return (toInterfaceOrientation == self.preferredInterfaceOrientationForPresentation);}-  shouldAutorotate{ //  // Only auto rotate if we're on the screen (see above) return NO;}- (NSUInteger)supportedInterfaceOrientations{ return UIInterfaceOrientationMaskLandscape;}- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{ //  // Prefer  landscape return UIInterfaceOrientationLandscapeRight;}

然而,事事尽不如人意,不得行。于是不得不开始查找资料,然而在网上找了许多资料后,发现几乎都是一样的..跑起来效果也差强人意,话不多说,直接上最后解决方法,是针对push的,present这种比较好实现,这里就不写了。

二、强制个别界面竖屏显示

1、通过appdelegate的代理方法application:supportedInterfaceOrientationsForWindow实现:

- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(nullable UIWindow *)window{ if(_enablePortrait){ return UIInterfaceOrientationMaskPortrait; } return UIInterfaceOrientationMaskLandscape | UIInterfaceOrientationMaskPortrait;}

其中enablePortraitappdelegate.h中的一个BOOL值属性,如果某个界面仅仅支持竖屏,就设置为YES,否则不用管,如下在某个控制器中的代码:

//不会直接变为横屏-viewWillAppear:animated{ AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate; delegate.enablePortrait = YES;}- viewWillDisappear:animated{ [super viewWillDisappear:animated]; AppDelegate *delegate = (AppDelegate *)[UIApplicationsharedApplication].delegate; delegate.enablePortrait = NO;}

这样设置完之后控制器就不支持横屏显示

2、通过方法shouldAutorotatesupportedInterfaceOrientations实现个别界面竖屏:

-shouldAutorotate{ return YES;}- (UIInterfaceOrientationMask)supportedInterfaceOrientations{ return UIInterfaceOrientationMaskPortrait;}

shouldAutorotate 意思是是否支持自动旋转 supportedInterfaceOrientations意思是旋转的方向,当添加了以上两个方法你会发现并没什么用,看看官方文档:

图片 3过描述我们可以发现这两个方法需要设置在根视图中,意思是说确实这个页面是否旋转旋转的方向都是要根据根视图的这两个方法来判断,如果有导航控制器,在ViewController中的shouldAutorotate不会被调用,会调用到导航控制器中的shouldAutorotate方法,解决问题:新建BaseNavigationController继承UINavigationController,实现方法

//是否自动旋转-shouldAutorotate{ return self.topViewController.shouldAutorotate;}//支持的方向- (UIInterfaceOrientationMask)supportedInterfaceOrientations{ return self.topViewController.supportedInterfaceOrientations;}//一开始的方向 很重要- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{ return self.topViewController.preferredInterfaceOrientationForPresentation;}

使ViewController的导航控制器为BaseNavigationController类型,并实现方法:

-shouldAutorotate{ return YES;}- (UIInterfaceOrientationMask)supportedInterfaceOrientations{ return UIInterfaceOrientationMaskPortrait;}//- (UIInterfaceOrientationMask)supportedInterfaceOrientations{// return UIInterfaceOrientationMaskLandscape;//}//一开始就左横屏,必须supportedInterfaceOrientations方法返回的集合包括UIInterfaceOrientationLandscapeLeft//- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{// return UIInterfaceOrientationLandscapeLeft;//}

此时你会发现屏幕仅仅支持竖屏,横屏不再起作用,如果这两个方法未实现,则默认支持横竖屏如果大部分界面是竖屏,个别界面是横屏,最好写个继承自UIViewController类的基类BaseViewController,在基类中实现方法:

-shouldAutorotate{ return YES;}- (UIInterfaceOrientationMask)supportedInterfaceOrientations{ return UIInterfaceOrientationMaskPortrait;}

之后再创建控制器就继承BaseViewController,则创建的控制器就默认是竖屏,需要横屏界面重新实现supportedInterfaceOrientations方法即可

- (UIInterfaceOrientationMask)supportedInterfaceOrientations { return UIInterfaceOrientationMaskLandscape; } 

实现

话不多说,上代码。

图片 4设置APP支持的手机方向

方案

图片 512.png

  • 先在工程中进行如此配置(工程需要指出竖屏 某些界面的强制横屏)
  • 由于是push,所以我们需要在自定义的NavigationController中进行如下设置,MainViewController为我们需要强制横屏的vc
@interface MyNavigationController ()@end@implementation MyNavigationController- viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view.}- didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}- (UIInterfaceOrientationMask)supportedInterfaceOrientations { if ([self.topViewController isKindOfClass:[MainViewController class]]){ return [self.topViewController supportedInterfaceOrientations]; } return UIInterfaceOrientationMaskPortrait;}- shouldAutorotate{ if ([self.topViewController isKindOfClass:[MainViewController class]]){ return [self.topViewController shouldAutorotate]; } return NO;}@end
  • 在需要选择的vc中添加如下代码
- viewWillAppear:animated { [super viewWillAppear:animated]; [self orientationToPortrait:UIInterfaceOrientationLandscapeLeft];}- viewWillDisappear:animated { [super viewWillDisappear:animated];}#pragma mark ==强制横屏- (UIInterfaceOrientationMask)supportedInterfaceOrientations { return UIInterfaceOrientationMaskAllButUpsideDown;}- orientationToPortrait:(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]; }}//设置为允许旋转- shouldAutorotate { return YES;}- viewWillTransitionToSize:size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator{ [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];}

上面supportedInterfaceOrientations方法中的返回值其实也可以设置成你需要的横屏方式,我这设置的只不过还包括了竖屏而已,shouldAutorotate这个是必须要的,不然你在设置横屏的时候将会无效,viewWillTransitionToSize这个函数不是旋转需要加的,我在这里写出来是为了方便在旋转后进行界面UI的调整,size的大小你会发现长宽变成了旋转之前的宽长

  • 如何在返回的时候调整为竖屏呢?一开始我是打算在viewWillDisappear中继续调用orientationToPortrait方法,只不过参数为竖屏的值,然而并没有什么用...为了解决这个问题,目前我的解决办法就是在push前的页面中也来实现改方法,只不过在viewWillAppear中调用的方法为[self orientationToPortrait:UIInterfaceOrientationPortrait]由于时间紧迫就不在仔细去研究了,希望有更好的方案的朋友多多指教~

三、强制界面仅仅支持横屏或竖屏

1、通过KVC,在ViewDidLoad中实现以下代码:

[[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIDeviceOrientationLandscapeLeft] forKey:@"orientation"];[[self class] attemptRotationToDeviceOrientation];

这里不是直接使用苹果的私有变量,而是利用KVC的方法 间接的调用此方法,可以上架,不会被打回2、利用 NSInvocation 调用 对象的消息//使用这里的代码也是oK的。 这里利用 NSInvocation 调用 对象的消息

//使用这里的代码也是oK的。 这里利用 NSInvocation 调用 对象的消息-  viewWillAppear:animated{ [super viewWillAppear:animated]; 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 = UIInterfaceOrientationLandscapeLeft;//横屏 [invocation setArgument:&val atIndex:2]; [invocation invoke]; }}

第一个参数需要接收一个指针,也就是传递值的时候需要传递地址第二个参数:需要给指定方法的第几个参数传值注意:设置参数的索引时不能从0开始,因为0已经被self占用,1已经被_cmd占用在NSInvocation的官方文档中已经说明,(_cmdObjective-C的方法中表示当前方法的selector,正如同self表示当前方法调用的对象实例。)

[invocationsetArgument:&valatIndex:2];

调用NSInvocation对象的invoke方法*只要调用invocation的invoke方法,就代表需要执行NSInvocation对象中制定对象的指定方法,并且传递指定的参数

[invocation invoke];

1. Present模式下,强制横屏

// HMXThirdViewController.m

// 支持两个横屏的方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskLandscapeLeft;
}

Present模式很简单,只需要设置支持的屏幕方向为横屏的方向就能够达到目的。

这样,就可以在项目中使用代码来控制页面的朝向了,在这之前,需要知道控制视图是否能够自动旋转,以及支持的朝向,是通过哪些方法来控制的?其实,主要使用的是下面三个方法:

四、获取当前屏幕的方向

UIInterfaceOrientation currentOrient = [UIApplication sharedApplication].statusBarOrientation;

2. Push模式下,强制横屏

HMXSecondViewController.m

-(void)viewWillAppear:(BOOL)animated{

    [super viewWillAppear:animated];
    [self setInterfaceOrientation:UIInterfaceOrientationLandscapeRight];
}

//强制转屏(这个方法最好放在BaseVController中)
- (void)setInterfaceOrientation:(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]];
        // 从2开始是因为前两个参数已经被selector和target占用
        [invocation setArgument:&orientation atIndex:2];
        [invocation invoke];
    }
}

// 这里必须设置为YES, 否则setInterfaceOrientation方法无效
- (BOOL)shouldAutorotate {
    return YES;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
}

这里有几点需要注意:

  1. 如果按照Present模式的方法进行强制横屏操作,会发现根本没有效果,而且supportedInterfaceOrientations方法不会被调用。
    查找资料才知道,HMXSecondViewController的supportedInterfaceOrientations方法会被UINavigationController拦截,所以我们这里需要自定义一个HMXNavigationController,并对它的supportedInterfaceOrientations方法进行处理,来让HMXSecondViewController的supportedInterfaceOrientations能够执行。
// HMXNavigationController.m

// 返回最上面一个UIViewController的shouldAutorotate
- (BOOL)shouldAutorotate {
    return self.topViewController.shouldAutorotate;
}

// 返回最上面一个UIViewController的supportedInterfaceOrientations
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return self.topViewController.supportedInterfaceOrientations;
}

上面的代码会让HMXSecondViewController的supportedInterfaceOrientations方法得到执行。

  1. 这时候我们会发现,在跳转页面的之后,跳转后的页面横竖屏模式和跳转前的一样。也就是说,如果我们跳转之前的页面是竖屏显示,那么跳转之后依然是竖屏显示。但是等到我们将页面旋转之后,页面就无法再旋转为竖屏显示。
    解决方式是:在跳转页面之后,强制把页面设置为横屏显示。所以在HMXSecondViewController中调用了setInterfaceOrientation方法。

3. 在添加了setInterfaceOrientation方法并执行之后,我们发现,我们想要解决的问题其实还没有被解决。 解决方案是:在HMXSecondViewController添加shouldAutorotate并返回YES,允许setInterfaceOrientation对屏幕显示模式进行修改。同时需要在HMXNavigationController添加shouldAutorotate方法,让HMXSecondViewController中的shouldAutorotate能够被调用

// New Autorotation support. //是否自动旋转,返回YES可以自动旋转 - shouldAutorotate NS_AVAILABLE_IOS __TVOS_PROHIBITED; //返回支持的方向 - (UIInterfaceOrientationMask)supportedInterfaceOrientations NS_AVAILABLE_IOS __TVOS_PROHIBITED; // Returns interface orientation masks. //这个是返回优先方向 - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation NS_AVAILABLE_IOS __TVOS_PROHIBITED; 

五、ipad横屏时tableView左右有空白

- viewDidLoad { [super viewDidLoad]; self.tableView = [[UITableView alloc]initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStylePlain]; self.tableView.delegate = self; self.tableView.dataSource = self; [self.view addSubview:self.tableView];}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return 100;}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *indentifier = @"cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:indentifier]; if  { cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:indentifier]; } cell.textLabel.text = [NSString stringWithFormat:@"cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:indentifier];"]; return cell;}

结果:

图片 6如上使用的是系统的cell,会出现以下左右空白问题,这是系统cell自动设置的内边距,我们要自定义cell替换系统cell,cell中添加一个button做测试:

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { self.testBtn = [[UIButton alloc]init]; self.testBtn.backgroundColor = [UIColor orangeColor]; [self addSubview:self.testBtn]; } return self;}- layoutSubviews{ [super layoutSubviews]; self.testBtn.frame = CGRectMake(0, 10, [UIScreen mainScreen].bounds.size.width, 30);}

此时只需要将控制器中的系统cell替换为自定义的cell即可

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *indentifier = @"cell"; TestCell *cell = [tableView dequeueReusableCellWithIdentifier:indentifier]; if  { cell = [[TestCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:indentifier]; } return cell;}

结果:

图片 7

可看到横屏下右侧有空白,是因为横屏是的tableViewframe还是竖屏时的frame,因此应该在屏幕旋转时重新设置tableViewframe

- willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{ self.tableView.frame = [UIScreen mainScreen].bounds;}

此时横竖屏是cell都能撑满整个屏幕注意:如果在cell中设置btnframe是在init方法中,横屏时cell右侧仍然有空白,因此规范写法就是在layoutSubviews设置frame,如:

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { self.testBtn = [[UIButton alloc]initWithFrame:CGRectMake(0, 10, [UIScreen mainScreen].bounds.size.width, 30)]; self.testBtn.backgroundColor = [UIColor orangeColor]; [self addSubview:self.testBtn]; } return self;}

细心观察会发现tableViewd的分割线有内缩进,为了让tableView分割线自定义且为了适配ios7、8、9以上系统,应在cell将要出现时:

- tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { if ([cell respondsToSelector:@selector(setPreservesSuperviewLayoutMargins:)]) { [cell setPreservesSuperviewLayoutMargins:NO]; } if ([cell respondsToSelector:@selector(setLayoutMargins:)]) { [cell setLayoutMargins:UIEdgeInsetsZero]; }}

并且在viewDidLoad中增加代码:

if ([self.tableView respondsToSelector:@selector(setCellLayoutMarginsFollowReadableWidth:)]) { self.tableView.cellLayoutMarginsFollowReadableWidth = NO; } if ([self.tableView respondsToSelector:@selector(setSeparatorInset:)]) { [self.tableView setSeparatorInset:UIEdgeInsetsMake(0, 16, 0, 16)]; }

此时即可实现分割线内边距,不过还可以隐藏系统分割线,自己在自定义cell中设置最后代码如下:

//cell中代码@implementation TestCell- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { self.testBtn = [[UIButton alloc]init]; self.testBtn.backgroundColor = [UIColor orangeColor]; [self addSubview:self.testBtn]; } return self;}- layoutSubviews{ [super layoutSubviews]; self.testBtn.frame = CGRectMake(0, 10, [UIScreen mainScreen].bounds.size.width, 30);}@end//控制器中代码#import "ViewController.h"#import "TestCell.h"@interface ViewController ()<UITableViewDelegate, UITableViewDataSource>@property (nonatomic, strong)UITableView *tableView;@end@implementation ViewController- viewDidLoad { [super viewDidLoad]; self.tableView = [[UITableView alloc]initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStylePlain]; self.tableView.delegate = self; self.tableView.dataSource = self; [self.view addSubview:self.tableView]; if ([self.tableView respondsToSelector:@selector(setCellLayoutMarginsFollowReadableWidth:)]) { self.tableView.cellLayoutMarginsFollowReadableWidth = NO; } if ([self.tableView respondsToSelector:@selector(setSeparatorInset:)]) { [self.tableView setSeparatorInset:UIEdgeInsetsMake(0, 16, 0, 16)]; }}- willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{ self.tableView.frame = [UIScreen mainScreen].bounds;}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return 100;}- tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { if ([cell respondsToSelector:@selector(setPreservesSuperviewLayoutMargins:)]) { [cell setPreservesSuperviewLayoutMargins:NO]; } if ([cell respondsToSelector:@selector(setLayoutMargins:)]) { [cell setLayoutMargins:UIEdgeInsetsZero]; }}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *indentifier = @"cell"; TestCell *cell = [tableView dequeueReusableCellWithIdentifier:indentifier]; if  { cell = [[TestCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:indentifier]; } return cell;}@end

参考

解决这个问题查看了很多博客,感谢各位大大们的分享。
这里贴出来一个写的非常好的博客: http://www.jianshu.com/p/780738918a6c

一般情况下,实现前两个方法即可!这些都是UIViewController的实例方法,直接在需要设置的控制器内重写上面的方法即可;有时候,你会发现,只有在APP启动时出现的第一个界面内重写上面的两个方法,会有效果;而在其他的界面重写,并没有效果.这是因为,程序启动后的第一个界面是程序的跟视图控制器,而这些设置,必须是影响了跟视图中的相应设置,才会有效果;假如,你在跟视图中进行了如下设置:

六、iphone适配竖屏、iPad适配横竖屏设置

可进行如下设置:

图片 8图片 9

我注意到有些项目中可以直接分别设置iphone和iPad的横竖屏、状态栏样式,但是我新建的项目中并没有,网上也没有找到资料,知道的读者麻烦说下,如图:

图片 10

- shouldAutorotate{ return NO; } 

最后

1、继承自UIView的类设置子控件frame最好在layoutSubViews方法,继承自UIViewControllerUITableViewControllerUICollectionController的控制器布局最好在viewWillLayoutSubviews里面,这样横竖屏切换不用再设置frame2、 willRotateToInterfaceOrientation:duration:在ios8之后已经废弃,建议使用viewWillTransitionToSize: withTransitionCoordinator:

那么,在整个项目中,无论你如何设置,都不会有自动旋转屏幕的效果.综上可知,如果要改变某个视图屏幕方向,需要告诉跟视图,在跟视图中修改为相应的设置即可;所以,问题来了,怎么告诉跟视图呢?这要视情况而定:

1、在viewWillTransitionToSize: withTransitionCoordinator:方法中获取屏幕宽高:[UIScreen mainScreen].bounds.size.width,这个方法是在屏幕旋转之前执行的,按理说此时屏幕宽高是旋转之前的屏幕宽高,但是某些情况下会出问题,问题截图:

图片 11问题的四个状态:1、勾选Upside DownLaunch Screen File选中LaunchScreen2、勾选Upside DownLaunch Screen File清空3、不勾选Upside DownLaunch Screen File选中LaunchScreen4、不勾选Upside DownLaunch Screen File清空然后我们在控制器中打印屏幕宽度

#define kSrceenW [UIScreen mainScreen].bounds.size.width- viewWillTransitionToSize:size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; NSLog(@"%f %f", [UIScreen mainScreen].bounds.size.width, kSrceenW);}```根据打印结果我们会发现情况2、3、4三种情况打印的是旋转之前的屏幕宽度,情况1打印的是旋转之后屏幕宽度,坑,所以我们最好用方法参数`size`(控制器view的size)对屏幕进行适配

1.当前viewController就是跟视图控制器

这种情况最简单,直接在控制器中重写上面的方法,设置是否支持自动旋转,以及支持的方向即可:

- shouldAutorotate{ return YES;}#if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0- (NSUInteger)supportedInterfaceOrientations#else- (UIInterfaceOrientationMask)supportedInterfaceOrientations#endif{ return UIInterfaceOrientationMaskAll;}

像上面我设置为支持自动旋转,支持所有的朝向;这种方式, 切换视图的方式主要是模态(present), 只需要在相应的UIViewController里重写上面的方法即可.但是这种情况很少见...对应的demo为:LZInterfaceRotation中的Rotation-UIViewController

2.项目的跟视图控制器是导航(UINavigationController)

这种情况比较常见,也很简单;一般使用的导航的时候,都不会去直接使用系统的UINavigationController,而会定义一个他的子类,做一些公共的设置:

#import <UIKit/UIKit.h>@interface BaseNaviViewController : UINavigationController@end

然后在导航的viewController里面重写上面的三个方法,这里也有两种方式来控制:

2.1 设置最后一个push的viewController

这种方式,主要是用在,当push到某个视图控制器时,要求他能够支持自动旋转,或者强制转为横屏;这里主要用到了导航的viewControllers属性,这是一个数组,数组的最后一个元素,就是最后push进去的viewController,所以,实现代码如下:

-shouldAutorotate { return [[self.viewControllers lastObject] shouldAutorotate]; } #if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0 - (NSUInteger)supportedInterfaceOrientations #else - (UIInterfaceOrientationMask)supportedInterfaceOrientations #endif { return [[self.viewControllers lastObject] supportedInterfaceOrientations]; } - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation]; } 

到这里还没有结束,这里只是获取到了最后一个push的viewController,然后用它去调用了相应的方法,具体的方法实现,还是需要在哪个viewController里进行设置的,也就是在需要特殊设置的viewController里重写上面的方法:

-shouldAutorotate{ return YES;}#if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0- (NSUInteger)supportedInterfaceOrientations#else- (UIInterfaceOrientationMask)supportedInterfaceOrientations#endif{ return UIInterfaceOrientationMaskPortraitUpsideDown;}- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { return UIInterfaceOrientationPortraitUpsideDown;}

其实这样也就是告诉跟视图控制器,这个界面我要特殊设置一下;对应的demo为: LZInterfaceRotation中的Rotation-UINavigationController

2.2设置指定的控制器

第一种方法是,只要push进去一个视图控制器,都可以进行相应的设置,而这种方法是只针对特定的控制器进行设置,例如这里,我需要把SecondViewController单独设置为支持横屏,只需要将第一种方法中的导航里的相应代码做如下修改:

-shouldAutorotate{ if ([[self.viewControllers lastObject]isKindOfClass:[SecondViewController class]]) { return YES; } return NO;}#if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0- (NSUInteger)supportedInterfaceOrientations#else- (UIInterfaceOrientationMask)supportedInterfaceOrientations#endif{ if ([[self.viewControllers lastObject]isKindOfClass:[SecondViewController class]]) { return UIInterfaceOrientationMaskLandscapeLeft; } return UIInterfaceOrientationMaskPortrait;}- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { if ([[self.viewControllers lastObject]isKindOfClass:[SecondViewController class]]) { return UIInterfaceOrientationLandscapeLeft; } return UIInterfaceOrientationPortrait;}

这样的好处是,可以控制指定的视图控制器,也不用在指定的视图控制器内再重写上面的方法;坏处就是,如果需要控制的视图控制器比较多的话,加的判断会多很多;没有方式一灵活;对应的demo为: LZInterfaceRotation中的Rotation-UINavigationController副本

3.跟视图控制器是UITabbarController

这种情况比较常见,项目的跟视图控制器是一个UITabBarController,这种情况的实现相对来说要复杂一些,但是,明白了原理,总是有方法来实现的.归根结底,也就是告诉跟视图,我这个界面需要支持旋转了;由于,在UITabBarController里直接获取到UIViewController比较困难,所以这里我采用通知的形式,来获取相应的设置状态;首先,设置一个全局的BOOL值,用于接收通知发送的参数:

#import <UIKit/UIKit.h>@interface BaseTabBar : UITabBarController{ BOOL shouldAutorotate; }@end

然后注册一个通知:

//注册旋转屏幕的通知 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(autorotateInterface:) name:@"InterfaceOrientationNotification" object:nil];

实现通知方法:

-autorotateInterface:(NSNotification *)notifition{ shouldAutorotate = [notifition.object boolValue];}

然后在重写的方法里加入判断:

/** * * @return 是否支持旋转 */ -shouldAutorotate { NSLog(@"======%d",shouldAutorotate); return shouldAutorotate;} /** * 适配旋转的类型 * * @return 类型 */ -(UIInterfaceOrientationMask)supportedInterfaceOrientations { if (!shouldAutorotate) { return UIInterfaceOrientationMaskPortrait; } return UIInterfaceOrientationMaskAllButUpsideDown; } 

这里我直接将支持的方向写死了,只是判断了一下是否支持自动旋转屏幕,如果需要将支持的方向传过来,可以修改通知携带的参数;最后在需要自动转屏的控制器内发送通知:

[[NSNotificationCenter defaultCenter] postNotificationName:@"InterfaceOrientationNotification" object:@"YES"]; 

对应的demo为: LZInterfaceRotation中的Rotation-UITabBarController

PS新增: 关于何时发送通知例如有这样的需求: A竖屏, 不能自动旋转, B 可横屏可竖屏可自动旋转, 而且在从B回到A的时候, 如果不是竖屏, 强制转为竖屏, 这就用到了下面强制转屏的方法;我们在使用的时候肯定是在BviewWillAppear方法里发送通知告诉跟视图要自动旋转, 而在离开这个视图时, 就有可能会在viewWillDisappear方法里发送不能自动旋转的通知, 这样是不能实现效果的, 应该放在viewDidDisappear方法里发送;为严谨起见, 建议在viewDidAppear方法里发送可以自动旋转的通知, 在viewDidDisappear方法里发送不可以自动选择的通知.这里使用的通知来告知跟视图是否自动旋转, 也可以使用NSUserDefaults** 来传递这个值, 或者单例都可以.**

到此,对于控制屏幕的旋转及方向,基本就介绍完了,如果有不足或者不对的地方,还请指正;如果你有更好的方式实现同样的效果,还请不吝赐教...

附加

最后介绍一个强制旋转屏幕的方法,这个方法可以在进入某个视图时,强制转成你需要的屏幕方向,用的比较多的是在一个竖屏的应用中强制转换某一个界面为横屏:

PS: 这个方法的使用有个前提, 一定是在跟视图的-shouldAutorotate返回值为YES的时候才有效.

//强制旋转屏幕 - orientationToPortrait:(UIInterfaceOrientation)orientation { 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];//前两个参数已被target和selector占用 [invocation invoke]; } 

使用的时候,只需要把你需要旋转的方向传过去即可!有一点需要注意:从A进入B的时候, 把B强制转换成横屏,返回的时候,需要在A出现的时候再转换为原来的方向,不然会有问题;个人建议可以在BviewWillAppear调用这个方法,转换屏幕,然后在AviewWillAppear中转换回来; 如果使用通知来告诉跟视图是否可以自动旋转, 这个方法要在通知发送之后调用.

具体效果及设置, 可参考demo: LZInterfaceRotation

本文由星彩网app下载发布于计算机编程,转载请注明出处:代码控制APP中视图横屏,iPad横竖屏适配

TAG标签: 星彩网app下载
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。