Zorgan
Zorgan

Reputation: 9173

How to make a reusable modifier for view lifecycle events (onAppear, onDisappear)?

I have a common fadeIn and fadeOut animation that I use for when views appear and disappear:

struct ActiveView: View {
    @State var showCode: Bool = false
    @State var opacity = 0.0
    var body: some View {

    if self.showCode {
        Color.black.opacity(0.7)
            .onAppear{
                let animation = Animation.easeIn(duration: 0.5)
                    return withAnimation(animation) {
                        self.opacity = 1
                    }
            }
            .onDisappear{
                let animation = Animation.easeOut(duration: 0.5)
                    return withAnimation(animation) {
                        self.opacity = 0
                    }
            }
    }
    }
}

However, I want to use these same animations on other views, so I want it to be simple and reusable, like this:

if self.showCode {
    Color.black.opacity(0.7)
        .fadeAnimation()
}

How can I achieve this?

EDIT:

Trying to implement a View extension:

extension View {
    func fadeAnimation(opacity: Binding<Double>) -> some View {
        self.onAppear{
            let animation = Animation.easeIn(duration: 0.5)
                return withAnimation(animation) {
                    opacity = 1
                }
        }
        .onDisappear{
            let animation = Animation.easeOut(duration: 0.5)
                return withAnimation(animation) {
                    opacity = 0
                }
        }
    }
}

Upvotes: 2

Views: 350

Answers (2)

Pondorasti
Pondorasti

Reputation: 627

The functionality that you are trying to implement is already part of the Animation and Transition modifiers from SwiftUI.

Therefore, you can add .transition modifier to any of your Views and it will animate its insertion and removal.

if self.showCode {
    Color.black.opacity(0.7)
        .transition(AnyTransition.opacity.animation(.easeInOut(duration: 0.5)))
}

You can use a multitude of different transitions like .slide, .scale, .offset, etc. More information about transitions here.

You can even create custom transitions with different actions for insertion and removal. In your case, different animation curves.

extension AnyTransition {
    static var fadeTransition: AnyTransition {
        .asymmetric(
            insertion: AnyTransition.opacity.animation(.easeIn(duration: 0.5)),
            removal: AnyTransition.opacity.animation(.easeOut(duration: 0.5))
        )
    }
}

And use it like this:

if self.showCode {
    Color.black.opacity(0.7)
        .transition(.fadeTransition)
}

Hope this helps 😉!

Upvotes: 2

Asperi
Asperi

Reputation: 258541

What you try to do is already present and named opacity transition, which is written in one modifier.

Here is a demo:

demo

struct ActiveView: View {
    @State var showCode: Bool = false
    var body: some View {
        ZStack {
            if self.showCode {
                Color.black.opacity(0.7)
                    .transition(AnyTransition.opacity.animation(.default))
            }
            Button("Demo") { self.showCode.toggle() }
        }.frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

Upvotes: 2

Related Questions