Simple auto-animating HUD #1553
-
Hello all :) I'm trying to create a simple auto-animating view that behaves more or less like a heads-up display (HUD), ie, when the state driving it says it's showing then it animates itself in and when the state says it's not showing, it animates itself away. I managed to do it using a timer but that seems unnecessarily complex for such a simple task. I looked at the animation case study in the repo but it wasn't all that helpful because I'm not actually sending any actions to the store (though the code allows that possibility); in the preview, I'm merely setting the state and I expected the view to animate itself in or out, depending on the state value being set. Note that the preview sets the period of the animation to be 5 seconds. Yet, setting What am I missing? Thanks in advance for any help. import ComposableArchitecture
import SwiftUI
extension Hud {
public struct State: Equatable {
public internal(set) var isShowing: Bool
public let duration: Double
public init(
isShowing: Bool = false,
duration: Double = 0.8
) {
self.isShowing = isShowing
self.duration = duration
}
}
public enum Action: Equatable {
case show
case hide
}
}
public struct Hud: ReducerProtocol {
public init() {}
public func reduce(into state: inout State, action: Action) -> Effect<Action, Never> {
switch action {
case .show:
state.isShowing = true
return .none
case .hide:
state.isShowing = false
return .none
}
}
}
extension Hud {
public struct View: SwiftUI.View {
private let store: Store<State, Action>
public init(store: Store<State, Action>) {
self.store = store
}
public var body: some SwiftUI.View {
WithViewStore(store) { viewStore in
Text("Showing")
.padding(10)
.background(Color.green)
.cornerRadius(10)
.opacity(viewStore.isShowing ? 1.0 : 0.0)
.animation(.easeOut(duration: viewStore.duration), value: viewStore.isShowing)
}
}
}
}
struct HudView_Previews: PreviewProvider {
static var previews: some View {
Hud.View(store: .init(
initialState: Hud.State(
isShowing: true,
duration: 5.0
),
reducer: Hud()
))
}
} |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments
-
The view begins with the state of For example, I added this button to the view and tapping it does what I expect: WithViewStore(store) { viewStore in
Button("Toggle") {
if viewStore.isShowing {
viewStore.send(.hide)
} else {
viewStore.send(.show)
}
}
…
} |
Beta Was this translation helpful? Give feedback.
-
If I'm understanding your issue, you would like the HUD to be animated even when you install this view into your hierarchy with struct Indicator<Content: View>: View {
init(
isVisible: Bool,
@ViewBuilder content: () -> Content
) {
self.isVisible = isVisible
self.content = content()
}
let isVisible: Bool
let content: Content
@State var didAppear: Bool = false
var body: some View {
Group {
// We don't want to show right away if `isVisible`, so
// we wait for `didAppear` to be `true`. We will then
// transition from the `ViewB` to `ViewA` and SwiftUI
// will use any applied transition to animate this
// change. Please note that this is not `.hidden()` that
// is animated, but the change from `ViewB` to `ViewA`
// which have different structural identity, and thus
// warrant a transition.
if isVisible && didAppear {
content // ViewA
} else {
content // ViewB
.hidden()
}
}
.onAppear {
didAppear = true
}
}
} You can then use this view like: Indicator(isVisible: viewStore.isShowing) {
Text("Showing")
.padding(10)
.background(Color.green)
.cornerRadius(10)
}
.transition(
.opacity.animation(
.easeOut(duration: viewStore.duration)
)
) The
|
Beta Was this translation helpful? Give feedback.
-
Hi @mbrandonw and @tgrapperon , thank you both. :) I think Thomas' suggestion is what I need. Much appreciated. |
Beta Was this translation helpful? Give feedback.
If I'm understanding your issue, you would like the HUD to be animated even when you install this view into your hierarchy with
viewStore.isShowing == true
. This is purely a SwiftUI problem, and this is indeed slightly tricky. I would probably extract TCA from it and create a vanillaIndicator
view: