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

SwiftUI实现拖动边缘收缩视图的视频修剪器UI问题

SwiftUI视频修剪器UI实现问题

问题描述

我正在SwiftUI中实现视频修剪器UI,目前遇到两个核心问题:

  1. 现有代码能实现左侧按钮拖动移动,但拖动边缘时无法让SimpleTrimmer视图收缩,调整主HStack的偏移量、宽度等操作均无效
  2. 给左侧修剪器添加边界条件后,拖动至右侧边界时操作出现严重卡顿

现有实现代码

struct SimpleTrimmer: View {
    @State private var startPosition: CGFloat = 0
    @GestureState private var isDragging: Bool = false
    
    @State private var lastStartPosition: CGFloat = .zero
    @State private var frameWidth:CGFloat = 300
    
    var body: some View {
        HStack(spacing: 10) {
                Image(systemName: "chevron.compact.left")
                    .frame(height:70)
                    .frame(width:20)
                    .padding(.horizontal, 5)
                    .background(Color.blue)
                    .offset(x: startPosition)
                    .gesture(
                        DragGesture(minimumDistance: 0)
                            .updating($isDragging, body: { value, out, transaction in
                                out = true
                                
                            })
                            .onChanged { value in
                                let translation = value.translation.width
                                startPosition = translation + lastStartPosition
                            }.onEnded { _ in
                                lastStartPosition = startPosition
                                NSLog("Last start position \(lastStartPosition)")
                            }
                    )
            
                
                Spacer()
                
                Image(systemName: "chevron.compact.right")
                .frame(height:70)
                .frame(width:20)
                .padding(.horizontal, 5)
                .background(Color.blue)
            }
            .foregroundColor(.black)
            .font(.title3.weight(.semibold))
            .padding(.horizontal, 7)
            .padding(.vertical, 3)
            .background(.yellow)
            .clipShape(RoundedRectangle(cornerRadius: 7))
            .frame(width: frameWidth)
          //  .offset(x: startPosition)
            .onGeometryChange(for: CGFloat.self) { proxy in
                proxy.size.width
            } action: { width in
                print("width = \(width)")
            }
    }
}

添加边界条件后的代码片段

let minWidth: CGFloat = 10
    ....
    ....

    HStack(spacing: 10) {
            Image(systemName: "chevron.compact.left")
                .frame(width: 30, height: 70)
                .background(Color.blue)
                .offset(x: leftAdjustment)
                .gesture(
                    DragGesture(minimumDistance: 0)
                        .updating($leftDragOffset) { value, state, trans in
                            var dragWidth = value.translation.width
                            
                            if dragWidth + leftAdjustment > frameWidth - rightAdjustment - minWidth {
                                dragWidth = frameWidth - rightAdjustment - minWidth - leftAdjustment
                            }
                            
                            state = dragWidth
                        }
                        .onEnded { value in
                            leftOffset = max(0, leftOffset + value.translation.width)
                        }
                )

解决方案

核心是调整视图宽度与拖动偏移的绑定逻辑,避免状态冲突,同时优化边界校验的实时性:

修改后的完整代码

struct SimpleTrimmer: View {
    // 左侧拖动偏移量(控制视图左移+宽度收缩)
    @State private var leftOffset: CGFloat = 0
    // 右侧拖动偏移量(控制视图右移+宽度收缩)
    @State private var rightOffset: CGFloat = 0
    // 视图初始基础宽度
    private let baseWidth: CGFloat = 300
    // 视图最小允许宽度
    private let minWidth: CGFloat = 100
    
    var body: some View {
        HStack(spacing: 10) {
            Image(systemName: "chevron.compact.left")
                .frame(width: 30, height: 70)
                .background(Color.blue)
                .gesture(
                    DragGesture(minimumDistance: 0)
                        .onChanged { value in
                            // 计算新的左侧偏移量
                            let newLeftOffset = leftOffset + value.translation.width
                            // 校验边界:确保视图宽度不小于最小值,且偏移量非负
                            leftOffset = max(0, min(newLeftOffset, baseWidth + rightOffset - minWidth))
                        }
                )
            
            Spacer()
            
            Image(systemName: "chevron.compact.right")
                .frame(width: 30, height: 70)
                .background(Color.blue)
                .gesture(
                    DragGesture(minimumDistance: 0)
                        .onChanged { value in
                            let newRightOffset = rightOffset - value.translation.width
                            rightOffset = max(0, min(newRightOffset, baseWidth - leftOffset - minWidth))
                        }
                )
        }
        .foregroundColor(.black)
        .font(.title3.weight(.semibold))
        .padding(.horizontal, 7)
        .padding(.vertical, 3)
        .background(.yellow)
        .clipShape(RoundedRectangle(cornerRadius: 7))
        // 绑定视图偏移与宽度,实现拖动收缩效果
        .offset(x: leftOffset)
        .frame(width: baseWidth - leftOffset + rightOffset)
    }
}

关键优化点

  1. 合并偏移与宽度控制:不再单独给按钮加偏移,而是通过调整整个视图的offset和width实现收缩,避免视图层级冲突
  2. 实时边界校验:在onChanged中直接计算并限制偏移量,确保视图宽度始终符合最小要求,避免无效状态更新
  3. 简化状态管理:移除冗余的临时变量和GestureState,直接用核心状态变量管理拖动逻辑,减少重绘触发次数

修改后,拖动左右按钮时会同步收缩视图宽度,边界拖动也不会出现卡顿,所有计算高效且实时。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 07:19:59