iOS App中大多数页面是只展示竖屏下的效果,但是少部分页面需要支持横竖屏。并且一般来说,我们处理屏幕旋转的方案都是直接通过开启和监听设备旋转的通知来进行处理的。本文讲讲viewWillTransitionToSize:withTransitionCoordinator:
方法的使用。
//开启和监听 设备旋转的通知(不开启的话,设备方向一直是UIInterfaceOrientationUnknown)
if (![UIDevice currentDevice].generatesDeviceOrientationNotifications) {
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
}
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(handleDeviceOrientationChange:)
name:UIDeviceOrientationDidChangeNotification object:nil];
//设备方向改变的处理
- (void)handleDeviceOrientationChange:(NSNotification *)notification{
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
switch (ddeviceOrientation) {
case UIDeviceOrientationFaceUp:
NSLog(@"屏幕朝上平躺");
break;
case UIDeviceOrientationFaceDown:
NSLog(@"屏幕朝下平躺");
break;
case UIDeviceOrientationUnknown:
NSLog(@"未知方向");
break;
case UIDeviceOrientationLandscapeLeft:
NSLog(@"屏幕向左横置");
break;
case UIDeviceOrientationLandscapeRight:
NSLog(@"屏幕向右橫置");
break;
case UIDeviceOrientationPortrait:
NSLog(@"屏幕直立");
break;
case UIDeviceOrientationPortraitUpsideDown:
NSLog(@"屏幕直立,上下顛倒");
break;
default:
NSLog(@"无法辨识");
break;
}
}
//最后在dealloc中移除通知 和结束设备旋转的通知
- (void)dealloc{
//...
[[NSNotificationCenter defaultCenter]removeObserver:self];
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
}
当我们需要处理某个控制器的屏幕横竖屏的情况时,UIViewController
自带的这两个方法处理其实更方便。
// Notifies when rotation begins, reaches halfway point and ends.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration API_DEPRECATED("Implement viewWillTransitionToSize:withTransitionCoordinator: instead", ios(2.0, 8.0)) API_UNAVAILABLE(tvos);
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation API_DEPRECATED("", ios(2.0, 8.0)) API_UNAVAILABLE(tvos);
但是iOS 8.0
之后,这两个方法被苹果弃用了,如果项目只支持iOS 8.0
以上的系统建议使用这个方法:
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator API_AVAILABLE(ios(8.0));
使用时会发现这个方法调用是在屏幕将要旋转时,等于调用了方法willRotateToInterfaceOrientation
,如果我需要屏幕旋转完成之后再调用,等于要调用didRotateFromInterfaceOrientation
方法就需要像下面这样处理:
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
[self.tableView reloadData];//已经旋转完成后的处理
} completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
}];
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
}
在使用iPad
测试时,发现UITableView
向上偏移了,并且越后面的元素偏移量越大,但是打印出来的偏移量contentOffset
以及contentSize
又都是正确的。
解决方案:通过currentIndex
记录当前显示cell
,屏幕旋转完成之后滑动到当前显示的cell
,就可以防止UITableView
偏移。
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
self.currentIndex = scrollView.contentOffset.y / CGRectGetHeight(scrollView.frame);
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
NSIndexPath *curIndexPath = [NSIndexPath indexPathForRow:self.currentIndex inSection:0];
[self.tableView scrollToRowAtIndexPath:curIndexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
} completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
}];
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
}
注:对于只有一个页面需要旋转的时候,这个方法很实用,但是当子控制器页面也需要旋转时,可能会导致子视图横竖屏状态修改了,但是父视图横竖屏状态未修改的bug。
此时,我们可以通过调用UIViewController
的updateViewConstraints
方法更新刷新视图的UI,这样就可以确保子视图父视图都能及时被刷新。
- (void)updateViewConstraints {
[super updateViewConstraints];
NSIndexPath *curIndexPath = [NSIndexPath indexPathForRow:self.currentIndex inSection:0];
[self.tableView scrollToRowAtIndexPath:curIndexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
}