Created
January 27, 2025 11:40
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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