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

如何为Safe Area内的内容添加拖拽弹性展开功能?

实现拖拽Capsule弹性展开Bottom Sheet效果

要实现拖拽胶囊手柄展开/收起底部区域的弹性动画效果,可以通过DragGesture结合弹簧动画来实现,以下是修改后的完整代码:

struct ContentView: View {
    @State private var isExpanded = false
    @State private var dragOffset: CGFloat = 0
    
    // 定义收起和展开状态的高度
    private let collapsedHeight: CGFloat = 80
    private let expandedHeight: CGFloat = 160
    
    var body: some View {
        ScrollView {
            VStack {
                Image(systemName: "globe")
                    .imageScale(.large)
                    .foregroundColor(.accentColor)
                Text("Hello, world!")
            }
            .frame(maxWidth: .infinity)
            .padding()
        }
        .safeAreaInset(edge: .bottom) {
            VStack(spacing: 24) {
                Capsule()
                    .frame(width: 50, height: 5)
                    .opacity(0.5)
                    // 添加拖拽手势
                    .gesture(
                        DragGesture()
                            .onChanged { value in
                                // 限制拖拽范围,仅处理向上拖拽的有效偏移
                                dragOffset = max(-(expandedHeight - collapsedHeight), value.translation.height)
                            }
                            .onEnded { value in
                                // 设置切换状态的拖拽阈值
                                let threshold = (expandedHeight - collapsedHeight) * 0.3
                                withAnimation(.spring(response: 0.4, dampingFraction: 0.7)) {
                                    if abs(dragOffset) > threshold {
                                        isExpanded.toggle()
                                    }
                                    dragOffset = 0
                                }
                            }
                    )
                
                HStack(spacing: 16) {
                    Image(systemName: "list.bullet")
                    Spacer()
                    VStack {
                        Text("Track 2")
                    }
                    .font(.caption)
                    Spacer()
                    Image(systemName: "backward.fill")
                    Image(systemName: "play.fill")
                    Image(systemName: "forward.fill")
                    Spacer()
                    Button {
                        withAnimation(.spring(response: 0.4, dampingFraction: 0.7)) {
                            isExpanded.toggle()
                        }
                    } label: {
                        Image(systemName: "ellipsis.circle")
                    }
                }
                
                // 拖拽过程中也显示展开内容,提升交互流畅度
                if isExpanded || dragOffset != 0 {
                    HStack {
                        Image(systemName: "airplayaudio")
                        Image(systemName: "timer")
                        Spacer()
                        Text("Artist")
                    }
                }
            }
            .padding([.bottom, .horizontal])
            .padding(.top, 8)
            .background(.ultraThinMaterial)
            // 根据状态和拖拽偏移量动态调整底部区域高度
            .frame(height: isExpanded ? expandedHeight : collapsedHeight + dragOffset)
        }
    }
}

关键实现说明:

  • 拖拽手势控制:给胶囊手柄添加DragGesture,拖拽过程中实时更新偏移量并限制范围,松开时根据拖拽距离阈值判断是否切换展开状态。
  • 弹性动画效果:使用.spring动画实现自然的弹性拉伸/收缩效果,可通过调整response和dampingFraction参数自定义动画的快慢和弹性程度。
  • 动态高度适配:底部区域高度根据展开状态和拖拽偏移量实时计算,拖拽时跟随手势拉伸,松开后平滑过渡到目标状态。
  • 交互体验优化:拖拽过程中提前显示展开内容,避免状态切换时的突兀感。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 20:02:21