SwiftUI视图切换动态垂直滑动过渡效果实现求助
可以通过SwiftUI Transition实现该效果
你的需求完全可以通过SwiftUI的AnyTransition.asymmetric实现,它允许我们分别控制视图插入和移除时的过渡动画方向,结合保存的上一个视图索引就能精准匹配你要的滑动逻辑。
核心修改点:
- 使用
AnyTransition.asymmetric区分新视图插入、旧视图移除的动画方向 - 通过
ZStack同时渲染新旧视图,保证移除动画可见 - 用
withAnimation包裹状态变化,确保动画触发生效
修改后的完整代码:
enum ViewScreen: String, CaseIterable, Identifiable { case target1 case target2 case target3 case target4 case target5 var id: String { return self.rawValue } var index: Int { ViewScreen.allCases.firstIndex(of: self) ?? 0 } } // 示例视图(替换为你自己的View1-View5即可) struct View1: View { var body: some View { Text("Target 1").frame(maxWidth: .infinity, maxHeight: .infinity).background(.blue) } } struct View2: View { var body: some View { Text("Target 2").frame(maxWidth: .infinity, maxHeight: .infinity).background(.green) } } struct View3: View { var body: some View { Text("Target 3").frame(maxWidth: .infinity, maxHeight: .infinity).background(.orange) } } struct View4: View { var body: some View { Text("Target 4").frame(maxWidth: .infinity, maxHeight: .infinity).background(.purple) } } struct View5: View { var body: some View { Text("Target 5").frame(maxWidth: .infinity, maxHeight: .infinity).background(.pink) } } struct SidebarView: View { @Binding var target: ViewScreen var body: some View { VStack(spacing: 16) { ForEach(ViewScreen.allCases) { screen in Button(screen.rawValue) { withAnimation(.easeInOut(duration: 0.5)) { target = screen } } .padding() .background(target == screen ? .gray.opacity(0.3) : .clear) .cornerRadius(8) } } .padding() .frame(width: 150) .background(.gray.opacity(0.1)) } } struct ContentView: View { @State var target: ViewScreen = .target1 @State var previousTarget: ViewScreen = .target1 private var customTransition: AnyTransition { let isMovingUp = previousTarget.index > target.index // 判断是否为向上切换(如target3→target1) return AnyTransition.asymmetric( insertion: isMovingUp ? .move(edge: .bottom) : .move(edge: .top), removal: isMovingUp ? .move(edge: .bottom) : .move(edge: .top) ) .combined(with: .opacity) } var body: some View { HStack(alignment: .top, spacing: 0) { SidebarView(target: $target) ZStack { // 渲染上一个视图,用于展示移除动画 switch previousTarget { case .target1: View1() case .target2: View2() case .target3: View3() case .target4: View4() case .target5: View5() } // 渲染当前视图,用于展示插入动画 switch target { case .target1: View1() case .target2: View2() case .target3: View3() case .target4: View4() case .target5: View5() } } .transition(customTransition) } .onChange(of: target) { _ in previousTarget = target } } }
关键逻辑说明:
- 不对称过渡配置:
- 向上切换(如target3→target1):新视图从底部滑入,旧视图向底部滑出
- 向下切换(如target1→target3):新视图从顶部滑入,旧视图向顶部滑出
- ZStack的作用:同时保留新旧视图,确保移除动画不会因为视图被直接销毁而无法展示
- 动画触发:侧边栏按钮点击时用
withAnimation包裹状态更新,强制触发过渡动画
内容的提问来源于stack exchange,提问作者Vincent
相关产品推荐
相关产品推荐

