Skip to content

Instantly share code, notes, and snippets.

@yoxisem544
Created August 17, 2022 04:04
Show Gist options
  • Save yoxisem544/bed56d4aded6697a6163e5db314e6e17 to your computer and use it in GitHub Desktop.
Save yoxisem544/bed56d4aded6697a6163e5db314e6e17 to your computer and use it in GitHub Desktop.
Prevent screenshot view wrapper
import UIKit
/// Beware that if you add view to subview that has been pulled out from this recognizer,
/// calling getter will result in nil value.
struct HiddenContainerRecognizer {
private enum Error: Swift.Error {
case unsupportedIosVersion(version: Float)
case desiredContainerWasNotFound(_ containerName: String)
}
func getHiddenContainer(from view: UIView) throws -> UIView {
let containerName = try getHiddenContainerTypeInStringRepresentation()
let containers = view.subviews.filter { subview in
type(of: subview).description() == containerName
}
guard let container = containers.first else {
throw Error.desiredContainerWasNotFound(containerName)
}
return container
}
private func getHiddenContainerTypeInStringRepresentation() throws -> String {
if #available(iOS 15, *) {
return "_UITextLayoutCanvasView"
}
if #available(iOS 14, *) {
return "_UITextFieldCanvasView"
}
if #available(iOS 12, *) {
return "_UITextFieldContentView"
}
let currentIOSVersion = (UIDevice.current.systemVersion as NSString).floatValue
throw Error.unsupportedIosVersion(version: currentIOSVersion)
}
}
import UIKit
final class ScreenshotPreventingView: UIView {
// MARK: - πŸ“Œ Constants
// MARK: - πŸ”Ά Properties
var preventScreenCapture = true {
didSet {
textField.isSecureTextEntry = preventScreenCapture
}
}
private var contentView: UIView?
private let textField = UITextField()
private let recognizer = HiddenContainerRecognizer()
private lazy var hiddenContentContainer: UIView? = try? recognizer.getHiddenContainer(from: textField)
// MARK: - 🎨 Style
// MARK: - 🧩 Subviews
// MARK: - πŸ‘† Actions
// MARK: - πŸ”¨ Initialization
init(contentView: UIView) {
self.contentView = contentView
super.init(frame: .zero)
setupUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - πŸ–Ό View Lifecycle
// MARK: - πŸ— UI
private func setupUI() {
textField.backgroundColor = .clear
textField.isUserInteractionEnabled = false
guard let container = hiddenContentContainer else { return }
addSubview(container)
container.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
container.leadingAnchor.constraint(equalTo: leadingAnchor),
container.trailingAnchor.constraint(equalTo: trailingAnchor),
container.topAnchor.constraint(equalTo: topAnchor),
container.bottomAnchor.constraint(equalTo: bottomAnchor)
])
guard let contentView = contentView else { return }
setup(contentView: contentView)
DispatchQueue.main.async {
// setting secure text entry in init block will fail
// setting default value inside main thread
self.preventScreenCapture = true
}
}
// MARK: - 🚌 Public Methods
func setup(contentView: UIView) {
self.contentView?.removeFromSuperview()
self.contentView = contentView
guard let container = hiddenContentContainer else { return }
container.addSubview(contentView)
contentView.translatesAutoresizingMaskIntoConstraints = false
let bottomConstraint = contentView.bottomAnchor.constraint(equalTo: container.bottomAnchor)
bottomConstraint.priority = .required - 1
NSLayoutConstraint.activate([
contentView.leadingAnchor.constraint(equalTo: container.leadingAnchor),
contentView.trailingAnchor.constraint(equalTo: container.trailingAnchor),
contentView.topAnchor.constraint(equalTo: container.topAnchor),
bottomConstraint
])
}
// MARK: - πŸ”’ Private Methods
}
// MARK: - 🧢 Extensions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment