iOS Orientation一些事(不包括iOS6)
完整代码可在 gitcafe项目主页 中下载。
之前的一篇文章提到对iOS系统键盘的hack,其中有提到新版的东方财富通的数字键盘是自定义键盘。在新的需求里,个股搜索需要支持横屏,本人又比较懒,不想写代码一个键一个键的调frame,就想到了通过判断Orientation从xib载入不同的view直接贴上去。但是碰到了一个不小的问题。
因为代码是属于公司的,我不好直接拿出来贴。在这里我把问题简化地提出来:
如图有两个ViewController,设左边为A,右边为B。点击A时,把B push进navigationController。要求进入B时,如果画面为横屏,则在label显示landscape,如果是竖屏,则显示portrait。在B中,要随着设备的orientation变化,切换label显示landscape或者portrait。
一开始,我觉得这挺简单,只要在viewDidLoad中判断UIDeviceOrientation就可以了。于是写下了这样的代码:
A中:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return YES; }
B中:
- (void)viewDidLoad { [super viewDidLoad]; UILabel *label = (UILabel *)[self.view viewWithTag:1]; UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; if (UIDeviceOrientationIsLandscape(orientation)) { label.text = @"landscape"; }else{ label.text = @"portrait"; } } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return YES; } - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { UILabel *label = (UILabel *)[self.view viewWithTag:1]; if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) { label.text = @"landscape"; }else{ label.text = @"portrait"; } }
乍看起来好像挺正确的,怎么把玩都没问题。但是,一旦需求说,A不支持横屏,B支持。这时候问题就来了:
A改为:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return (interfaceOrientation == UIInterfaceOrientationPortrait); }
编译运行,在画面A的时候,就把模拟器设置成landscape:
这时,正如需求中要求的,A没有支持横屏。这是保持横屏的状态,点击push,则出现下面的画面:
可以看到,B虽然画面是竖屏的,但是label已经被改成landscape了。通过在ViewDidLoad和shouldAutorotateToInterfaceOrientation中设断点可以看到,UIDeviceOrientation是属于landscape的,而系统询问shouldAutorotateToInterfaceOrientation时,询问的是是否支持Portrait。这样两者值就不一样了。可见系统真正在渲染View的时候是按照Portrait来的,并不是根据UIDeviceOrientation来的,然而似乎没有一个方法来获得载入时UIInterfaceOrientation的办法。这个问题我一直没有找到办法去解决,或者即便测试测出来问题了,我也可以说,没有哪个傻逼是这么用手机的(事实上测试组一直没测试出这个BUG)。直到我今天看MBProgressHUD的代码时才直到UIInterfaceOrientation可以用一个看起来比较妖的办法来得到:
/*B中*/ - (void)viewDidLoad { [super viewDidLoad]; UILabel *label = (UILabel *)[self.view viewWithTag:1]; // UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; // if (UIDeviceOrientationIsLandscape(orientation)) { // label.text = @"landscape"; // }else{ // label.text = @"portrait"; // } UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; if (UIInterfaceOrientationIsLandscape(orientation)) { label.text = @"landscape"; }else{ label.text = @"portrait"; } }
这样一来,即便是那种变态的用法,也不会出现错误了。
除此之外我要说的是,我看到有些人把切换屏幕方向时,UI的变动都写在了shouldAutorotateToInterfaceOrientation方法里了,虽然也可以运行,但是只要变动比较复杂,可以看到很明显的动画延时。UI的变动应该写在willAnimateRotationToInterfaceOrientation才是,因为这个方法是运行在animation blocks里面的。shouldAutorotateToInterfaceOrientation应该只判断是否支持该屏幕方向。
本著作係採用創用 CC 姓名標示 2.0 通用版 授權條款授權.
2024年1月16日 17:02
hello, your website is really good. We do appreciate your give good results