这篇文章要介绍的是,在软件启动的时候,呈现一个视频播放功能,目前主流的社交App启动时都有这样的功能,效果还蛮不错的。
目标
这里要实现的功能是,第一次进入软件,启动时播放一段较长的视频,并且有进入应用按钮,不点击按钮会一直循环播放视频,直到点击按钮才会跳转到应用内部;如果是第二次进入软件,则启动时播放一段较短的视频,播放完成直接进入到应用内部,且没有进入应用按钮
细节实现
1.怎样在启动时进入视频播放,在需要结束播放时退出视频播放
软件启动过程中,会最先显示LaunchScreen.storyboard,然后都会走AppDelegate中的方法:- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions。所以
- 可以创建一个用于视频播放的控制器(下面说成AV控制器),在这个代理方法中将AV控制器,设置为根控制器,这样就可以进入视频播放了
- 在需要结束播放的地方(点击事件或者播放完成),将需要显示的控制器设置为窗口的根控制器,这样就可以退出了
2.流畅性能处理---视频播放中断和刚开始播放时插入图片
- 从LaunchScreen.storyboard到视频播放,会有一个间断,在这个地方添加一张图片,图片内容就和LaunchScreen.storyboard中的一样,然后LaunchScreen.storyboard中的图片和视频的第一帧一样,这样的话画面看起来就比较流畅了
- 开始播放后,会有一个自带的通知--AVPlayerItemTimeJumpedNotification,添加观察者,在观察者的事件中,移除开始播放前插入的图片
- 点击进入应用按钮,视频播放中断,跳转到应用界面中间会有个停顿,在这里添加一个图片,图片就是当前界面的截图,只需要很短的时间即可,这样看起来就比较流畅了
3.第一次进入和非第一次进入软件的处理
- 第一次进入,会有进入应用按钮,并且,不点击则循环播放视频;而非第一次进入,则直接播放另一个较短的视频,播放完成,则跳转到应用。根据这个区别,添加一个布尔值的属性,通过[NSUserDefaults standardUserDefaults]来记录是否为第一次进入应用,然后进行相关处理。
- 视频播放完成自带有这个通知--AVPlayerItemDidPlayToEndTimeNotification,添加观察者,第一次进入软件时,视频播放完成,在观察者的事件中,再次播放视频;第二次进入软件时,视频播放完成,在观察者的事件中,直接进入应用。
上代码
创建视频控制器AVPlayerVC,继承自AVPlayerViewController,可能刚创建会报错,需要引入AVKit框架---#import <AVKit/AVKit.h>,就不会报错了
#import <AVKit/AVKit.h>
@interface AVPlayerVC : AVPlayerViewController
@end
在AppDelegate中引入AVPlayerVC控制器,并设置根控制器
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.rootViewController = [[AVPlayerVC alloc] init];
[self.window makeKeyAndVisible];
// Override point for customization after application launch.
return YES;
}
在视频播放器中添加属性
@interface AVPlayerVC ()
//播放开始之前的图片
@property(nonatomic,strong)UIImageView * startPlayerImageView;
//播放中断时的图片
@property(nonatomic,strong)UIImageView * pausePlayerImageView;
//进入应用按钮
@property(nonatomic,strong)UIButton * enterMainButton;
//是否第一次进入App
@property(nonatomic,assign)BOOL isFirstLunchApp;
@end
用到的宏和头文件
#import <AVFoundation/AVFoundation.h>
#import "ViewController.h"
#import "AppDelegate.h"
#define kScreenHeight [UIScreen mainScreen].bounds.size.height
#define kScreenWidth [UIScreen mainScreen].bounds.size.width
#define kIsFirstLunchApp @"isFirstLunchApp"
因为用到通知,和播放器,所以先在dealloc方法中将其注销
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (self.player) {
self.player = nil;
}
}
初始化视图、添加通知、初始化播放器
- (void)viewDidLoad {
[super viewDidLoad];
[self initView];
}
#pragma mark -- 初始化视图
-(void)initView{
//添加一个图片,在视频播放之前放一张图片,这张图片和LunchScreen.storyboard中的相同,这样的话效果看起来连贯
self.startPlayerImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"lauch"]];
self.startPlayerImageView.frame = CGRectMake(0, 0, kScreenWidth, kScreenHeight);
[self.contentOverlayView addSubview:self.startPlayerImageView];
/*对于contentOverlayView 官方解释是这样的:A view displayed between the video content and the playback controls.*/
//第一次进入软件播放长一点的视频,并且带有“进入应用的按钮”;第二次进入软件播放短一点的视频,并且无进入按钮
self.isFirstLunchApp = [[NSUserDefaults standardUserDefaults] boolForKey:kIsFirstLunchApp];
if (!self.isFirstLunchApp) {//第一次启动软件
//添加“进入应用”按钮
[self addEnterButton];
}
//添加监听通知
[self addNotification];
//初始化视频
[self prepareAV];
}
-(void)addEnterButton{
self.enterMainButton = [UIButton buttonWithType:UIButtonTypeCustom];
_enterMainButton.frame = CGRectMake(24, kScreenHeight - 32 - 48, kScreenWidth - 48, 48);
_enterMainButton.layer.borderWidth =1;
_enterMainButton.layer.cornerRadius = 24;
_enterMainButton.layer.borderColor = [UIColor whiteColor].CGColor;
[_enterMainButton setTitle:@"进入应用" forState:UIControlStateNormal];
[self.view addSubview:_enterMainButton];
[_enterMainButton addTarget:self action:@selector(enterMainAction:) forControlEvents:UIControlEventTouchUpInside];
_enterMainButton.hidden = YES;//先设置为隐藏,等过三秒的时间在显示该按钮,
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
_enterMainButton.hidden = NO;
});
}
//添加通知
-(void)addNotification{
//添加播放器的几个通知--1.播放开始的时候,要删掉开始的占位图,如果是第一次进入应用,在没有点击“进入应用”时,需要循环播放
if (self.isFirstLunchApp) {
//第二次进入app视频需要直接结束
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlaybackComplete) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];//视频播放结束时添加通知
}else {
//第一次进入app视频需要轮播
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlaybackAgain) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];//视频播放结束时添加通知
}
//播放开始
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlaybackStart) name:AVPlayerItemTimeJumpedNotification object:nil];
}
//初始化播放器
-(void)prepareAV{
//首次运行
NSString *filePath = nil;
if (!self.isFirstLunchApp) {//没有值,说明是第一次
//第一次安装
filePath = [[NSBundle mainBundle] pathForResource:@"opening_long_1080*1920.mp4" ofType:nil];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:kIsFirstLunchApp];
}else {
filePath = [[NSBundle mainBundle] pathForResource:@"opening_short_1080*1920.mp4" ofType:nil];
}
//初始化player
self.player = [AVPlayer playerWithURL:[NSURL fileURLWithPath:filePath]];
self.showsPlaybackControls = NO;
//播放视频
[self.player play];
}
点击事件和通知事件
#pragma mark -- 点击事件及通知事件
//进入按钮点击事件
-(void)enterMainAction:(UIButton*)sender{
//暂停播放
[self.player pause];
//添加一个imageView,用于放置暂停播放时的图片
self.pausePlayerImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight)];
[self.contentOverlayView addSubview:self.pausePlayerImageView];
self.pausePlayerImageView.contentMode = UIViewContentModeScaleAspectFit;//设置
//截图并展示截图
[self getoverPlayerImage];
//播放结束要移除相关的对象
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self moviePlaybackComplete];
});
}
//结束播放删除对应的对象和注销通知事件
-(void)moviePlaybackComplete{
//移除播放前的占位图
[self.startPlayerImageView removeFromSuperview];
self.startPlayerImageView = nil;
//移除暂停播放的占位图
[self.pausePlayerImageView removeFromSuperview];
self.pausePlayerImageView = nil;
//跳转到新界面
[self pushToNewController];
}
//循环播放事件
-(void)moviePlaybackAgain{
//添加播放前的占位图
self.startPlayerImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"lauchAgain"]];
_startPlayerImageView.frame = CGRectMake(0, 0, kScreenWidth, kScreenHeight);
[self.contentOverlayView addSubview:_startPlayerImageView];
[self.pausePlayerImageView removeFromSuperview];
self.pausePlayerImageView = nil;
//初始化player
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"opening_long_1080*1920.mp4" ofType:nil];
self.player = [AVPlayer playerWithURL:[NSURL fileURLWithPath:filePath]];
self.showsPlaybackControls = NO;
//播放视频
[self.player play];
}
//开始播放通知事件
- (void)moviePlaybackStart {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.startPlayerImageView removeFromSuperview];
self.startPlayerImageView = nil;
});
}
私有方法
#pragma mark -- 私有方法
//获取截图
- (void)getoverPlayerImage {
AVAssetImageGenerator *gen = [[AVAssetImageGenerator alloc] initWithAsset:self.player.currentItem.asset];
gen.appliesPreferredTrackTransform = YES;
NSError *error = nil;
CMTime actualTime;
CMTime now = self.player.currentTime;
[gen setRequestedTimeToleranceAfter:kCMTimeZero];
[gen setRequestedTimeToleranceBefore:kCMTimeZero];
CGImageRef image = [gen copyCGImageAtTime:now actualTime:&actualTime error:&error];
if (!error) {
UIImage *thumb = [[UIImage alloc] initWithCGImage:image];
self.pausePlayerImageView.image = thumb;
}
NSLog(@"%f , %f",CMTimeGetSeconds(now),CMTimeGetSeconds(actualTime));
NSLog(@"%@",error);
}
//跳转到新的控制器
-(void)pushToNewController{
AppDelegate * appde = (AppDelegate*)[UIApplication sharedApplication].delegate;
UIViewController * mainC = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateInitialViewController];
appde.window.rootViewController = mainC;
[appde.window makeKeyWindow];
}