這篇文章要介紹的是李剖,在軟件啟動(dòng)的時(shí)候,呈現(xiàn)一個(gè)視頻播放功能囤耳,目前主流的社交App啟動(dòng)時(shí)都有這樣的功能篙顺,效果還蠻不錯(cuò)的崔慧。
目標(biāo)
這里要實(shí)現(xiàn)的功能是凑兰,第一次進(jìn)入軟件,啟動(dòng)時(shí)播放一段較長(zhǎng)的視頻湃窍,并且有進(jìn)入應(yīng)用按鈕,不點(diǎn)擊按鈕會(huì)一直循環(huán)播放視頻椎麦,直到點(diǎn)擊按鈕才會(huì)跳轉(zhuǎn)到應(yīng)用內(nèi)部;如果是第二次進(jìn)入軟件宰僧,則啟動(dòng)時(shí)播放一段較短的視頻,播放完成直接進(jìn)入到應(yīng)用內(nèi)部观挎,且沒有進(jìn)入應(yīng)用按鈕
細(xì)節(jié)實(shí)現(xiàn)
1.怎樣在啟動(dòng)時(shí)進(jìn)入視頻播放琴儿,在需要結(jié)束播放時(shí)退出視頻播放
軟件啟動(dòng)過程中,會(huì)最先顯示LaunchScreen.storyboard嘁捷,然后都會(huì)走AppDelegate中的方法:- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions造成。所以
- 可以創(chuàng)建一個(gè)用于視頻播放的控制器(下面說成AV控制器),在這個(gè)代理方法中將AV控制器雄嚣,設(shè)置為根控制器晒屎,這樣就可以進(jìn)入視頻播放了
- 在需要結(jié)束播放的地方(點(diǎn)擊事件或者播放完成),將需要顯示的控制器設(shè)置為窗口的根控制器缓升,這樣就可以退出了
2.流暢性能處理---視頻播放中斷和剛開始播放時(shí)插入圖片
- 從LaunchScreen.storyboard到視頻播放鼓鲁,會(huì)有一個(gè)間斷,在這個(gè)地方添加一張圖片港谊,圖片內(nèi)容就和LaunchScreen.storyboard中的一樣骇吭,然后LaunchScreen.storyboard中的圖片和視頻的第一幀一樣,這樣的話畫面看起來(lái)就比較流暢了
- 開始播放后封锉,會(huì)有一個(gè)自帶的通知--AVPlayerItemTimeJumpedNotification绵跷,添加觀察者,在觀察者的事件中成福,移除開始播放前插入的圖片
- 點(diǎn)擊進(jìn)入應(yīng)用按鈕碾局,視頻播放中斷,跳轉(zhuǎn)到應(yīng)用界面中間會(huì)有個(gè)停頓奴艾,在這里添加一個(gè)圖片净当,圖片就是當(dāng)前界面的截圖,只需要很短的時(shí)間即可蕴潦,這樣看起來(lái)就比較流暢了
3.第一次進(jìn)入和非第一次進(jìn)入軟件的處理
- 第一次進(jìn)入像啼,會(huì)有進(jìn)入應(yīng)用按鈕,并且潭苞,不點(diǎn)擊則循環(huán)播放視頻忽冻;而非第一次進(jìn)入,則直接播放另一個(gè)較短的視頻此疹,播放完成僧诚,則跳轉(zhuǎn)到應(yīng)用。根據(jù)這個(gè)區(qū)別蝗碎,添加一個(gè)布爾值的屬性湖笨,通過[NSUserDefaults standardUserDefaults]來(lái)記錄是否為第一次進(jìn)入應(yīng)用,然后進(jìn)行相關(guān)處理蹦骑。
- 視頻播放完成自帶有這個(gè)通知--AVPlayerItemDidPlayToEndTimeNotification慈省,添加觀察者,第一次進(jìn)入軟件時(shí)眠菇,視頻播放完成边败,在觀察者的事件中,再次播放視頻捎废;第二次進(jìn)入軟件時(shí)放闺,視頻播放完成,在觀察者的事件中缕坎,直接進(jìn)入應(yīng)用怖侦。
上代碼
創(chuàng)建視頻控制器AVPlayerVC,繼承自AVPlayerViewController,可能剛創(chuàng)建會(huì)報(bào)錯(cuò)谜叹,需要引入AVKit框架---#import <AVKit/AVKit.h>匾寝,就不會(huì)報(bào)錯(cuò)了
#import <AVKit/AVKit.h>
@interface AVPlayerVC : AVPlayerViewController
@end
在AppDelegate中引入AVPlayerVC控制器,并設(shè)置根控制器
- (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;
//播放中斷時(shí)的圖片
@property(nonatomic,strong)UIImageView * pausePlayerImageView;
//進(jìn)入應(yīng)用按鈕
@property(nonatomic,strong)UIButton * enterMainButton;
//是否第一次進(jìn)入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"
因?yàn)橛玫酵ㄖ衫埃筒シ牌餮藁冢韵仍赿ealloc方法中將其注銷
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (self.player) {
self.player = nil;
}
}
初始化視圖、添加通知女仰、初始化播放器
- (void)viewDidLoad {
[super viewDidLoad];
[self initView];
}
#pragma mark -- 初始化視圖
-(void)initView{
//添加一個(gè)圖片猜年,在視頻播放之前放一張圖片抡锈,這張圖片和LunchScreen.storyboard中的相同,這樣的話效果看起來(lái)連貫
self.startPlayerImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"lauch"]];
self.startPlayerImageView.frame = CGRectMake(0, 0, kScreenWidth, kScreenHeight);
[self.contentOverlayView addSubview:self.startPlayerImageView];
/*對(duì)于contentOverlayView 官方解釋是這樣的:A view displayed between the video content and the playback controls.*/
//第一次進(jìn)入軟件播放長(zhǎng)一點(diǎn)的視頻,并且?guī)в小斑M(jìn)入應(yīng)用的按鈕”乔外;第二次進(jìn)入軟件播放短一點(diǎn)的視頻床三,并且無(wú)進(jìn)入按鈕
self.isFirstLunchApp = [[NSUserDefaults standardUserDefaults] boolForKey:kIsFirstLunchApp];
if (!self.isFirstLunchApp) {//第一次啟動(dòng)軟件
//添加“進(jìn)入應(yīng)用”按鈕
[self addEnterButton];
}
//添加監(jiān)聽通知
[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:@"進(jìn)入應(yīng)用" forState:UIControlStateNormal];
[self.view addSubview:_enterMainButton];
[_enterMainButton addTarget:self action:@selector(enterMainAction:) forControlEvents:UIControlEventTouchUpInside];
_enterMainButton.hidden = YES;//先設(shè)置為隱藏,等過三秒的時(shí)間在顯示該按鈕杨幼,
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
_enterMainButton.hidden = NO;
});
}
//添加通知
-(void)addNotification{
//添加播放器的幾個(gè)通知--1.播放開始的時(shí)候撇簿,要?jiǎng)h掉開始的占位圖,如果是第一次進(jìn)入應(yīng)用差购,在沒有點(diǎn)擊“進(jìn)入應(yīng)用”時(shí)四瘫,需要循環(huán)播放
if (self.isFirstLunchApp) {
//第二次進(jìn)入app視頻需要直接結(jié)束
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlaybackComplete) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];//視頻播放結(jié)束時(shí)添加通知
}else {
//第一次進(jìn)入app視頻需要輪播
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlaybackAgain) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];//視頻播放結(jié)束時(shí)添加通知
}
//播放開始
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlaybackStart) name:AVPlayerItemTimeJumpedNotification object:nil];
}
//初始化播放器
-(void)prepareAV{
//首次運(yùn)行
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];
}
點(diǎn)擊事件和通知事件
#pragma mark -- 點(diǎn)擊事件及通知事件
//進(jìn)入按鈕點(diǎn)擊事件
-(void)enterMainAction:(UIButton*)sender{
//暫停播放
[self.player pause];
//添加一個(gè)imageView,用于放置暫停播放時(shí)的圖片
self.pausePlayerImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight)];
[self.contentOverlayView addSubview:self.pausePlayerImageView];
self.pausePlayerImageView.contentMode = UIViewContentModeScaleAspectFit;//設(shè)置
//截圖并展示截圖
[self getoverPlayerImage];
//播放結(jié)束要移除相關(guān)的對(duì)象
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self moviePlaybackComplete];
});
}
//結(jié)束播放刪除對(duì)應(yīng)的對(duì)象和注銷通知事件
-(void)moviePlaybackComplete{
//移除播放前的占位圖
[self.startPlayerImageView removeFromSuperview];
self.startPlayerImageView = nil;
//移除暫停播放的占位圖
[self.pausePlayerImageView removeFromSuperview];
self.pausePlayerImageView = nil;
//跳轉(zhuǎn)到新界面
[self pushToNewController];
}
//循環(huán)播放事件
-(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);
}
//跳轉(zhuǎn)到新的控制器
-(void)pushToNewController{
AppDelegate * appde = (AppDelegate*)[UIApplication sharedApplication].delegate;
UIViewController * mainC = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateInitialViewController];
appde.window.rootViewController = mainC;
[appde.window makeKeyWindow];
}