前話:前前后后弄了一下午,嘗試各種解決方案(估摸有一二十種吧),最后誤打誤撞終於解決,鑒於在國內國外查找各種解決方法都未能解決,故有此文。(當然stackOverFlow 各種新奇的解決方案,我一一嘗試實戰,卻未能解決,可能是英文還不夠好,理解國外解決方案的不夠透徹所致,不過稍感欣慰,還是解決了;不管黑貓白貓,能抓耗子就是好貓吧;還真是應了這句話)
需求:整個APP中界面以豎屏為主,且不能自動橫豎屏切換,個別播放視頻頁面可以根據手機的方向橫豎屏切換或固定橫屏;
蘋果系統支持橫屏順序
默認讀取plist里面設置的方向(優先級最高)等同於Xcode Geneal設置里面勾選
application window設置的級別次之
然后是UINavigationcontroller
級別最低的是viewcontroller
注:其實這個優先級跟你的window的rootViewcontroller有關系,如果rootViewcontroller是UITabbarViewontroller,那么tabbar就類似前面所說的UINavigationcontroller
翻閱各大網站和論壇我總結了好幾種方式,但是大同小異的旋轉屏幕的代碼,以供參考:
支持ARC版本:
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 = UIInterfaceOrientationLandscapeRight;//這里可以改變旋轉的方向 [invocation setArgument:&val atIndex:2]; [invocation invoke]; }
或者
//強行旋轉,(會導致程序崩潰,而且不會有崩潰日志,很難發現崩潰在哪里,所以使用這個方法的時候一定要注意設置支持的方向) --> 我反正沒遇到 我用着好好的
如何手動旋轉設備?
Objective-C:
NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];
不支持ARC版本
if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) { [[UIDevice currentDevice] performSelector:@selector(setOrientation:) withObject:(id)UIInterfaceOrientationLandscapeRight]; }
還有一點就是如何判斷當前屏幕的方向:可以根據電源的現實方向來判斷,蘋果提供了這一方法
NSInteger i = [[UIApplication sharedApplication] statusBarOrientation]; if (i == UIInterfaceOrientationLandscapeRight){ //在這里可以寫相應的代碼 }
有些人可能想要監聽屏幕的自動旋轉:
1.注冊UIApplicationDidChangeStatusBarOrientationNotification通知(舉例:在一個viewcontroller類的viewdidload中注冊該通知),示例代碼如下: [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarOrientationChange:)name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; - (void)statusBarOrientationChange:(NSNotification *)notification{ UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; if (orientation == UIInterfaceOrientationLandscapeRight) // home鍵靠右{ // } if (orientation ==UIInterfaceOrientationLandscapeLeft) // home鍵靠左 { // } if (orientation == UIInterfaceOrientationPortrait){ // } if (orientation == UIInterfaceOrientationPortraitUpsideDown){ // } } 注意這種方式監聽的是StatusBar也就是狀態欄的方向,所以這個是跟你的布局有關的,你的布局轉了,才會接到這個通知,而不是設備旋轉的通知。當我們關注的東西和布局相關而不是純粹設備旋轉,我們使用上面的代碼作為實現方案比較適合。
2.注冊UIDeviceOrientationDidChangeNotification通知(舉例:我們同樣在一個viewcontroller類的viewdidload中注冊該通知),示例代碼如下: [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientChange:)name:UIDeviceOrientationDidChangeNotification object:nil]; - (void)orientChange:(NSNotification *)noti { NSDictionary* ntfDict = [noti userInfo]; UIDeviceOrientation orient = [UIDevice currentDevice].orientation; /* 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 */ switch (orient) { case UIDeviceOrientationPortrait: break; case UIDeviceOrientationLandscapeLeft: break; case UIDeviceOrientationPortraitUpsideDown: break; case UIDeviceOrientationLandscapeRight: break; default: break; } } 注意到這種方式里面的方向還包括朝上或者朝下,很容易看出這個完全是根據設備自身的物理方向得來的,當我們關注的只是物理朝向時,我們通常需要注冊該通知來解決問題(另外還有一個加速計的api,可以實現類似的功能,該api較底層,在上面兩個方法能夠解決問題的情況下建議不要用,使用不當性能損耗非常大)。
1.在項目中用代碼控制視圖是否能夠自動旋轉,支持哪些方向主要是用了下面的三個方法:
- // New Autorotation support.
- //是否自動旋轉,返回YES可以自動旋轉
- - (BOOL)shouldAutorotate NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;
- //返回支持的方向
- - (UIInterfaceOrientationMask)supportedInterfaceOrientations NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;
- // Returns interface orientation masks.
- //這個是返回優先方向
- - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;
一般情況下實現前兩個方法即可!!這些都是UIViewController的實例方法,直接在需要設置的控制器重寫就行...
2.簡單介紹我試過的集中方法
1. 這個方法別人說能用,我用了沒效果,當時也沒看別人的Demo ; 可能有效果吧 ,Demo 見下方
- //強制旋轉屏幕
- - (void)orientationToPortrait:(UIInterfaceOrientation)orientation {
- 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];//前兩個參數已被target和selector占用
- [invocation invoke];
- }
調用的時候只需把你想要的方向傳過去即可!!
使用的時候有個點需要注意,從A進入B的時候,把B強制轉換成橫屏,返回的時候,需要在A出現的時候再轉換為原來的方向,不然會有問題;個人建議可以在B的viewWillAppear調用這個方法,轉換屏幕(例如轉換為橫屏),然后在A的viewWillAppear中轉換回來;
最后附上以上內容的Demo:Demo地址
2.這里有一個用JS 和原生item 控制橫豎屏切換的Demo。地址
3.我的方案:
其他界面不支持橫屏:
播放界面橫屏:
Step1:
在APPDelegate.h文件中增加屬性:是否支持橫屏
/*** 是否允許橫屏的標記 */ @property (nonatomic,assign)BOOL allowRotation;
在APPDelegate.m文件中增加方法,控制全部不支持橫屏
-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window { if (self.allowRotation) { return UIInterfaceOrientationMaskAll; } return UIInterfaceOrientationMaskPortrait; }
Step2:
豎屏界面A 跳到 橫屏界面B
首先豎屏界面A 配置,直接貼代碼了;
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
橫屏界面B
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
好,關鍵的兩步來了;
1.豎屏界面A 跳到 橫屏界面B 一定用presentViewController 跳轉 (不知道我說的什么法的,下面的可以不看了)
注:.豎屏界面A---》CFMatchPlayBackViewController 橫屏界面B:IJKVideoViewController
IJKVideoViewController *ijk = [[IJKVideoViewController alloc] initWithURL:[NSURL URLWithString:self.replyPath]];
CFMatchPlayBackViewController *VC = ((CFMatchPlayBackViewController *)nextResponder);
__weak __typeof(VC)weakSelf = VC;
ijk.deviceTap = ^ {
NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];
};
[((UIViewController *)nextResponder) presentViewController:ijk animated:YES completion:nil];
2.橫屏界面B 跳轉到 豎屏界面A
[self.presentingViewController dismissViewControllerAnimated:NO completion:^{
//
self.deviceTap();
}];
3.重點: 本來這樣跳,蘋果本身自己是有一個Bug的,就是dismiss掉,回來依然是橫屏,而且還是不自適應的橫屏;不過解決辦法就是 ----> 見下圖
4.UIView 橫豎屏;最下層加一個View,利用UIView 旋轉方法;不失為好方法,親測也是可用的好辦法;看需求吧
6.我的解決方案英文版 (思路一脈相承):
If the modal controller was in landscape orientation before dismissal, the presenting ViewController may not return to the origin orientation (portrait). The problem is because the AppDelegate supportedInterfaceOrientationsForWindow method is called before the controller is actually dismissed and the presented controller check still returns Landscape mask. Set a flag to indicate whether the (modal) presented view controller will be displayed or not.
And in the modal presented ViewController set the orientation according to the flag: When the modal ViewController is presented - return Landscape. When it is dismissed then return portrait
Last step - from your AppDelegate call the modal presented ViewController for its orientation. I am just checking the currently presented ViewController and call the supportedInterfaceOrientations on it
For more info check this link |
后記: 寫文章真的是費神費力;但鑒於找遍網上並沒有很好的解決方案,故寫此文,以幫助像我一樣智商一般的孩子;最近趕項目很忙,等空了github 上傳Demo; 留言要Demo的,傳的會更快喲 !(這是有史以來我寫的最清楚的文章,相信聰明的你已經Get到了)