Skip to content

Instantly share code, notes, and snippets.

@bguidolim
Last active July 30, 2025 12:00
Show Gist options
  • Save bguidolim/28e24c4e259a811440299a267285ceaa to your computer and use it in GitHub Desktop.
Save bguidolim/28e24c4e259a811440299a267285ceaa to your computer and use it in GitHub Desktop.
A clever way to hide sensitive information from screenshots and screen recordings. Solution by @firatkaratas
import SwiftUI
import UIKit
import UIUtility
extension View {
func captureProtected(isBlocked: Bool) -> some View {
modifier(BlockCapture(isBlocked: isBlocked))
}
}
struct BlockCapture: ViewModifier {
let isBlocked: Bool
func body(content: Content) -> some View {
if isBlocked {
CaptureBlockingView {
content
}
} else {
content
}
}
}
struct CaptureBlockingView<Content: View>: UIViewControllerRepresentable {
private let content: () -> Content
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
func makeUIViewController(context: Context) -> CaptureBlockingHostingController<Content> {
CaptureBlockingHostingController(content: content)
}
func updateUIViewController(_ uiViewController: CaptureBlockingHostingController<Content>, context: Context) { }
}
final class CaptureBlockingHostingController<Content: View>: UIViewController {
private let content: () -> Content
private let secureView = CaptureBlockingContainerView()
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
super.init(nibName: nil, bundle: nil)
setupUI()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI() {
view.addSubview(secureView)
secureView.translatesAutoresizingMaskIntoConstraints = false
secureView.backgroundColor = .clear
let hostingController = addChildView(content())
secureView.setup(contentView: hostingController.view)
}
}
final class CaptureBlockingContainerView: UIView {
private var textField = {
let textField = UITextField()
textField.isSecureTextEntry = true
textField.backgroundColor = .clear
textField.isUserInteractionEnabled = false
return textField
}()
private lazy var secureViewContainer: UIView? = {
let containerPrefix = "UIText"
let containerSuffix = "CanvasView"
let containerName: String = "Layout"
let secureCanvas = textField.subviews.first { subview in
// This type has the logic of capture blocking.
// It is being extracted so that we don't have to
// deal with textField responder logic.
let canvasType = "_" + containerPrefix + containerName + containerSuffix
return NSStringFromClass(type(of: subview)) == canvasType
}
guard let secureCanvas else { return nil }
addSubview(secureCanvas)
secureCanvas.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate {
secureCanvas.constrain.sameFrame(as: self)
}
return secureCanvas
}()
func setup(contentView: UIView) {
guard let viewContainer = secureViewContainer else { return }
viewContainer.addSubview(contentView)
contentView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate {
contentView.constrain.sameFrame(as: viewContainer)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment