Skip to content

Instantly share code, notes, and snippets.

@Kishan-Simform
Created January 27, 2025 11:40
Show Gist options
  • Save Kishan-Simform/6497759b6f997c6f0d17de972ebe23c0 to your computer and use it in GitHub Desktop.
Save Kishan-Simform/6497759b6f997c6f0d17de972ebe23c0 to your computer and use it in GitHub Desktop.
A view that prevents screenshots of its content by embedding it within a secure environment. It displays a placeholder while the actual content remains hidden to prevent screenshotting.
import SwiftUI
/// A view that prevents screenshots of its content by embedding it within a secure environment.
/// It displays a placeholder while the actual content remains hidden to prevent screenshotting.
struct ScreenshotPreventView<Content: View, Placeholder: View>: View {
private var content: Content
private var placeholder: Placeholder
@State private var hostingController: UIHostingController<Content>?
/// Initializes the view with content and a placeholder.
///
/// - Parameters:
/// - content: The actual content view that will be protected from screenshots.
/// - placeholder: The view that is shown when the content is hidden for protection.
init(
@ViewBuilder content: @escaping () -> Content,
@ViewBuilder placeholder: @escaping () -> Placeholder
) {
self.content = content()
self.placeholder = placeholder()
}
var body: some View {
ZStack {
placeholder // Shown when content is hidden to prevent screenshots
content
.opacity(0) // Makes the actual content invisible to avoid screenshot capture
.background(
GeometryReader { geometry in
Color.clear
.preference(key: SizeKey.self, value: geometry.size)
.onPreferenceChange(SizeKey.self) { size in
updateHostingControllerSize(to: size)
}
}
)
.background(
_ScreenshotPreventHelper(hostingController: $hostingController)
)
}
}
/// Updates the size of the hosting controller's view.
private func updateHostingControllerSize(to size: CGSize) {
guard size != .zero else { return }
if hostingController == nil {
hostingController = UIHostingController(rootView: content)
hostingController?.view.backgroundColor = .clear
hostingController?.view.tag = 1009
}
hostingController?.view.frame = CGRect(origin: .zero, size: size)
}
}
fileprivate struct SizeKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
value = nextValue()
}
}
fileprivate struct _ScreenshotPreventHelper<Content: View>: UIViewRepresentable {
@Binding var hostingController: UIHostingController<Content>?
func makeUIView(context: Context) -> UIView {
let secureField = UITextField()
secureField.isSecureTextEntry = true
return secureField.subviews.first ?? UIView()
}
func updateUIView(_ uiView: UIView, context: Context) {
guard let hostingController else { return }
// Add the hosting controller's view if not already added
if !uiView.subviews.contains(where: { $0.tag == 1009 }) {
uiView.addSubview(hostingController.view)
}
}
}
/// Usage Example:
struct ContentView: View {
var body: some View {
ScreenshotPreventView(
content: {
VStack {
Text("Some sensitive content here")
.font(.largeTitle)
.padding()
Image(systemName: "lock.fill")
.font(.largeTitle)
}
.background(Color.blue)
},
placeholder: {
VStack {
Text("[Protected Content]")
.font(.title)
.foregroundColor(.gray)
}
}
)
.frame(width: 300, height: 200)
.background(Color.blue)
.cornerRadius(10)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment