iOS 录制屏幕
思路1:http://blog.sina.com.cn/s/blog_6d64a8df01018i5n.html
思路总结:http://www.jianshu.com/p/be8040eb4106
录制屏幕操作,同时录制声音,合成视频存入相册:
https://github.com/Blazeice/ScreenAndAudioRecordDemoScreenAndAudioRecordDemo
本人在项目中先用ReplayKit实现了录屏幕逻辑,根据https://github.com/cdpenggod/CDPReplay
项目改动而来,多谢作者
录制代码奉上
//
// ULCDPReplay.h
// UXLive
//
// Created by zhangxiwei on 2017/6/20.
// Copyright © 2017年 UXIN CO. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <AssetsLibrary/ALAssetsLibrary.h>
#import <Photos/Photos.h>
#import <UIKit/UIKit.h>
#import <ReplayKit/ReplayKit.h>
typedef void(^ULReplayPreviewBlock)(BOOL isSuccess,RPPreviewViewController *viewController);
/**
录制回调delegate
*/
@protocol ULCDPReplayDelegate <NSObject>
@optional
/**
* 开始录制回调
*/
- (void)replayRecordStart;
/**
录制结束或错误回调
@param previewViewController 预览viewController
@param errorInfo 错误信息
*/
- (void)replayRecordFinishWithVC:(RPPreviewViewController *)previewViewController errorInfo:(NSString *)errorInfo;
/**
* 保存到系统相册成功回调
*/
- (void)saveSuccess;
@end
/**
系统版本需要是iOS9.0及以上才支持ReplayKit框架录制
*/
@interface ULCDPReplay : NSObject
/**
* 代理
*/
@property (nonatomic,weak) id <ULCDPReplayDelegate> delegate;
/**
* 是否正在录制
*/
@property (nonatomic,assign,readonly) BOOL isRecording;
/**
* 单例对象
*/
+ (instancetype)sharedReplay;
/**
开始录制
@param block 回调
*/
- (void)startRecordBlock:(void(^)(BOOL successed, NSString *msg))block;
/**
结束录制
@param isShow isShow 是否录制完后自动展示视频预览页
@param finishBlock 完成回调
@param previewBlock 预览回调
*/
- (void)stopRecordAndShowVideoPreviewController:(BOOL)isShow
finishBlock:(void(^)(BOOL successed, NSURL *videoUrl ,NSString *msg))finishBlock
previewBlock:(ULReplayPreviewBlock)previewBlock;
/**
取消录制
*/
- (void)cancelRecord;
@end
//
// ULCDPReplay.m
// UXLive
//
// Created by zhangxiwei on 2017/6/20.
// Copyright © 2017年 UXIN CO. All rights reserved.
//
#import "ULCDPReplay.h"
#import "ULAssertUtil.h"
@interface ULCDPReplay () <RPPreviewViewControllerDelegate>
@property (nonatomic, strong) NSDate *createDate;
@end
@implementation ULCDPReplay
/**
单例化对象
@return 实例
*/
+ (instancetype)sharedReplay {
static ULCDPReplay *replay=nil;
static dispatch_once_t token;
dispatch_once(&token, ^{
replay=[[ULCDPReplay alloc] init];
});
return replay;
}
/**
是否正在录制
@return 是否
*/
- (BOOL)isRecording {
return [RPScreenRecorder sharedRecorder].recording;
}
#pragma mark - 开始/结束录制
- (void)startRecordBlock:(void (^)(BOOL success, NSString *message))block {
self.createDate = [NSDate date];
if ([RPScreenRecorder sharedRecorder].recording==YES) {
ULLogInfo(@"CDPReplay:已经开始录制");
return;
}
if ([self systemVersionOK]) {
if ([[RPScreenRecorder sharedRecorder] isAvailable]) {
ULLogInfo(@"CDPReplay:录制开始初始化");
[ULToast showProgress:@"录屏初始化中..."];
[[RPScreenRecorder sharedRecorder] startRecordingWithMicrophoneEnabled:YES handler:^(NSError *error){
dispatch_main_sync_ulsafe(^{
[ULToast hideProgress];
});
if (error) {
ULLogInfo(@"CDPReplay:开始录制error %@",error);
[[RPScreenRecorder sharedRecorder] discardRecordingWithHandler:^{
ULLogInfo(@"CDPReplay:discardRecordingWithHandler");
}];
if (block) {
block(NO, @"录制失败,请重试");
}
if ([_delegate respondsToSelector:@selector(replayRecordFinishWithVC:errorInfo:)]) {
[_delegate replayRecordFinishWithVC:nil errorInfo:[NSString stringWithFormat:@"CDPReplay:开始录制error %@",error]];
}
} else {
ULLogInfo(@"CDPReplay:开始录制");
if (block) {
block(YES, @"开始录制");
}
if ([_delegate respondsToSelector:@selector(replayRecordStart)]) {
[_delegate replayRecordStart];
}
}
}];
} else {
if (block) {
block(NO, @"环境不支持屏幕录制");
}
ULLogInfo(@"CDPReplay:环境不支持ReplayKit录制");
if ([_delegate respondsToSelector:@selector(replayRecordFinishWithVC:errorInfo:)]) {
[_delegate replayRecordFinishWithVC:nil errorInfo:@"CDPReplay:环境不支持ReplayKit录制"];
}
}
} else {
ULLogInfo(@"CDPReplay:系统版本需要是iOS9.0及以上才支持ReplayKit录制");
if (block) {
block(NO, @"系统版本需要是iOS9.0及以上才支持屏幕录制");
}
if ([_delegate respondsToSelector:@selector(replayRecordFinishWithVC:errorInfo:)]) {
[_delegate replayRecordFinishWithVC:nil errorInfo:@"CDPReplay:系统版本需要是iOS9.0及以上才支持ReplayKit录制"];
}
}
}
- (void)stopRecordAndShowVideoPreviewController:(BOOL)isShow
finishBlock:(void (^)(BOOL, NSURL *, NSString *))finishBlock
previewBlock:(ULReplayPreviewBlock)previewBlock {
ULLogInfo(@"CDPReplay:正在结束录制");
WS(weakSelf)
[[RPScreenRecorder sharedRecorder] stopRecordingWithHandler:^(RPPreviewViewController *previewViewController, NSError * error){
if (error) {
ULLogInfo(@"CDPReplay:结束录制error %@", error);
[[RPScreenRecorder sharedRecorder] discardRecordingWithHandler:^{
ULLogInfo(@"CDPReplay:discardRecordingWithHandler");
}];
if ([_delegate respondsToSelector:@selector(replayRecordFinishWithVC:errorInfo:)]) {
[_delegate replayRecordFinishWithVC:nil errorInfo:[NSString stringWithFormat:@"CDPReplay:结束录制error %@",error]];
}
if (finishBlock) {
finishBlock(NO, nil, @"录制失败,请稍后重试");
}
} else {
ULLogInfo(@"CDPReplay:录制完成");
if ([_delegate respondsToSelector:@selector(replayRecordFinishWithVC:errorInfo:)]) {
[_delegate replayRecordFinishWithVC:previewViewController errorInfo:@""];
}
[previewViewController setPreviewControllerDelegate:weakSelf];
if (ULOSVersionFloat >= 9.0f && ULOSVersionFloat < 10.0f) {
if (previewBlock) {
previewBlock(YES, previewViewController);
}
} else {
NSURL *movieURL = [previewViewController valueForKey:@"movieURL"];
ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc]init];
[assetsLibrary writeVideoAtPathToSavedPhotosAlbum:movieURL completionBlock:^(NSURL *assetURL, NSError *error) {
if (error) {
ULLogInfo(@"CDPReplay 保存失败 %@ %@", error, assetURL);
if (finishBlock) {
finishBlock(NO, assetURL, @"录制成功");
}
} else {
ULLogInfo(@"CDPReplay 保存成功 %@", assetURL);
if (finishBlock) {
finishBlock(YES, assetURL, @"录制成功");
}
}
}];
}
}
}];
}
- (void)cancelRecord {
ULLogInfo(@"CDPReplay 取消录制");
[[RPScreenRecorder sharedRecorder] stopRecordingWithHandler:^(RPPreviewViewController *previewViewController, NSError * error){
if (error) {
ULLogInfo(@"CDPReplay cancelRecord:结束录制error %@", error);
} else {
ULLogInfo(@"CDPReplay cancelRecord:录制完成");
}
[[RPScreenRecorder sharedRecorder] discardRecordingWithHandler:^{
}];
}];
}
#pragma mark - 其他方法
/**
判断对应系统版本是否支持ReplayKit
@return 是否支持系统
*/
- (BOOL)systemVersionOK {
if (IS_IOS9_OR_LATER) {
return YES;
} else {
return NO;
}
}
@end
注意点:
- 系统版本在iOS10 以上
NSURL *movieURL = [previewViewController valueForKey:@"movieURL"];
通过私有变量能获取到存储地址,可以直接读取视频地址了,但是存在几个坑,各位有好办法也可以同步,我直接拿地址操作时不行的,以为地址的目录是在ReplayKit的一个目录下,个人理解应该是跨应用了,数据保护之类的(猜测),我做了一把直接存储到相册,然后再从相册里面读取就能对文件进行操作了。
- 系统 iOS9~iOS 10包含iOS9
该系统下通过previewViewController通过上述方法是找不到的,打印一下私有属性就能证明,只能通过提供的previewViewController来保存做操作。我的方法是引导用户去点击存储,然后通过点击存储回调从相册里面获取最新的文件,(坑出现)虽然给的回调是存储回调,但是一般情况下这个时候文件还没有存储成功,我延迟1s后获取。因为我们一般都是限制60s以内的视频,所以一般都没有问题。
获取最新的asset
+ (PHAsset *)latestAsset {
// 获取所有资源的集合,并按资源的创建时间排序
PHFetchOptions *options = [[PHFetchOptions alloc] init];
options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]];
options.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld",
PHAssetMediaTypeVideo];
PHFetchResult *assetsFetchResults = [PHAsset fetchAssetsWithOptions:options];
return [assetsFetchResults firstObject];
}
上一篇: python 实现屏幕录制
下一篇: Maven系列——超简单入门级教程