如何在Flutter中通过Swift代码停止iOS控制中心启动的录屏?
问题:Flutter应用无法停止iOS控制中心启动的屏幕录制
环境
- Flutter v2.10.5
问题描述
在模拟器和iPhone上测试时,能通过ReplayKit正常检测屏幕录制状态(isRecording返回正确值),但调用停止录屏的方法后,无法终止由iOS控制中心启动的全局屏幕录制。
代码实现
AppDelegate.swift
import UIKit import Flutter import MoEngage import moengage_flutter import flutter_downloader import AVFoundation import Foundation import ReplayKit @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { if #available(iOS 10.0, *) { UNUserNotificationCenter.current().delegate = self } var moSDKConfig : MOSDKConfig let moAppId = "" if let config = MoEngage.sharedInstance().getDefaultSDKConfiguration() { moSDKConfig = config moSDKConfig.moeAppID = moAppId } else { moSDKConfig = MOSDKConfig.init(appID: moAppId) } moSDKConfig.appGroupID = "group.com.package-name.shared" moSDKConfig.moeDataCenter = DATA_CENTER_01 MOFlutterInitializer.sharedInstance.initializeWithSDKConfig( moSDKConfig, andLaunchOptions: launchOptions ) FlutterDownloaderPlugin.setPluginRegistrantCallback(registerPlugins) let controller : FlutterViewController = window?.rootViewController as! FlutterViewController let isMicrophoneAvailable = FlutterMethodChannel(name: "package-name/audio_recorder", binaryMessenger: controller.binaryMessenger) isMicrophoneAvailable.setMethodCallHandler({ [weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in guard call.method == "isMicrophoneAvailable" else { result(FlutterMethodNotImplemented) return } self?.isMicrophoneAvailable(result: result) }) let screenRecording = FlutterMethodChannel(name: "package-name/screen_recording", binaryMessenger: controller.binaryMessenger) screenRecording.setMethodCallHandler({ [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in switch call.method { case "stop_screen_share", "is_screen_share_active": self?.screenRecordActions(call, result) default: result(FlutterMethodNotImplemented) } }) GeneratedPluginRegistrant.register(with: self) // Set your desired minimumBackgroundFetchInterval let backgroundIntervalInMinutes = 60 UIApplication.shared.setMinimumBackgroundFetchInterval(TimeInterval(60*backgroundIntervalInMinutes)) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } private func isMicrophoneAvailable(result: FlutterResult) { if AVAudioSession.sharedInstance().isOtherAudioPlaying == false { result(Bool(true)) } else { result(Bool(false)) } } // MARK: - Screen Record private func screenRecordActions(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { switch call.method { case "stop_screen_share": RPScreenRecorder.shared().stopCapture( handler: { (error) in print("stopping recording"); }) case "is_screen_share_active": let isRecording = RPScreenRecorder.shared().isRecording; result(isRecording) default: print("Not Valid") } } } private func registerPlugins(registry: FlutterPluginRegistry) { if (!registry.hasPlugin("FlutterDownloaderPlugin")) { FlutterDownloaderPlugin.register(with: registry.registrar(forPlugin: "FlutterDownloaderPlugin")!) } }
content_protection.dart
part of '../app_extension.dart'; class QContentProtection { QContentProtection._(); static const platform = MethodChannel('package-name/screen_recording'); final SecureContent _secureContent = SecureContent(); void preventScreenshotAndroid(bool prevent) { _secureContent.preventScreenshotAndroid(prevent); } // Future<void> startRecording() async { // try { // await platform.invokeMethod('start_screen_share'); // } catch (e, s) { // log('[QContentProtection.startRecording] error : $e, $s'); // QAppX.extendedRouter.showNotificationsShade( // message: e.toString(), // type: QInAppNotificationType.failure, // ); // } // } Future<void> stopRecording() async { try { await platform.invokeMethod('stop_screen_share'); } catch (e, s) { log('[QContentProtection.stopRecording] error : $e, $s'); QAppX.extendedRouter.showNotificationsShade( message: e.toString(), type: QInAppNotificationType.failure, ); } } Future<bool> isScreenRecordingOn() async { try { final isScreenRecordingOn = await platform.invokeMethod<bool>('is_screen_share_active'); log('[QContentProtection.isScreenRecordingOn] isScreenRecordingOn : $isScreenRecordingOn'); return isScreenRecordingOn ?? false; } catch (e, s) { log('[QContentProtection.isScreenRecordingOn] error : $e, $s'); QAppX.extendedRouter.showNotificationsShade( message: e.toString(), type: QInAppNotificationType.failure, ); return false; } } }
main.dart
onPressed: () async { final isScreenRecordingOn = await QAppX.contentProtection.isScreenRecordingOn(); if (isScreenRecordingOn) { QAppX.contentProtection.stopRecording(); } }
解答
核心原因
ReplayKit的stopCapture()方法只能终止由当前应用启动的屏幕录制会话,无法停止通过iOS控制中心启动的系统级全局录屏。这是iOS的权限限制:控制中心启动的录屏属于系统级操作,第三方应用没有权限干预或终止它。
代码检查
你的代码逻辑整体没问题:
isRecording能正确检测录屏状态,说明ReplayKit的状态监听正常stopCapture()的调用方式符合官方文档要求,但因为目标录屏会话不是由你的应用启动,所以调用后不会产生效果- 另外,
main.dart中的按钮点击回调原代码缺少async关键字,会导致编译错误,已在代码块中修正
替代方案
既然无法主动停止控制中心的录屏,你可以:
- 检测到录屏状态时,在应用内弹出提示,引导用户手动从控制中心停止录屏
- 对敏感内容启用屏幕保护,防止录屏捕获敏感信息
内容的提问来源于stack exchange,提问作者codenameakshay
相关产品推荐
相关产品推荐

