Skip to content

Instantly share code, notes, and snippets.

@Amzd
Last active October 19, 2024 08:27
Show Gist options
  • Save Amzd/2652bc98a6ae098701b342d710e45aa7 to your computer and use it in GitHub Desktop.
Save Amzd/2652bc98a6ae098701b342d710e45aa7 to your computer and use it in GitHub Desktop.
import UIKit
/// A view that hides its content to a user's recording, broadcast or screenshot.
/// https://gist.github.com/Amzd/2652bc98a6ae098701b342d710e45aa7
@available(iOS 12, *)
@dynamicMemberLookup
class PrivacySensitiveView<ContentView: UIView>: UIView {
private var content: ContentView
private var textField = {
let textField = UITextField()
textField.isSecureTextEntry = true
textField.isUserInteractionEnabled = false
textField.backgroundColor = .clear
return textField
}()
/// Setting this property to `false` enables the user’s ability to record and broadcast the content in the view again.
/// Default is `true`.
public var isPrivacySensitive: Bool {
get { textField.isSecureTextEntry }
set {
textField.isSecureTextEntry = newValue
setNeedsLayout()
}
}
public init(content: ContentView) {
self.content = content
super.init(frame: content.frame)
let container = textField.secureContainer ?? {
assertionFailure("failed to get secureContainer")
return UIView()
}()
addSubview(container)
container.fillSuperview()
container.addSubview(content)
content.fillSuperview()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
content.hitTest(point, with: event)
}
subscript<T>(dynamicMember member: KeyPath<ContentView, T>) -> T {
content[keyPath: member]
}
subscript<T>(dynamicMember member: WritableKeyPath<ContentView, T>) -> T {
get { content[keyPath: member] }
set { content[keyPath: member] = newValue }
}
}
extension UITextField {
@available(iOS 12, *)
fileprivate var secureContainer: UIView? {
let containerString = secureContainerTypeStringRepresentation
return subviews.first { subview in
type(of: subview).description() == containerString
}
}
@available(iOS 12, *)
private var secureContainerTypeStringRepresentation: String {
if #available(iOS 15, *) {
return "_UITextLayoutCanvasView"
} else if #available(iOS 14, *) {
return "_UITextFieldCanvasView"
} else if #available(iOS 13, *) {
return "_UITextFieldCanvasView"
} else /*if #available(iOS 12, *)*/ {
return "_UITextFieldContentView"
}
}
}
extension UIView {
func fillSuperview() {
guard let superview else { return }
translatesAutoresizingMaskIntoConstraints = false
leftAnchor.constraint(equalTo: superview.leftAnchor).isActive = true
rightAnchor.constraint(equalTo: superview.rightAnchor).isActive = true
topAnchor.constraint(equalTo: superview.topAnchor).isActive = true
bottomAnchor.constraint(equalTo: superview.bottomAnchor).isActive = true
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment