Home > Software design >  Broken SwiftUI Transitions
Broken SwiftUI Transitions

Time:04-30

I have a pretty simple view modifier for presenting toasts

struct ToastItemModifier<Item: Equatable, M: View>: ViewModifier {

    typealias Style = ToastStyle

    // MARK: - Environment

    @Environment(\.colorScheme)
    var colorScheme

    // MARK: - Properties

    private let style: Style

    private let item: Item?

    private let message: (Item) -> M

    @Binding
    private var isPresented: Bool

    init(item: Item?, isPresented: Binding<Bool>, @ViewBuilder message: @escaping (Item) -> M, style: Style) {
        self.style = style
        self.item = item
        self.message = message
        _isPresented = isPresented
    }

    /// Indicates if content is presented.
    private var contentIsPresented: Bool {
        item != nil && isPresented
    }

    private var animation: Animation {
        .easeInOut(duration: 1)
    }

    // MARK: - ViewModifier

    func body(content: Content) -> some View {
        ZStack {
            content
            ZStack {
                if let item = item, contentIsPresented {
                    overlay(item: item)
                        .transition(.asymmetric(insertion: .scale(scale: 0.5), removal: .opacity).animation(animation))
                }
            }
        }
            .onReceive(Timer.publish(every: 3, on: .main, in: .default).autoconnect().first()) { _ in
                isPresented = false
            }
    }

    private func overlay(item: Item) -> some View {
        HStack {
            Image(uiImage: style.image)
                .padding(.leading, 20)
            message(item)
                .font(Font.custom("DMSans-Bold", size: 18))
                .foregroundColor(.white)
                .padding(.trailing)
        }
            .frame(height: 66)
            .background(style.background(for: colorScheme))
            .cornerRadius(20)
            .edgesIgnoringSafeArea(.all)
            .shadow(color: Color.black.opacity(0.15), radius: 12, x: 0, y: 8)
    }
}

Using an asymmetric transition, I can achieve a scale upon insertion and an opacity fade upon removal. However, changing those transitions to ones like .slide or .move, completely breaks the transition.

Are certain transitions broken in SwiftUI? I've had great success with opacity and scale but the other ones don't seem to work.

CodePudding user response:

See my comment to question (above) and I'd move animation to container, like

ZStack {
    if let item = item, contentIsPresented {
        overlay(item: item)
            .transition(.asymmetric(insertion: .scale(scale: 0.5), removal: .opacity))
    }
}
.animation(animation, value: isPresented)   // << here !!
  • Related