
問題回放
如果我們在一個iOS項目中使用到了視頻播放器就難免會遇到強制橫豎屏的問題.這個老生常談的問題我們只需要監(jiān)聽設備的方向改變的通知即可,一般情況是不會出現(xiàn)什么的問題的.但是事實真的是這樣嗎?網(wǎng)上在這方面的資料也是雜亂不堪,大部分只是說說如何進行強制橫豎屏,沒有進一步說明動畫操作,雖然大部分的情況是沒有任何問題的,可是當用戶快速的從左橫屏→豎屏→右橫屏的時候就會出現(xiàn)PlayView的尺寸顯示不正確的問題了.原因是當左橫屏→豎屏的動畫還未完成時(動畫過程需要0.3s),設備已經(jīng)完成了從豎屏到右豎屏的過程了.導致兩個動畫同時執(zhí)行,界面就出現(xiàn)問題.當然了,還有如下的問題也是類似的.都是尺寸縮放動畫錯亂造成的!
錯誤代碼如下
#define KmainWidth [UIScreen mainScreen].bounds.size.width
#define KmainHeight [UIScreen mainScreen].bounds.size.height
//添加監(jiān)聽
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(rotateScreenViews:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
#pragma mark - 屏幕旋轉(zhuǎn)的主要動畫操作方法
- (void)rotateScreenViews:(NSObject *)sender{
UIDevice* device = [sender valueForKey:@"object"];
if (device.orientation == UIDeviceOrientationPortrait ) {
self.playView.frame = CGRectMake(0, 0, KmainWidth, KmainWidth/16.0*9);
}
if (device.orientation == UIDeviceOrientationLandscapeLeft ) {
self.playView.frame = CGRectMake(0, 0, KmainWidth, KmainHeight);
}
if (device.orientation == UIDeviceOrientationLandscapeRight ) {
self.playView.frame = CGRectMake(0, 0, KmainWidth, KmainHeight);
}
}
* 正常情況(此時寬高比:16:9)

* 動畫執(zhí)行錯亂情況(此時寬高比:橫屏情況下的寬高比)
解決思路
上面說到的這種問題我們該怎么解決呢?其實很簡單,我們來自定義動畫.通過布爾值(isPortraitFinished)來監(jiān)控屏幕旋轉(zhuǎn)的動畫是否完成,如果完成我們才執(zhí)行接下來的動畫操作,或者結(jié)束操作.而且這里我加入了另外的一個布爾值(isFullScreen)來減少執(zhí)行動畫的次數(shù).具體代碼如下所示.
在.m的類擴展中有以下屬性.
#define KmainWidth [UIScreen mainScreen].bounds.size.width
#define KmainHeight [UIScreen mainScreen].bounds.size.height
@interface FullScreenViewController ()
@property(nonatomic,strong)UILabel *playView;
@property(nonatomic,assign)BOOL isFullScreen;//是否是全屏,減少動畫執(zhí)行次數(shù)
@property(nonatomic,assign)BOOL isPortraitFinished;//是否已經(jīng)完成了豎屏.
@end
viewDidLoad主要是來進行視頻View的初始化,注冊旋轉(zhuǎn)通知等,如下所示.
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.playView];
self.isPortraitFinished = YES;//這個要開始設置為YES,不管在橫屏還是豎屏的情況下都可以適應.
//注冊橫豎屏的通知
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(rotateScreenViews:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
}
- (UILabel *)playView{
if (_playView == nil) {
_playView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, KmainWidth, KmainWidth/16.0*9)];
_playView.backgroundColor = [UIColor lightGrayColor];
_playView.text = @"播放內(nèi)容";
_playView.font = [UIFont systemFontOfSize:18];
_playView.textAlignment = NSTextAlignmentCenter;
}
return _playView;
}
在viewWillAppear中先設定屏幕的設備方向為豎屏方向,如下所示.
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
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 = UIInterfaceOrientationPortrait;
[invocation setArgument:&val atIndex:2];
[invocation invoke];
}
}
現(xiàn)在正題來了,我們的旋轉(zhuǎn)方法相比于問題回放中代碼有何改變和優(yōu)化呢? 這里首先進行第一步通知回調(diào)方法的實現(xiàn),代碼如下所示.
- (void)rotateScreenViews:(NSObject *)sender{
UIDevice* device = [sender valueForKey:@"object"];
if (device.orientation == UIDeviceOrientationPortrait && _isFullScreen) {
CGFloat duration = [UIApplication sharedApplication].statusBarOrientationAnimationDuration;//時間
_isFullScreen = NO;
[UIView animateWithDuration:duration animations:^{
CGFloat nowWidth = 0;
if (KmainWidth > KmainHeight) {
nowWidth = KmainHeight;
}else{
nowWidth = KmainWidth;
}
NSLog(@"豎進行");
self.playView.frame = CGRectMake(0, 0, nowWidth, nowWidth/16.0*9);
self.isPortraitFinished = NO;
} completion:^(BOOL finished) {
NSLog(@"豎完成");
self.isPortraitFinished = YES;
[self portraitFinishaNextAction];
}];
}
if (device.orientation == UIDeviceOrientationLandscapeLeft && !_isFullScreen) {
CGFloat duration = [UIApplication sharedApplication].statusBarOrientationAnimationDuration;//時間
_isFullScreen = YES;
[UIView animateWithDuration:duration animations:^{
if (self.isPortraitFinished != NO) {
self.playView.frame = CGRectMake(0, 0, KmainWidth, KmainHeight);
NSLog(@"左右進行");
}
}completion:^(BOOL finished) {
NSLog(@"左右完成");
}];
}
if (device.orientation == UIDeviceOrientationLandscapeRight && !_isFullScreen) {
CGFloat duration = [UIApplication sharedApplication].statusBarOrientationAnimationDuration;//時間
_isFullScreen = YES;
[UIView animateWithDuration:duration animations:^{
if (self.isPortraitFinished != NO) {
self.playView.frame = CGRectMake(0, 0, KmainWidth, KmainHeight);
NSLog(@"左右進行");
}
} completion:^(BOOL finished) {
}];
}
}
在上一方法中,我們通過 isPortraitFinished 屬性來隔斷動畫的執(zhí)行.然后用戶快速的旋轉(zhuǎn)屏幕的話,我們會在第二個方法( portraitFinishaNextAction )中處理這種情況,當豎屏動畫完成之后,我們還要判斷當前設備的方向是否是豎屏,如果是,那么沒有動畫執(zhí)行,如果不是的話,那么我們就再次執(zhí)行旋轉(zhuǎn)動畫.代碼如下所示.
//豎屏完成之后判斷現(xiàn)在的手機設備的方向,如果是橫豎屏,那么需要再次進行動畫操作.
-(void)portraitFinishaNextAction{
UIDeviceOrientation orient = [UIDevice currentDevice].orientation;
if (orient == UIDeviceOrientationLandscapeLeft || orient == UIDeviceOrientationLandscapeRight) {
CGFloat duration = [UIApplication sharedApplication].statusBarOrientationAnimationDuration;//時間
[UIView animateWithDuration:duration delay:duration options:UIViewAnimationOptionLayoutSubviews animations:^{
if (self.isPortraitFinished != NO) {
self.playView.frame = self.playView.frame;
}
} completion:^(BOOL finished) {
self.playView.frame = CGRectMake(0, 0, KmainWidth, KmainHeight);
}];
}
}
好了,這里就完成我們的屏幕旋轉(zhuǎn)動畫的優(yōu)化處理了,Demo鏈接地址會在文章尾部給出.
屏幕旋轉(zhuǎn)的補充說明
這里有幾點需要補充的,假定當期的播放頁面可以在橫屏的情況下進行返回上一個頁面的操作.這時候我們需要先旋轉(zhuǎn)屏幕,我們該如何實現(xiàn)呢?我們只需要在viewWillDisappear中寫入如下代碼即可.
//假設有上一級的頁面 我們通過下面代碼來進行豎屏切換,如果沒有 去掉就好
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
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 = UIInterfaceOrientationPortrait;
[invocation setArgument:&val atIndex:2];
[invocation invoke];
}
}
第二補充點就是我們?nèi)绻?strong>鎖屏操作怎么做才合適呢?比如我現(xiàn)在的項目中就同時有直播和點播,難道我要寫兩套代碼嗎?不,我們只需要在AppDelegate中設定設備是否可以旋轉(zhuǎn),以及設備旋轉(zhuǎn)的方向,這里的值我們可以隨意設定代碼如下所示.
AppDelegate.h中的屬性代碼
@property (nonatomic,assign)NSInteger allowRotate;
AppDelegate.m中的實現(xiàn)代碼
#pragma mark - 設備是否可以旋轉(zhuǎn)
//此方法會在設備橫豎屏變化的時候調(diào)用
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
if (_allowRotate == 111) {
return UIInterfaceOrientationMaskLandscape;
}
if (_allowRotate == 999) {
return UIInterfaceOrientationMaskLandscapeRight;
}
if (_allowRotate == 666) {
return UIInterfaceOrientationMaskLandscapeLeft;
}
if (_allowRotate == 333) {
return UIInterfaceOrientationMaskPortrait;
}
if (_allowRotate == 1) {
return UIInterfaceOrientationMaskLandscape | UIInterfaceOrientationMaskPortrait;
}else{
return (UIInterfaceOrientationMaskPortrait);
}
}
// 返回是否支持設備自動旋轉(zhuǎn)
- (BOOL)shouldAutorotate
{
if (_allowRotate == 999 || _allowRotate == 666) {
return NO;
}
if (_allowRotate == 1) {
return YES;
}
return NO;
}
這樣我們只需要操作AppDelegate中的allowRotate屬性即可.
結(jié)語
好了,這一次分享就到這里了.如果喜歡的話,歡迎點贊收藏,當然了,如果有任何問題都可以聯(lián)系騷棟.或者在評論區(qū)提出來,我會及時回復的,謝謝.最后還是老慣例,附上實現(xiàn)代碼的傳送門.
FullScreenDemo傳送門??
