Created
September 8, 2023 22:08
-
-
Save sroebert/77554248276ac6fe28a83c553c4f48fa to your computer and use it in GitHub Desktop.
SwiftUI LockableView
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 | |
import UIKit | |
struct ContentView: View { | |
@State private var isLocked = false | |
@State private var isSheetVisible = false | |
var body: some View { | |
LockableView(isLocked: isLocked) { | |
Color.white | |
.ignoresSafeArea() | |
.overlay { | |
VStack { | |
Text("This is the app") | |
Button("Click here to present sheet") { | |
isSheetVisible.toggle() | |
} | |
Button("Click here to lock") { | |
isLocked.toggle() | |
} | |
.foregroundStyle(Color.red) | |
} | |
} | |
.sheet(isPresented: $isSheetVisible) { | |
Color.gray | |
.ignoresSafeArea() | |
.overlay { | |
VStack { | |
Text("This is a sheet") | |
Button("Click here to dismiss sheet") { | |
isSheetVisible.toggle() | |
} | |
Button("Click here to lock") { | |
isLocked.toggle() | |
} | |
.foregroundStyle(Color.red) | |
} | |
} | |
} | |
} lockedOverlay: { | |
Color.black | |
.ignoresSafeArea() | |
.overlay { | |
VStack { | |
Text("This is the lock screen") | |
.foregroundStyle(Color.white) | |
Button("Click here to unlock") { | |
isLocked.toggle() | |
} | |
.foregroundStyle(Color.green) | |
} | |
} | |
} | |
.ignoresSafeArea() | |
} | |
} | |
struct LockableView<Content: View, LockedOverlay: View>: UIViewControllerRepresentable { | |
var isLocked: Bool | |
@ViewBuilder var content: Content | |
@ViewBuilder var lockedOverlay: LockedOverlay | |
func makeUIViewController(context: Context) -> LockableViewController<Content, LockedOverlay> { | |
LockableViewController(content: content, lockedOverlay: lockedOverlay, isLocked: isLocked) | |
} | |
func updateUIViewController(_ uiViewController: LockableViewController<Content, LockedOverlay>, context: Context) { | |
uiViewController.update(content: content, lockedOverlay: lockedOverlay, isLocked: isLocked) | |
} | |
} | |
class LockableViewController<Content: View, LockedOverlay: View>: UIHostingController<Content> { | |
private var lockedWindow: UIWindow? | |
private let lockedHostingController: UIHostingController<LockedOverlay> | |
private var isLocked: Bool | |
init( | |
content: Content, | |
lockedOverlay: LockedOverlay, | |
isLocked: Bool | |
) { | |
lockedHostingController = UIHostingController(rootView: lockedOverlay) | |
self.isLocked = isLocked | |
super.init(rootView: content) | |
} | |
required init?(coder aDecoder: NSCoder) { | |
fatalError("init(coder:) has not been implemented") | |
} | |
override func viewDidAppear(_ animated: Bool) { | |
super.viewDidAppear(animated) | |
if isLocked { | |
showLock() | |
} | |
} | |
private func showLock() { | |
guard lockedWindow == nil, let windowScene = view.window?.windowScene else { | |
return | |
} | |
let lockedWindow = UIWindow(windowScene: windowScene) | |
lockedWindow.rootViewController = lockedHostingController | |
lockedWindow.alpha = 0 | |
lockedWindow.makeKeyAndVisible() | |
self.lockedWindow = lockedWindow | |
UIView.animate(withDuration: 0.3) { | |
lockedWindow.alpha = 1 | |
} | |
} | |
private func hideLock() { | |
guard let window = lockedWindow else { | |
return | |
} | |
self.lockedWindow = nil | |
UIView.animate(withDuration: 0.3) { | |
window.alpha = 0 | |
} completion: { _ in | |
// Keep a reference to the window during the animation, | |
// to make sure it does not immediately disappear | |
_ = window | |
} | |
} | |
func update( | |
content: Content, | |
lockedOverlay: LockedOverlay, | |
isLocked: Bool | |
) { | |
rootView = content | |
lockedHostingController.rootView = lockedOverlay | |
if isLocked != self.isLocked { | |
self.isLocked = isLocked | |
if isLocked { | |
showLock() | |
} else { | |
hideLock() | |
} | |
} | |
} | |
} |
Thank you, it helped me a lot!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you very much - this is really nice work from you.