如何阻止用户在我的flutter应用程序中查看ios屏幕截图中的应用程序内容

问题描述 投票:0回答:1

在我的 flutter 应用程序中,我希望每当用户截屏时,在某些特定页面上,我的应用程序内容不应该是可见的。为了解决这个问题,我使用了一些本机 kotlin 代码在 android 中实现这一点,但对于 ios,我面临一些问题。

我创建了方法通道来实现此功能,并为此编写了一些本机 swift 代码。

这是我的 AppDelegate 文件:

import UIKit
import Flutter
import flutter_downloader
import GoogleMaps
import FirebaseCore
import FirebaseMessaging

@main
@objc class AppDelegate: FlutterAppDelegate {
    var blackOverlayView: UIView?
    var methodChannel: FlutterMethodChannel?

    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        guard let controller = window?.rootViewController as? FlutterViewController else {
            print("Error: RootViewController is not FlutterViewController")
            return false
        }

        methodChannel = FlutterMethodChannel(name: "my_channel_name",
                                           binaryMessenger: controller.binaryMessenger)

        methodChannel?.setMethodCallHandler { [weak self] (call, result) in
            guard let self = self else {
                result(FlutterError(code: "ERROR", message: "Self is nil", details: nil))
                return
            }

            switch call.method {
            case "preventScreenshots":
                print("Inside my prevent screenshot")
                DispatchQueue.main.async {
                    print("Inside 1")
                    guard let controller = self.window?.rootViewController as? FlutterViewController else {
                        print("Inside else")
                        result(FlutterError(code: "ERROR", message: "Root view controller not found", details: nil))
                        return
                    }
                    print("Inside 2")
                    let field = UITextField()
                    field.isSecureTextEntry = true
                    print("Inside 3")
                    field.translatesAutoresizingMaskIntoConstraints = false
                    controller.view.addSubview(field)
                    print("Inside 4")
                    NSLayoutConstraint.activate([
                        field.centerYAnchor.constraint(equalTo: controller.view.centerYAnchor),
                        field.centerXAnchor.constraint(equalTo: controller.view.centerXAnchor)
                    ])
                    print("Inside 5")
                }
                result(nil)
            case "allowScreenshots":
                DispatchQueue.main.async {
                    controller.view.subviews.forEach { view in
                        if view is UITextField {
                            view.removeFromSuperview()
                        }
                    }
                }
                result(nil)
            case "showBlackOverlay":
                self.showBlackOverlay()
                result(nil)
            case "hideBlackOverlay":
                self.hideBlackOverlay()
                result(nil)
            case "addScreenshotListener":
                self.addScreenshotObserver()
                result(nil)
            case "removeScreenshotListener":
                self.removeScreenshotObserver()
                result(nil)
            default:
                result(FlutterMethodNotImplemented)
            }
        }

        FirebaseApp.configure()
        GMSServices.provideAPIKey("my_api_key")
        GeneratedPluginRegistrant.register(with: self)
        FlutterDownloaderPlugin.setPluginRegistrantCallback(registerPlugins)

        if #available(iOS 10.0, *) {
            UNUserNotificationCenter.current().delegate = self
            let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
            UNUserNotificationCenter.current().requestAuthorization(
                options: authOptions,
                completionHandler: { _, _ in }
            )
        } else {
            let settings: UIUserNotificationSettings =
                UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
            application.registerUserNotificationSettings(settings)
        }

        application.registerForRemoteNotifications()
        Messaging.messaging().delegate = self

        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }

    @objc private func handleScreenshotNotification() {
        DispatchQueue.main.async {
            guard let controller = self.window?.rootViewController as? FlutterViewController else { return }
            print("Listened to screenshot, now calling method")
            self.methodChannel?.invokeMethod("preventScreenshots", arguments: nil)
        }
    }

    private func addScreenshotObserver() {
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(self.handleScreenshotNotification),
            name: UIApplication.userDidTakeScreenshotNotification,
            object: nil
        )
        print("Screenshot listener added")
    }

    private func removeScreenshotObserver() {
        NotificationCenter.default.removeObserver(
            self,
            name: UIApplication.userDidTakeScreenshotNotification,
            object: nil
        )
        print("Screenshot listener removed")
    }

    private func showBlackOverlay() {
        guard let window = self.window else { return }
        if blackOverlayView == nil {
            blackOverlayView = UIView(frame: window.bounds)
            blackOverlayView?.backgroundColor = UIColor.black
            blackOverlayView?.isUserInteractionEnabled = false
            window.addSubview(blackOverlayView!)
        }
        blackOverlayView?.isHidden = false
    }

    private func hideBlackOverlay() {
        blackOverlayView?.isHidden = true
    }
}

extension AppDelegate: MessagingDelegate {
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
        print("Firebase registration token: \(String(describing: fcmToken))")

        let dataDict: [String: String] = ["token": fcmToken ?? ""]
        NotificationCenter.default.post(
            name: Notification.Name("FCMToken"),
            object: nil,
            userInfo: dataDict
        )
    }
}

private func registerPlugins(registry: FlutterPluginRegistry) {
    if (!registry.hasPlugin("FlutterDownloaderPlugin")) {
        FlutterDownloaderPlugin.register(with: registry.registrar(forPlugin: "FlutterDownloaderPlugin")!)
    }
}

这是我如何调用我的方法通道:

class AppMethodChannels {
  static const _channel = MethodChannel("eduvate_admin/channel");

  ///Currently implemented for android only
  ///This method prevents user from taking screenshots
  static void preventScreenshots() {
    if (Platform.isAndroid || Platform.isIOS) {
      _channel.invokeMethod('preventScreenshots');
    }
  }

  ///Currently implemented for android only
  ///This method allows user to take screenshots
  static void allowScreenshots() {
    if (Platform.isAndroid || Platform.isIOS) {
      _channel.invokeMethod('allowScreenshots');
    }
  }

  ///Shows a black overlay on the screen.
  ///Currently implemented for iOS only
  static void showBlackOverlay() {
    if (Platform.isIOS) {
      _channel.invokeMethod('showBlackOverlay');
    }
  }

  ///Hides the black overlay from the screen.
  ///Currently implemented for iOS only
  static void hideBlackOverlay() {
    if (Platform.isIOS) {
      _channel.invokeMethod('hideBlackOverlay');
    }
  }

  static void setScreenshotListener() {
    if (Platform.isIOS) {
      print("Setting ss listener");
      _channel.invokeMethod("addScreenshotListener");
    }
  }
}

我正在页面的初始状态下调用

setScreenshotListener

现在,我不知道 swift,但据我所知,当我从我的方法通道调用

addScreenshotListener
时,

NotificationCenter.default.addObserver(
            self,
            selector: #selector(self.handleScreenshotNotification),
            name: UIApplication.userDidTakeScreenshotNotification,
            object: nil
        )

这部分代码将运行,它是某种监听器,它将触发该函数,每当通知到来时,该函数就会传递到选择器中。

现在,在

handleScreenshotNotification
方法中,我调用了
self.methodChannel?.invokeMethod("preventScreenshots", arguments: nil)
应该触发我上面声明的
preventScreenshots
方法。

现在,这个打印语句正在打印,

print("Listened to screenshot, now calling method")
但是我的
preventScreenshots
方法中的所有打印语句都没有打印,即我的方法没有被调用。

因此,我尝试在监听器中编写相同的代码,而不是调用函数,而是打印所有语句,但我无法实现所需的输出。

有人可以帮我解决这个问题吗?比如,为什么我的方法没有被调用以及为什么空白/空屏幕的代码不起作用?

ios swift flutter
1个回答
0
投票

我找到了使用这个包的解决方案https://pub.dev/packages/screen_protector

这对于 ios 和 android 来说工作得很好,因为它会显示一个空白的屏幕截图视图。我认为我们可以更改它的代码并显示我们的自定义小部件/图像。

© www.soinside.com 2019 - 2024. All rights reserved.