一、監聽橫豎屏的切換
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; }
其中 enablePortrait
為 appdelegate.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、通過方法 shouldAutorotate
和 supportedInterfaceOrientations
實現個別界面豎屏:
-(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
的官方文檔中已經說明,
( _cmd
在 Objective-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; }
結果:
可看到橫屏下右側有空白,是因為橫屏是的 tableView
的 frame
還是豎屏時的 frame
,因此應該在屏幕旋轉時重新設置 tableView
的 frame
:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{ self.tableView.frame = [UIScreen mainScreen].bounds; }
此時橫豎屏是cell都能撐滿整個屏幕
注意:如果在 cell
中設置 btn
的 frame
是在 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
方法,繼承自 UIViewController
、 UITableViewController
、 UICollectionController
的控制器布局最好在 viewWillLayoutSubviews
里面,這樣橫豎屏切換不用再設置 frame
2、 willRotateToInterfaceOrientation:duration:
在ios8之后已經廢棄,建議使用 viewWillTransitionToSize: withTransitionCoordinator:
Tags: UITableView
文章來源:http://www.jianshu.com/p/6ac34ab1ea24