Skip to content

Commit

Permalink
Fix lock view flickering on first appearance
Browse files Browse the repository at this point in the history
Fixes #781
  • Loading branch information
keeshux committed Oct 30, 2024
1 parent aadc46e commit c1d168c
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 30 deletions.
2 changes: 1 addition & 1 deletion Passepartout/Library/Sources/AppUI/Theme/Theme+UI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ struct ThemeLockScreenModifier: ViewModifier {

func body(content: Content) -> some View {
LockableView(
locksInBackground: $locksInBackground,
locksInBackground: locksInBackground,
content: {
content
},
Expand Down
75 changes: 46 additions & 29 deletions Passepartout/Library/Sources/UtilsLibrary/Views/LockableView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,51 +32,49 @@ public struct LockableView<Content: View, LockedContent: View>: View {
@Environment(\.scenePhase)
private var scenePhase

@Binding
private var locksInBackground: Bool
// unobserved here, observed in LockedContentWrapper
private let lock: Lock

private let locksInBackground: Bool

private let content: () -> Content

private let lockedContent: () -> LockedContent

private let unlockBlock: () async -> Bool

@ObservedObject
private var lock: Lock = .shared

@Binding
private var state: Lock.State

@MainActor
public init(
locksInBackground: Binding<Bool>,
locksInBackground: Bool,
content: @escaping () -> Content,
lockedContent: @escaping () -> LockedContent,
unlockBlock: @escaping () async -> Bool
) {
_locksInBackground = locksInBackground
lock = .shared
self.locksInBackground = locksInBackground
self.content = content
self.lockedContent = lockedContent
self.unlockBlock = unlockBlock

_state = .init {
Lock.shared.state
} set: {
Lock.shared.state = $0
}
}

public var body: some View {
ZStack {
debugChanges()
return ZStack {
content()
if locksInBackground && state != .none {
lockedContent()
if locksInBackground {
LockedContentWrapperView(
lock: lock,
content: lockedContent
)
}
}.onChange(of: scenePhase, perform: onScenePhase)
}
.onChange(of: scenePhase, perform: onScenePhase)
}
}

// MARK: -

@MainActor
private final class Lock: ObservableObject {
enum State {
case none
Expand All @@ -88,23 +86,25 @@ private final class Lock: ObservableObject {

static let shared = Lock()

@Published var state: State = .locked
@Published
var state: State = .locked

private init() {
}
}

// MARK: -

@MainActor
private extension LockableView {
func onScenePhase(_ scenePhase: ScenePhase) {
switch scenePhase {
case .active:
unlockIfNeeded()

case .inactive:
if state == .none {
state = .covered
if lock.state == .none {
lock.state = .covered
}

case .background:
Expand All @@ -119,30 +119,47 @@ private extension LockableView {
guard locksInBackground else {
return
}
state = .locked
lock.state = .locked
}

func unlockIfNeeded() {
guard locksInBackground else {
state = .none
lock.state = .none
return
}
switch state {
switch lock.state {
case .none:
break

case .covered:
state = .none
lock.state = .none

case .locked:
Task { @MainActor in
Task {
guard await unlockBlock() else {
return
}
state = .none
lock.state = .none
}
}
}
}

// MARK: -

private struct LockedContentWrapperView<Content>: View where Content: View {

@ObservedObject
var lock: Lock

@ViewBuilder
let content: Content

var body: some View {
if lock.state != .none {
content
}
}
}

#endif

0 comments on commit c1d168c

Please sign in to comment.