前言
前段时间接手新华社的项目时,其实也是有接触到视频列表这样的需求的,但毕竟那个时候比较赶,为了能确保性能和功能,就做成直接点击全屏竖屏播放的形式。但是纵观各大新闻应用,自媒体客户端都不会这么直接粗暴,而如今自己公司也有这样的一个需求,所以现在就着手认真思考这样一个需求的各种实现细节。
具体需求
- 点击播放之后能能够在cell中播放而不是跳全屏
- 用户主动点击全屏之后,变成横屏播放
- 当播放视频的cell离开了显示屏,就停止播放
- 任何时候只有一个视频被播放
技术点实现
横竖屏的随时切换
说到横竖的问题,就很自然会考虑app的一个代理方法
- (UIInterfaceOrientationMask)application:(UIApplication *)application
supportedInterfaceOrientationsForWindow:(nullable UIWindow *)window;
这个方法在官方文档中这样描述的
When determining whether to rotate a particular view controller, the orientations returned by this method are intersected with the orientations supported by the root view controller or topmost presented view controller. The app and view controller must agree before the rotation is allowed.
就说明了这个方法会在呈现一个新的视图控制时被调用(经实验证实,是会被调用,而且会被调用很多次),当视图控制器完全呈现时停止调用。这个并不是一个意图去主动旋转屏幕的方法,只是声明当时的视图支持以什么方向呈现(同时也侧面不支持什么方向的呈现),因此我们在使用其它方法旋转屏幕时,一定要通过这个方法的支持才能使用,例如这个方法
[[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIInterfaceOrientationLandscapeRight]
forKey:@"orientation"];
对于应用中有很多页面有不同方向的呈现可以有更一般的实现
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
-(BOOL)shouldAutorotate;
-(NSUInteger)supportedInterfaceOrientations;
在官方文档是这样描述
When the user changes the device orientation, the system calls this method on the root view controller or the topmost presented view controller that fills the window. If the view controller supports the new orientation, the window and view controller are rotated to the new orientation. This method is only called if the view controller’s shouldAutorotate method returns YES.
就是说当用户主动去旋转屏幕时就会调用这个方法检验是否能旋转,在使用这些方法时一定要注意这些方法的返回值要和parentController的返回值一致才行,因为parent(或parent的parent)在这个情况下才最具话语权。
结合上诉关于旋转屏幕的方法和我们的需求(不需要用户主动横屏,全屏时自动横屏),最后我采取的方案是,建立一个单例专门去自动控制横竖屏的
单例头文件
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface Tool : NSObject
@property(nonatomic,assign)int flag;
+ (Tool *)sharedClient;
-(UIInterfaceOrientationMask)interfaceOrientationMask;
@end
单例实现文件
#import "Tool.h"
@implementation Tool
+ (Tool *)sharedClient
{
static Tool *client;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
client = [[Tool alloc] init];
});
return client;
}
-(UIInterfaceOrientationMask)interfaceOrientationMask {
if (self.flag == 1) {
return UIInterfaceOrientationMaskLandscape;
}else {
return UIInterfaceOrientationMaskPortrait;
}
}
@end
AppDelegate
-(UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
return [Tool sharedClient].interfaceOrientationMask;
}
如此只要在新的控制器出现前改变单例的flag的可以改变新出现的控制器的方向,甚至可以在控制器出现之后结合其它方法随时旋屏。
播放器非全屏播放
对于播放器播放的界面大小无非就是下面几个方法确定
_moviePlayer = [[MPMoviePlayerController alloc] init];
[[_moviePlayer view] setFrame:self.frame];
_moviePlayer.contentURL = [NSURL URLWithString:_movieURL];
_moviePlayer.scalingMode = MPMovieScalingModeAspectFit;
[_moviePlayer setControlStyle:MPMovieControlStyleEmbedded];
最后把播放器的view加入到cell里面或者是其它的view中,就可以完成嵌入播放器了,而对于播放器的各种状态的回调,用通知方法即可,最后一定要记得remove通知,不然会造成内存泄漏。
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(moviePlayerLoadStateChanged:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
//视频结束
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
//进入全屏
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(moviePlayEnterFullscreen:)
name:MPMoviePlayerDidEnterFullscreenNotification
object:nil];
//退出全屏
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(moviePlayExitFullscreen:)
name:MPMoviePlayerWillExitFullscreenNotification
object:nil];
计算TableView中的播放器在控制器View中位置
播放视频的cell离开了显示屏,就立刻停止播放,这样的需求就必须要计算TableView中的播放器在控制器View中位置,我们很容易联想到了tableView的父类scrollView的代理方法,最后实现如下:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
if (!indexPaths) {
return;
}
CGRect rectInTableView = [_tableView_mv rectForRowAtIndexPath:indexPaths];
//计算播放视频的cell的frame
CGRect rectInSuperview = [_tableView_mv convertRect:rectInTableView toView:[_tableView_mv superview]];
//计算这个cell在控制器view的位置
if (rectInSuperview.origin.y <= -rectInTableView.size.height || rectInSuperview.origin.y >= KDeviceHeight) {
[movieView.moviePlayer stop];
}
}