-(BOOL)shouldAutorotate
和-(UIInterfaceOrientationMask)supportedInterfaceOrientations
。解決方案是:重寫nav的旋轉方法,把結果指向到topViewController:bash
-(BOOL)shouldAutorotate{
return self.topViewController.shouldAutorotate;
}
-(UIInterfaceOrientationMask)supportedInterfaceOrientations{
return self.topViewController.supportedInterfaceOrientations;
}
複製代碼
對於UITabBarController
,就轉嫁爲它的selectedViewController
的結果。app
邏輯流的初始時物理上手機方向改變了。全部若是A push到 B,A只支持豎屏,而B只支持橫屏,若是這時手機物理方向沒變,那麼B仍是會跟A同樣豎屏,哪怕它只支持橫屏而且問題1也解決了的。測試
解決方案:強制旋轉。ui
@implementation UIDevice (changeOrientation)
+ (void)changeInterfaceOrientationTo:(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];
}
}
@end
複製代碼
給UIDevice
提供一個category,調用setOrientation:
這個私有方法來實現。spa
那麼解決方案是:設計
-(BOOL)shouldAutorotate
和-(UIInterfaceOrientationMask)supportedInterfaceOrientations
來達到本身的目的。-(void)viewWillAppear:(BOOL)animated{
[UIDevice changeInterfaceOrientationTo:(UIInterfaceOrientationLandscapeLeft)];
}
複製代碼
要驗證問題3的方案是否知足須要,知足須要的意思是:每一個頁面可以顯示它支持的方向並且不會干擾到其餘界面。因此測試一下push和pop的狀況。code
測試變量有:繼承
先後兩個界面方向一致的畫,結果確定是好的,就不測試 了。最終測試結果以下:get
動做 | 當前 | 目標 | 目標可旋轉 | 結果 |
---|---|---|---|---|
push | 默認 | 特殊 | ✔️ | 成功 |
push | 默認 | 特殊 | ❌ | 失敗 |
push | 特殊 | 默認 | ✔️ | 失敗(2) |
push | 特殊 | 默認 | ❌ | 失敗(3) |
pop | 默認 | 特殊 | ✔️ | 成功 |
pop | 默認 | 特殊 | ❌ | 失敗 |
pop | 特殊 | 默認 | ✔️ | 成功(1) |
pop | 特殊 | 默認 | ❌ | 成功(1) |
viewWillDisappear
裏的強制旋轉後,標記2能夠解決。但標記3仍是失敗,其實push時,下一個界面若是是不可旋轉的,那麼方向必定是不變了。特殊界面只要保持,進入和離開時都調用強制旋轉,而且自身shouldAutorate
爲YES,那麼push或pop進入特殊界面都沒有問題。關鍵是從特殊界面離開進入默認界面,pop時是成功的,push時若是默認界面是不可旋轉的,就會失敗。string
針對這個有兩種方案:
由於特殊界面支持的方向不包含默認方向,因此只是強制旋轉時不起做用的,在強制旋轉前還要修改支持的方向。具體代碼:
- (IBAction)push:(id)sender {
[self changeOrientationBeforeDisappear]; //離開前先修改方向,其餘每一個出口都要調用這個方法。不能在`viewWillDisappear`裏調用,由於這時push等已經觸發了
TFThirdViewController *thirdVC = [[TFThirdViewController alloc] init];
[self.navigationController pushViewController:thirdVC animated:YES];
}
-(void)changeOrientationBeforeDisappear{
_orientation = UIInterfaceOrientationMaskPortrait; //替換爲默認方向
[UIDevice changeInterfaceOrientationTo:(UIInterfaceOrientationPortrait)];
_orientation = UIInterfaceOrientationMaskLandscapeLeft; //替換爲特殊方向界面自身須要的方向
}
-(UIInterfaceOrientationMask)supportedInterfaceOrientations{
return _orientation; //根據變量變化而變化
}
複製代碼
若是下一個界面不是默認,會是什麼狀況?會有兩次旋轉。離開時旋轉到默認,進入下一個界面,它自身又旋轉到指定方向。效果很差,若是想一次到位,怎麼辦?就要離開的時候知道下一個界面指望的方向是什麼,而後preferredInterfaceOrientationForPresentation
正好符合這個意圖。 因此修改成:
@interface TFSecondViewController (){
UIInterfaceOrientationMask _orientation;
UIInterfaceOrientationMask _needOrientation;
}
@end
@implementation TFSecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
_needOrientation = UIInterfaceOrientationMaskLandscapeLeft;
_orientation = _needOrientation;
}
-(void)viewWillAppear:(BOOL)animated{
[UIDevice changeInterfaceOrientationTo:(UIInterfaceOrientationLandscapeLeft)];
}
-(BOOL)shouldAutorotate{
return YES;
}
- (IBAction)push:(id)sender {
TFThirdViewController *thirdVC = [[TFThirdViewController alloc] init];
[self changeOrientationBeforeDisappearTo:thirdVC]; //離開前先修改方向,其餘每一個出口都要調用這個方法。不能在`viewWillDisappear`裏調用,由於這時push等已經觸發了
[self.navigationController pushViewController:thirdVC animated:YES];
}
-(void)changeOrientationBeforeDisappearTo:(UIViewController *)nextVC{
_orientation = UIInterfaceOrientationMaskAll; //改成任意方向
[UIDevice changeInterfaceOrientationTo:[nextVC preferredInterfaceOrientationForPresentation]];
_orientation = _needOrientation; //替換爲特殊方向界面自身須要的方向
}
-(UIInterfaceOrientationMask)supportedInterfaceOrientations{
return _orientation; //根據變量變化而變化
}
@end
複製代碼
_needOrientation
時當前頁面須要的樣式。
總結起來就是:
viewWillAppear
)強制旋轉到須要的方向viewWillDisappear
,而是push操做以前,先修改方向爲下一個界面的指望方向。shouldAutorotate
保持爲YES。preferredInterfaceOrientationForPresentation
的方向要和進入時的方向一致,這樣就不會有2次旋轉。相比把基類的shouldAutorotate
改成YES,這個方案的好處是,把特殊狀況的處理基本都壓縮在特殊界面自身內部了,依賴的只有其餘界面的supportedInterfaceOrientations
,這個方法是一個補充性的,不會干擾其餘界面本來的設計。而對shouldAutorotate
卻比較麻煩,由於其餘界面可能不但願旋轉。
再次測試pop和push狀況:
動做 | 當前 | 目標 | 目標可旋轉 | 結果 |
---|---|---|---|---|
push | 默認 | 特殊 | ✔️ | 成功 |
push | 特殊 | 默認 | ✔️ | 成功 |
push | 特殊 | 默認 | ❌ | 成功 |
pop | 默認 | 特殊 | ✔️ | 成功 |
pop | 特殊 | 默認 | ✔️ | 成功 |
pop | 特殊 | 默認 | ❌ | 成功 |
push | 特殊1 | 特殊2 | ✔️ | 成功 |
pop | 特殊1 | 默認2 | ✔️ | 成功 |
動做 | 當前 | 目標 | 目標可旋轉 | 結果 |
---|---|---|---|---|
present | 默認 | 特殊 | ✔️ | 奔潰(1) |
present | 特殊 | 默認 | ✔️ | 成功 |
present | 特殊 | 默認 | ❌ | 成功 |
dismiss | 默認 | 特殊 | ✔️ | 成功 |
dismiss | 特殊 | 默認 | ✔️ | 成功 |
dismiss | 特殊 | 默認 | ❌ | 成功 |
present | 特殊1 | 特殊2 | ✔️ | 成功 |
dismiss | 特殊1 | 默認2 | ✔️ | 成功 |
奔潰1的問題是由於沒有實現preferredInterfaceOrientationForPresentation
,而默認結果是當前的statusBar的樣式,從默認過去,那就是豎直方向,而這個界面supportedInterfaceOrientations
的樣式又是橫屏,因此優先的方向(preferredxxx)不包含在支持的方向(supportedxxx)裏就奔潰了。按照以前的約定,supportedInterfaceOrientations
是必須實現的,實現了就成功了。
因此解決方案經過測試。
最後,present和push的切換方式有個不一樣:若是A--->B使用present方式,A不可旋轉,但同時支持橫豎屏,B可旋轉,支持橫豎屏,那麼 A豎屏 ---> B豎屏 ---> 旋轉到橫屏 ---> dismiss 這個流程後,A會變成橫屏且不可旋轉。
也就是dismiss時,返回的界面不看你能不能旋轉,若是你支持當前的方向,就會直接變成當前方向的樣式。而supportedInterfaceOrientations
默認是3個方向的,因此不實現這個方法而使用默認的,在dismiss的時候會有坑。