iOS开发常用iOS之Swift框架理解经验demo

iOS-你不知道的系统录屏框架-ReplayKit

2016-08-10  本文已影响6307人  清無

前段时间公司项目有个要求,让找一个第三方的录屏框架,实现画板绘画过程的录制,github找了好久,但没有让人十分满意又符合项目要求的,所以就研究了下系统提供的ReplayKit,这里整理下,发出来,用到的书友们可以借鉴学习。

1. ReplayKit相关的class和protocol

* classes:

RPPreviewViewController - 录制完成预览vc

The RPPreviewViewController class displays a user interface that allows users to preview and edit a screen recording created with ReplayKit. The preview view controller is passed into the completion handler for stopRecordingWithHandler: upon a successful recording.

RPScreenRecorder - 真正录屏的类

Use the RPScreenRecorder class to implement the ability to record audio and video of your app.

* protocols:

RPPreviewViewControllerDelegate - 录制完成预览vc的代理回调协议 - 录制完成预览页面

Implement the RPPreviewViewControllerDelegate protocol to respond to changes to a screen recording user interface, represented by a RPPreviewViewController object.

RPScreenRecorderDelegate - 录屏功能回调协议 - 录屏开始 / 结束 / 出错接受通知

Implement the RPScreenRecorderDelegate protocol to receive notifications from an RPScreenRecorder object. The delegate is called when recording stops or there is a change in recording availability.

2.实现思路

简单实现流程

3.具体实现过程 - 关键代码

1. 录制前的检测: 设备是否是真机、iOS版本是否>9.0、录屏硬件是否可用
    // 真机模拟器检测
    struct Platform {
        static let isSimulator: Bool = {
            var isSim = false
            #if arch(i386) || arch(x86_64)
                isSim = true
            #endif
            return isSim
        }()
    }

    // Alert提示框
    private func showUnsuportAlert(message: String, showVC: UIViewController){
        let ok = UIAlertAction(title: "好的", style: .Default) { (action) in
        }
        let alert = UIAlertController(title: nil, message: message, preferredStyle: .Alert)
        alert.addAction(ok)
        showVC .presentViewController(alert, animated: true, completion: nil)
    }

    // 是真机还是模拟器
    func isPlatformSuport(showVC: UIViewController) -> Bool{
        if Platform.isSimulator {
            showUnsuportAlert("录屏功能只支持真机设备", showVC: showVC)
            return false
        }
        return true
    }
    
    // 系统版本是9.0
    func isSystemVersionSuport(showVC: UIViewController) -> Bool{
        if NSString(string: UIDevice.currentDevice().systemVersion).floatValue < 9.0 {
            showUnsuportAlert("录屏功能要求系统版本9.0以上", showVC: showVC)
            return false
        }
        return true
    }
    
    // 检查是否可用
    func isRecorderAvailable(showVC: UIViewController) -> Bool {
        if !RPScreenRecorder.sharedRecorder().available{
            showUnsuportAlert("录屏功能不可用", showVC: showVC)
            return false
        }
        return true
    }
2. 录制功能类: 开始、结束
    // 开始
    func startCapture(){
        print("录屏初始化...")
        let recorder = RPScreenRecorder.sharedRecorder()
        recorder.delegate = self
        // 关键方法
        recorder.startRecordingWithMicrophoneEnabled(false) { (error) in
            print("录屏开始")
            self.delegate?.didStartRecord()
            if let error = error {
                self.delegate?.didStopWithError(error)
            }
        }
    }
    
    // 完成
    func stopCapture(){
        let recorder = RPScreenRecorder.sharedRecorder()
        // 关键方法
        recorder.stopRecordingWithHandler { (previewController, error) in
            if let error = error {
                self.delegate?.didStopWithError(error)
            } else if let preview = previewController{
                print("录屏完成")
                preview.previewControllerDelegate = self
                self.delegate?.didFinishRecord(preview)
                print("显示预览页面...")
            }
        }
    }
    
    // 自定义录屏manager协议   
    protocol ScreenCaptureManagerDelegate: class{
        func didStartRecord() // 正在录制
        func didFinishRecord(preview: UIViewController) // 完成录制
        func didStopWithError(error: NSError) //发生错误停止录制
        func savingRecord() // 保存
        func discardingRecord() // 取消保存
    }
3. 代理方法: RPPreviewViewControllerDelegate, RPScreenRecorderDelegate
extension ScreenCaptureManager: RPScreenRecorderDelegate{
    // 录制出错而停止
    func screenRecorder(screenRecorder: RPScreenRecorder, didStopRecordingWithError error: NSError, previewViewController: RPPreviewViewController?) {
        delegate?.didStopWithError(error)
    }
}

extension ScreenCaptureManager: RPPreviewViewControllerDelegate{
    // 取消回调
    func previewControllerDidFinish(previewController: RPPreviewViewController) {
        print("previewControllerDidFinish")
        dispatch_async(dispatch_get_main_queue()) {
            self.delegate?.discardingRecord() // 取消保存
        }
    }
    
    // 保存-分享等的回调
    func previewController(previewController: RPPreviewViewController, didFinishWithActivityTypes activityTypes: Set<String>) {
        if activityTypes.contains("com.apple.UIKit.activity.SaveToCameraRoll") {
            dispatch_sync(dispatch_get_main_queue(), {
                self.delegate?.savingRecord() // 正在保存
            })
        }
    }
}
4. 录屏完成后的视频处理逻辑类:CaptureVideoManager

1. import AssetsLibrary
2. 你可以在将录制的视频转存到沙盒目录后,删除系统相册里的视频,以减小空间,但每次会提示用户是否删除,用户体验很不好,后来没找到解决方法就直接不删除了

    // 导出视频
    private func outputVideo(url: NSURL, board: Blackboard, model: CaptureVideo, success: (() -> Void)?){
        let asset = AVAsset(URL: url)
        let fmanager = NSFileManager.defaultManager()
        let path = blackboardPath(board.id) + "/\(BlackboardFileName.video.rawValue)"
        if !fmanager.fileExistsAtPath(path) {
            guard model.URL != nil else{
                return
            }
            
            let session = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality)
            session?.outputFileType = AVFileTypeQuickTimeMovie //MOV
            session?.outputURL = NSURL(fileURLWithPath: path)
            session?.exportAsynchronouslyWithCompletionHandler({
                ()in
                PrintUtil.Log("导出视频成功", args: nil)
                
                dispatch_async(dispatch_get_main_queue(), {
                    if success != nil{
                        success!()
                    }
                })
            })
        }
    }

    // 视频操作 - model转换
    private func operatingVideoAsset(board: Blackboard, asset: ALAsset, success: (() -> Void)?){
        let name = asset.defaultRepresentation().filename()
        let url = asset.defaultRepresentation().url()
        let model = CaptureVideo(URL: url, videoName: name, board: nil) //录制视频model类
        // 保存预览图
        let img = UIImage(CGImage: asset.aspectRatioThumbnail().takeUnretainedValue())
        let path = blackboardPath(board.id) + "/\(BlackboardFileName.thumbnail.rawValue)"
        if NSFileManager.defaultManager().createFileAtPath(path, contents: UIImagePNGRepresentation(img), attributes: nil){
            
            PrintUtil.Log("保存视频预览图成功", args: [path])
            // 导出视频
            outputVideo(url, board: board, model: model, success: success)
        }
    }

    // 提取刚才录制的视频 - NSEnumerationOptions.Reverse 倒序遍历,时间
    func saveVideo(board: Blackboard, success: (() -> Void)?){
        assets.enumerateGroupsWithTypes(ALAssetsGroupSavedPhotos, usingBlock: {
            (group, stop) in
            if group != nil{
                PrintUtil.Log("group", args: [group])
                // 过滤
                group.setAssetsFilter(ALAssetsFilter.allVideos())
                group.enumerateAssetsWithOptions(NSEnumerationOptions.Reverse, usingBlock:          { (asset, index, stop2) in
                    if asset != nil{
                        let name = asset.defaultRepresentation().filename()
                        // 特征点,一般为app boundle id
                        if name.containsString("com.founder.OrangeClass"){ 
                            // 是否是要的视频
                            self.operatingVideoAsset(board, asset: asset, success: success)
                            PrintUtil.Log("asset", args: [asset,index])
                            // 停止遍历 - 倒序遍历,第一个就是刚才录制的
                            stop2.memory = true
                        }
                    }
                })
            }
            
        }) { (error) in
            PrintUtil.Log("error", args: [error])
        }
    }

这里差不多了,可能没有讲太清楚,希望用到的童鞋可以参考下,共同学习探讨。

有空接着写完吧 懒懒~

上一篇下一篇

猜你喜欢

热点阅读