You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI原生Sheet内导航时如何实现高度自适应动画?

SwiftUI原生Sheet实现导航时高度自适应平滑动画

可以通过SwiftUI原生组件实现你要的效果,核心是在Sheet内部用NavigationStack管理导航流程,配合动态的presentationDetents设置,再通过withAnimation触发高度变化的平滑过渡,无需自定义Sheet视图。

实现代码示例

import SwiftUI

// 定义Sheet内的导航步骤
enum SheetViewStep: Identifiable {
    case first, second, third
    var id: Self { self }
}

struct ContentView: View {
    @State private var currentSheetStep: SheetViewStep?
    
    var body: some View {
        Button("打开Sheet") {
            currentSheetStep = .first
        }
        .sheet(item: $currentSheetStep) { _ in
            NavigationStack {
                sheetContentView(for: currentSheetStep ?? .first)
                    .navigationTitle(titleForStep(currentSheetStep))
                    .navigationBarTitleDisplayMode(.inline)
            }
            // 动态设置Sheet高度
            .presentationDetents([.height(heightForStep(currentSheetStep))])
            .presentationDragIndicator(.visible)
        }
    }
    
    // 根据步骤返回对应视图
    @ViewBuilder
    private func sheetContentView(for step: SheetViewStep) -> some View {
        switch step {
        case .first:
            VStack(spacing: 30) {
                Text("第一步\n矮高度视图")
                    .multilineTextAlignment(.center)
                    .font(.title)
                Button("进入第二步") {
                    withAnimation {
                        currentSheetStep = .second
                    }
                }
            }
            .padding()
        case .second:
            VStack(spacing: 30) {
                Text("第二步\n中等高度视图")
                    .multilineTextAlignment(.center)
                    .font(.title)
                Button("进入第三步") {
                    withAnimation {
                        currentSheetStep = .third
                    }
                }
                Button("返回第一步") {
                    withAnimation {
                        currentSheetStep = .first
                    }
                }
            }
            .padding()
        case .third:
            VStack(spacing: 30) {
                Text("第三步\n最高高度视图")
                    .multilineTextAlignment(.center)
                    .font(.title)
                Button("返回第二步") {
                    withAnimation {
                        currentSheetStep = .second
                    }
                }
            }
            .padding()
        }
    }
    
    // 对应步骤的标题
    private func titleForStep(_ step: SheetViewStep?) -> String {
        switch step {
        case .first: return "第一步"
        case .second: return "第二步"
        case .third: return "第三步"
        default: return ""
        }
    }
    
    // 对应步骤的Sheet高度
    private func heightForStep(_ step: SheetViewStep?) -> CGFloat {
        switch step {
        case .first: return 220
        case .second: return 400
        case .third: return 600
        default: return 220
        }
    }
}

关键实现要点

  • 内部导航管理:用NavigationStack在Sheet内部承载所有导航视图,避免了使用isPresented时的视图重载,或item绑定错误导致的Sheet关闭重开问题。
  • 动态高度+动画:通过presentationDetents根据当前步骤动态设置Sheet高度,切换步骤时用withAnimation包裹状态变更,触发高度变化的平滑过渡动画。
  • 状态连续性:用@State变量currentSheetStep管理导航流程,保证Sheet始终处于打开状态,仅内部视图和高度发生变化。

如果需要根据视图内容自动计算高度(而非固定值),可以结合GeometryReader获取内容的实际高度,再传递给presentationDetents,但固定高度的方式在布局稳定性上表现更好。

内容的提问来源于stack exchange,提问作者Froig

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 08:15:44