iOS-Swift使用ReplayKit实现录屏功能

前段时间有碰到Android用户使用系统录屏发来的一些bug重现录像,iOS端不会用系统录屏的用户都是拍视频联系产品然后反馈给我们开发,这中间有的时候挺耗费时间和精力的,空下来就琢磨了下iOS的录屏使用。录屏用得比较多的是苹果自带的ReplayKit框架,不需要导包,使用比较便捷,而且耗费内存较小,基本忽略不计,这也是我选择ReplayKit的原因,如对功能研究较感兴趣的同学可以忽略本篇。
我使用的手机是锤子M1的是比较老的版本,我对照着锤子做了一个简单的demo,效果图如下:


效果图

系统版本9.0以下ReplayKit是不可使用的,所以在使用前一定要判断系统版本是否符合要求,在Targets->Build Settings->iOS Deployment Target中设置的版本号高于9.0也可使用,也可加if #avaliable(iOS 9.0,*)判断减少此类风险,这边是个大坑,我写的demo中Timer等需要10.0以上版本,所以请勿抬杠,个人因情况而定。


Targets->Build Settings->iOS Deployment Target

工具条对应的文件

“展示录屏控件”触发方法,在给ReplayToolsView中的关闭按钮和录屏开始按钮加事件的时候我是使用的闭包,这样可以控制效果图中红色按钮的状态;对红色按钮的状态判断是为了避免多次点击按钮后ReplayToolsView的叠加;replayVideoView.addGestureRecognizer给replayVideoView添加移动手势,王者荣耀中在录屏时按钮是固定的,以下demo中的录屏可以在整个app中使用,包括记录页面跳转过程等。

@IBAction func showActionView(_ sender: UIButton) {
        if !self.showViewBtn.isSelected {
            let replayVideoView = ReplayToolsView(frame: CGRect(x: 20, y: self.view.bounds.height-100, width: self.view.bounds.width-40, height: 50))
            //使用block代替在view中编写关闭方法,添加对“app录屏”按钮的选中状态设置
            replayVideoView.closeBlock = { () in
                //此处设置结束录制后将replayVideoView移除,也可以隐藏isHidden,在此处和startRecordingScreen方法中将view改成隐藏,replayVideoView需要改为全局变量使用
                replayVideoView.removeFromSuperview()
                self.showViewBtn.isSelected = !self.showViewBtn.isSelected
            }
            replayVideoView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(dragFunction)))
            replayVideoView.actionBlock = {
                self.startRecordingScreen(replayVideoView)
                replayVideoView.closeButton.isHidden = replayVideoView.actionButton.isSelected
            }
            let window = UIApplication.shared.keyWindow
            window!.addSubview(replayVideoView)
            self.showViewBtn.isSelected = !self.showViewBtn.isSelected
        }
    }
 // Janise: 设置拖动手势(可用于拖动控件后设置控件依赖位置,此处只设置可在y轴方向上移动,如需要在x方向上移动,可对x位置修改)
    @objc func dragFunction(_ pan: UIPanGestureRecognizer) {
        let point = pan.translation(in: view)
        if let v = pan.view {
            //设置移至顶部和底部时不可移动,64为个人主观设置值,可根据个人需要更改
            if (v.frame.origin.y > 64 && point.y < 0)
                || (v.frame.origin.y + v.frame.height < (UIApplication.shared.keyWindow?.frame.height)! && point.y > 0) {
                v.center.y = v.center.y + point.y
            }
            pan.setTranslation(.zero, in: view)
        }
    }
 // Janise:  开始录制屏幕(考虑问题,可否像android那样在录屏时不将ReplayVideoToolView视图作为屏幕的一部分录制,我暂时还没找到方法)
    
    /// 开始录屏(遗留问题:初次录屏时会询问用户是否同意使用录屏功能,但是时间在用户点击录屏按钮时已开始计时,与用户同意使用录屏功能时间点有时间差,这部分需要解决)
    ///
    /// - Parameter toolView: 自制黑色半透明录屏视图(用于图标的展示修改和时间的变化)
    @objc func startRecordingScreen(_ toolView: ReplayToolsView) {
        // 检测设备是否支持录屏功能
        if RPScreenRecorder.shared().isAvailable && systemVersionAvaliable()  {
            //录屏按钮是否选中
            if toolView.actionButton.isSelected {
                //停止计时
                self.time?.invalidate()
                self.time = nil
                toolView.seconds = 0
                //选中状态下停止录制
                RPScreenRecorder.shared().stopRecording { (previewCon, error) in
                    if let errors = error {
                        print(errors)
                    }
                    if let controller = previewCon {
                        controller.previewControllerDelegate = self
                        UIApplication.shared.keyWindow?.rootViewController?.present(controller, animated: true, completion: nil)
                    }
                }
                
                toolView.actionButton.isSelected = false
                toolView.removeFromSuperview()
                self.showViewBtn.isSelected = !self.showViewBtn.isSelected
                return
            }else {
                //开始计时
                self.time = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { (timer) in
                    toolView.seconds += 1
                })
                //未选中状态下开始录屏
                RPScreenRecorder.shared().startRecording { (err) in
                    if let error = err {
                        print(error)
                    }
                }
                toolView.actionButton.isSelected = true
            }
        }else {
            //            //正式编写时需将两种提示分开展示,权限与版本两个不可合二为一
            //            let alert = UIAlertController(title: "提示", message: "请先授予app录屏权限,系统版本低于9.0不支持录屏功能,请升级版本后使用该功能", preferredStyle: .alert)
            //            self.present(alert, animated: true, completion: nil)
        }
    }
/// 视频播放
    ///
    /// - Parameter previewController: 视频预览播放器
    func previewControllerDidFinish(_ previewController: RPPreviewViewController) {
        //关闭视频预览
        previewController.dismiss(animated: true, completion: nil)
    }
/// 判断版本是否在9.0版本及以上版本
    ///
    /// - Returns: 布尔值
    func systemVersionAvaliable() -> Bool {
        if #available(iOS 9.0, *) {
            return true
        }
        return false
    }

replayVideoView的编写较为简单,此处不做展示。
我没有添加麦克风录音功能,startRecording(withMicrophoneEnabled:true...在10.0版本已被弃用,我在找其他的方法来替代。

RPScreenRecorder.shared().startRecording(withMicrophoneEnabled:true) { (<#Error?#>)in
                    <#code#>
                }

ReplayKit不可以与AVPlayer同用,会出现兼容问题,录屏功能只能在真机上使用,虚拟机不支持,我是用的Swift编写,网上较多的使用OC,IOS 一个很好的录制屏幕实现IOS功能 - 飞翔的熊blabla - CSDN博客csdn上这篇写得比较好。

以上代码我传到了GitHub上,地址是demo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353