iPad橫豎屏適配

分類:技術 時間:2017-01-13

一、監聽橫豎屏的切換

1、通知方式:

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

如果使用這個通知,當iPhone/iPad旋轉的時候,你會得到的旋轉方向會是所有的 UIDeviceOrientationUnknown(屏幕方向向下)UIDeviceOrientationFaceUp(屏幕方向向上) 等,即如下枚舉中所有值

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

2、通過方法 willRotateToInterfaceOrientation: duration 來判斷

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
    if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) {// 橫屏
        NSLog(@quot;quot;);
    } else {//豎屏
    }
}

其中可以根據 UIInterfaceOrientation 值來判斷橫豎屏, UIInterfaceOrientation 為:

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

二、強制個別界面豎屏顯示

1、通過 appdelegate 的代理方法 application:supportedInterfaceOrientationsForWindow 實現:

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

其中 enablePortraitappdelegate.h 中的一個 BOOL 值屬性,如果某個界面僅僅支持豎屏,就設置為 YES ,否則不用管,如下在某個控制器中的代碼:

//不會直接變為橫屏
-(void)viewWillAppear:(BOOL)animated{
    AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    delegate.enablePortrait = YES;
}
- (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    AppDelegate *delegate = (AppDelegate *)[UIApplicationsharedApplication].delegate;
    delegate.enablePortrait = NO;
}

這樣設置完之后控制器就不支持橫屏顯示

2、通過方法 shouldAutorotatesupportedInterfaceOrientations 實現個別界面豎屏:

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

shouldAutorotate 意思是是否支持自動旋轉 supportedInterfaceOrientations 意思是旋轉的方向,當添加了以上兩個方法你會發現并沒什么用,看看官方文檔:

過描述我們可以發現這兩個方法需要設置在根視圖中,意思是說確實這個頁面是否旋轉旋轉的方向都是要根據根視圖的這兩個方法來判斷,如果有導航控制器,在 ViewController 中的 shouldAutorotate 不會被調用,會調用到導航控制器中的 shouldAutorotate 方法,解決問題:

新建 BaseNavigationController 繼承 UINavigationController ,實現方法

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

使 ViewController 的導航控制器為 BaseNavigationController 類型,并實現方法:

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

此時你會發現屏幕僅僅支持豎屏,橫屏不再起作用,如果這兩個方法未實現,則默認支持橫豎屏

如果大部分界面是豎屏,個別界面是橫屏,最好寫個繼承自 UIViewController 類的基類 BaseViewController ,在基類中實現方法:

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

之后再創建控制器就繼承 BaseViewController ,則創建的控制器就默認是豎屏,需要橫屏界面重新實現 supportedInterfaceOrientations 方法即可

- (UIInterfaceOrientationMask)supportedInterfaceOrientations  {  
    return UIInterfaceOrientationMaskLandscape;  
}

三、強制界面僅僅支持橫屏或豎屏

1、通過 KVC ,在 ViewDidLoad 中實現以下代碼:

[[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIDeviceOrientationLandscapeLeft] forKey:@quot;orientationquot;];
[[self class] attemptRotationToDeviceOrientation];

這里不是直接使用蘋果的私有變量,而是利用KVC的方法 間接的調用此方法,可以上架,不會被打回

2、利用 NSInvocation 調用 對象的消息

//使用這里的代碼也是oK的。 這里利用 NSInvocation 調用 對象的消息

//使用這里的代碼也是oK的。 這里利用 NSInvocation 調用 對象的消息
- (void) viewWillAppear:(BOOL)animated{
     [super viewWillAppear:animated];

    if([[UIDevice currentDevice]respondsToSelector:@selector(setOrientation:)]) {
         SEL selector = NSSelectorFromString(@quot;setOrientation:quot;);
         NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
         [invocation setSelector:selector];
         [invocation setTarget:[UIDevice currentDevice]];
         int val = UIInterfaceOrientationLandscapeLeft;//橫屏
         [invocation setArgument:amp;val atIndex:2];
         [invocation invoke];

    }
}

第一個參數需要接收一個指針,也就是傳遞值的時候需要傳遞地址

第二個參數:需要給指定方法的第幾個參數傳值

注意:設置參數的索引時不能從0開始,因為0已經被 self(target) 占用,1已經被 _cmd(selector) 占用在 NSInvocation 的官方文檔中已經說明,

( _cmdObjective-C 的方法中表示當前方法的 selector ,正如同 self 表示當前方法調用的對象實例。)

[invocationsetArgument:amp;valatIndex:2];

調用NSInvocation對象的invoke方法*只要調用invocation的invoke方法,就代表需要執行NSInvocation對象中制定對象的指定方法,并且傳遞指定的參數

[invocation invoke];

四、獲取當前屏幕的方向

UIInterfaceOrientation currentOrient = [UIApplication  sharedApplication].statusBarOrientation;

五、ipad橫屏時tableView左右有空白

- (void)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 = @quot;cellquot;;
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:indentifier];
    if (!cell) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:indentifier];
    }
    cell.textLabel.text = [NSString stringWithFormat:@quot;cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:indentifier];quot;];
    return cell;
}

結果:

如上使用的是系統的 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;
}
- (void)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 = @quot;cellquot;;
    TestCell *cell = [tableView dequeueReusableCellWithIdentifier:indentifier];
    if (!cell) {
        cell = [[TestCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:indentifier];
    }
    return cell;
}

結果:

可看到橫屏下右側有空白,是因為橫屏是的 tableViewframe 還是豎屏時的 frame ,因此應該在屏幕旋轉時重新設置 tableViewframe

- (void)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將要出現時:

- (void)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;
}
- (void)layoutSubviews{
    [super layoutSubviews];
    self.testBtn.frame = CGRectMake(0, 10, [UIScreen mainScreen].bounds.size.width, 30);
}
@end


//控制器中代碼
#import quot;ViewController.hquot;
#import quot;TestCell.hquot;
@interface ViewController ()lt;UITableViewDelegate, UITableViewDataSourcegt;
@property (nonatomic, strong)UITableView *tableView;
@end
@implementation ViewController
- (void)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)];
    }
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
    self.tableView.frame = [UIScreen mainScreen].bounds;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return 100;
}
- (void)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 = @quot;cellquot;;
    TestCell *cell = [tableView dequeueReusableCellWithIdentifier:indentifier];
    if (!cell) {
        cell = [[TestCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:indentifier];
    }
    return cell;
}
@end

最后

1、繼承自 UIView 的類設置子控件 frame 最好在 layoutSubViews 方法,繼承自 UIViewControllerUITableViewControllerUICollectionController 的控制器布局最好在 viewWillLayoutSubviews 里面,這樣橫豎屏切換不用再設置 frame
2、 willRotateToInterfaceOrientation:duration: 在ios8之后已經廢棄,建議使用 viewWillTransitionToSize: withTransitionCoordinator:


Tags: UITableView

文章來源:http://www.jianshu.com/p/6ac34ab1ea24


ads
ads

相關文章
ads

相關文章

ad