首页 > 解决方案 > SwiftUI 像内置导航一样滑过动画

问题描述

我正在尝试复制 SwiftUI 的导航而不使用所有的黑盒魔法。但是,我在动画方面遇到了麻烦。直到第二次或第三次推送/弹出时才会发生动画。当它最终动画化时,很难描述它做了什么。但这绝对不是我所期望的。

我尝试了各种不同的动画,但通常是相同的行为。

struct RouterDemo: View {
    @State private var items: [Int] = Array(0..<50)
    @State private var selectedItem: Int?

    var body: some View {
        RouterStore(
            route: $selectedItem,
            state: { route in items.first(where: { $0 == route }) },
            content: { ItemsList(items: items, selectedItem: $0) },
            destination: { route, item in
                ItemDetail(item: item, selectedItem: route)
            }
        )
    }
}


public struct RouterStore<Destination, Content, Route, DestinationState>: View
where Destination: View,
      Content: View,
      Route: Hashable,
      DestinationState: Equatable {

    @Binding private var route: Route?
    private let toDestinationState: (Route) -> DestinationState?
    private let destination: (Binding<Route?>, DestinationState) -> Destination
    private let content: (Binding<Route?>) -> Content

    public init(
        route: Binding<Route?>,
        state toDestinationState: @escaping (Route) -> DestinationState?,
        @ViewBuilder content: @escaping (Binding<Route?>) -> Content,
        @ViewBuilder destination: @escaping (Binding<Route?>, DestinationState) -> Destination
    ) {
        self._route = route
        self.toDestinationState = toDestinationState
        self.destination = destination
        self.content = content
    }

    public var body: some View {
        GeometryReader { geometry in
            ZStack {
                content($route)
                wrappedDestination()
                    .frame(width: geometry.size.width)
                    .offset(
                        x: route == nil ? geometry.size.width : 0,
                        y: 0
                    )
                    .animation(self.animation)
            }
        }
    }

    private var animation: Animation = .easeIn(duration: 2)

    @ViewBuilder
    private func wrappedDestination() -> some View {
        if let _route = Binding($route),
           let _destinationState = toDestinationState(_route.wrappedValue) {
            ZStack {
                Group {
                    if #available(iOS 15.0, *) {
                        Color(uiColor: UIColor.systemBackground)
                    } else {
                        Color(UIColor.systemBackground)
                    }
                }
                .preferredColorScheme(.light)
                .ignoresSafeArea()
                self.destination($route, _destinationState)
            }
        } else {
            EmptyView()
        }
    }
}

struct ItemsList: View {
    let items: [Int]
    @Binding var selectedItem: Int?

    var body: some View {
        List {
            ForEach(items, id: \.self) { item in
                Button(
                    action: { selectedItem = item },
                    label: { Text(String(item)) }
                )
                    .contentShape(Rectangle())
            }
        }
    }
}

struct ItemDetail: View {
    let item: Int
    @Binding var selectedItem: Int?

    var body: some View {
        VStack {
            Text(String(item))
            Button(
                action: { selectedItem = nil },
                label: { Text("Back") }
            )
        }
    }
}

标签: iosswiftswiftuiswiftui-animation

解决方案


感谢 Asperi 提供的链接,我明白了。

将动画应用到容器并为动画提供要监视的值来修复它。


推荐阅读