在SwiftUI中,通过动画将视图向上滑动并在点击时隐藏

12

我创建了一个横幅修改器,可以从顶部显示横幅。这个动画效果很好。然而,当我点击以将其关闭时,它不会进行任何动画,只是隐藏起来,尽管点击手势操作已经使用withAnimation包装了它。

struct BannerModifier: ViewModifier {
    @Binding var model: BannerData?
    
    func body(content: Content) -> some View {
        content.overlay(
            Group {
                if model != nil {
                    VStack {
                        HStack(alignment: .firstTextBaseline) {
                            Image(systemName: "exclamationmark.triangle.fill")
                            VStack(alignment: .leading) {
                                Text(model?.title ?? "")
                                    .font(.headline)
                                if let message = model?.message {
                                    Text(message)
                                        .font(.footnote)
                                }
                            }
                        }
                        .padding()
                        .frame(minWidth: 0, maxWidth: .infinity)
                        .foregroundColor(.white)
                        .background(.red)
                        .cornerRadius(10)
                        .shadow(radius: 10)
                        Spacer()
                    }
                    .padding()
                    .animation(.easeInOut)
                    .transition(AnyTransition.move(edge: .top).combined(with: .opacity))
                    .onTapGesture {
                        withAnimation {
                            model = nil
                        }
                    }
                    .gesture(
                        DragGesture()
                            .onChanged { _ in
                                withAnimation {
                                    model = nil
                                }
                            }
                    )
                }
            }
        )
    }
}

struct BannerData: Identifiable {
    let id = UUID()
    let title: String
    let message: String?
}

在这里输入图像描述

在触摸手势中,我清除了模型,但它没有动画效果。 它只是立即隐藏。 我该如何使它动画滑动,以便与显示时向下滑动相反的方式上滑? 如果我能使拖动手势交互式,就像本机通知一样,那就太好了。


你在哪里添加了修饰符?它在 Xcode 12.1 和 iOS 14.1 上运行良好。 - Raja Kishan
2个回答

15

移除视图时容器总是会有动画效果,所以为了修复您的修饰符,需要在一些辅助容器上应用.animation(注意:Group实际上不是一个真正的容器)。

演示

这是修正后的变体。

struct BannerModifier: ViewModifier {
    @Binding var model: BannerData?
    
    func body(content: Content) -> some View {
        content.overlay(
            VStack {               // << holder container !!
                if model != nil {
                    VStack {
                        HStack(alignment: .firstTextBaseline) {
                            Image(systemName: "exclamationmark.triangle.fill")
                            VStack(alignment: .leading) {
                                Text(model?.title ?? "")
                                    .font(.headline)
                                if let message = model?.message {
                                    Text(message)
                                        .font(.footnote)
                                }
                            }
                        }
                        .padding()
                        .frame(minWidth: 0, maxWidth: .infinity)
                        .foregroundColor(.white)
                        .background(Color.red)
                        .cornerRadius(10)
                        .shadow(radius: 10)
                        Spacer()
                    }
                    .padding()
                    .transition(AnyTransition.move(edge: .top).combined(with: .opacity))
                    .onTapGesture {
                        withAnimation {
                            model = nil
                        }
                    }
                    .gesture(
                        DragGesture()
                            .onChanged { _ in
                                withAnimation {
                                    model = nil
                                }
                            }
                    )
                }
            }
            .animation(.easeInOut)         // << here !!
        )
    }
}

在 Xcode 12.1 / iOS 14.1 上测试并测试视图:

struct TestBannerModifier: View {
    @State var model: BannerData?
    var body: some View {
        VStack {
            Button("Test") { model = BannerData(title: "Error", message: "Fix It!")}
            Button("Reset") { model = nil }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .modifier(BannerModifier(model: $model))
    }
}

运行得非常好,谢谢!顺便说一句,它对“Group”也起作用,我不明白你为什么不喜欢它。对我来说,当横幅广告不显示时,我宁愿保留“Group”,这样就少了一个不必要的容器,但我可能错过了什么。再次感谢! - TruMan1
1
此修复引入了一个已弃用的修饰符.animation(.easeInOut) - undefined

1
这是一个稍微改进过的横幅,由Asperi发布。 希望能对某些人有所帮助。
import SwiftUI

public class BannerData {
    public enum BannerType {
        case warning, error, success

        var textColor: Color {
            switch self {
            case .warning:
                return .black
            case .error:
                return .white
            case .success:
                return .white
            }
        }

        var backgroundColor: Color {
            switch self {
            case .warning:
                return .yellow
            case .error:
                return .red
            case .success:
                return .green
            }
        }

        var icon: String {
            switch self {
            case .warning:
                return "exclamationmark.triangle.fill"
            case .error:
                return "exclamationmark.circle.fill"
            case .success:
                return "checkmark.circle.fill"
            }
        }
    }

    var type: BannerType = .success
    let title: String
    let message: String?

    public init(title: String, message: String? = nil, type: BannerType) {
        self.title = title
        self.message = message
        self.type = type
    }
}

public struct BannerModifier: ViewModifier {
    @Binding var model: BannerData?

    public init(model: Binding<BannerData?>) {
        _model = model
    }

    public func body(content: Content) -> some View {
        content.overlay(
            VStack {
                if model != nil {
                    VStack {
                        HStack(alignment: .firstTextBaseline) {
                            Image(systemName: model?.type.icon ?? BannerData.BannerType.success.icon)
                            VStack(alignment: .leading) {
                                Text(model?.title ?? "")
                                    .font(.headline)
                                if let message = model?.message {
                                    Text(message)
                                        .font(.footnote)
                                }
                            }
                            Spacer()
                        }
                        .padding()
                        .frame(minWidth: 0, maxWidth: .infinity)
                        .foregroundColor(.white)
                        .background(model?.type.backgroundColor ?? .clear)
                        .cornerRadius(10)
                        .shadow(radius: 10)
                        Spacer()
                    }
                    .padding()
                    .transition(AnyTransition.move(edge: .top).combined(with: .opacity))
                    .onTapGesture {
                        withAnimation {
                            model = nil
                        }
                    }
                    .gesture(
                        DragGesture()
                            .onChanged { _ in
                                withAnimation {
                                    model = nil
                                }
                            }
                    )
                }
            }
            .animation(.spring())
        )
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接