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

SwiftUI视频编辑时间轴:拖拽调整时保持背景缩略图静止

SwiftUI视频编辑时间轴缩略图拖拽偏移问题

我用SwiftUI实现了一个视频编辑时间轴示例,通过HStack搭建了简易时间轴结构:左侧间隔区、右侧间隔区(黑色色块)和中间的trimmer操作区域。拖拽左右手柄可调整trimmer的宽度,两侧间隔区的宽度也会随之变化。

问题:调整trimmer尺寸时,其内部的背景缩略图(当前用不同颜色矩形模拟)会跟着移动,我希望这些缩略图保持静止,该如何修复?

拖拽时缩略图移动的效果

初始实现代码:

import SwiftUI

struct SampleTimeline: View {
    
    let viewWidth:CGFloat = 340 //Width of HStack container for Timeline
   
    @State var frameWidth:CGFloat = 280 //Width of trimmer
    
    var minWidth: CGFloat {
        2*chevronWidth + 10
    } //min Width of trimmer
    
    @State private var leftViewWidth:CGFloat = 20
    @State private var rightViewWidth:CGFloat = 20
    
    var chevronWidth:CGFloat {
        return 24
    }
    
    var body: some View {
        
        HStack(spacing:0) {
            Color.black
                .frame(width: leftViewWidth)
                .frame(height: 70)
            
            HStack(spacing: 0) {
            
                Image(systemName: "chevron.compact.left")
                    .frame(width: chevronWidth, height: 70)
                    .background(Color.blue)
                    .gesture(
                        DragGesture(minimumDistance: 0)
                            .onChanged({ value in
                                leftViewWidth = max(leftViewWidth + value.translation.width, 0)
                                
                                if leftViewWidth > viewWidth - minWidth - rightViewWidth {
                                    leftViewWidth = viewWidth - minWidth - rightViewWidth
                                }
                                   
                                frameWidth = max(viewWidth - leftViewWidth - rightViewWidth, minWidth)
                                
                            })
                            .onEnded { value in
                               
                            }
                    )
        
                Spacer()
                
                Image(systemName: "chevron.compact.right")
                    .frame(width: chevronWidth, height: 70)
                    .background(Color.blue)
                    .gesture(
                        DragGesture(minimumDistance: 0)
                            .onChanged({ value in
                                rightViewWidth = max(rightViewWidth - value.translation.width, 0)
                                
                                if rightViewWidth > viewWidth - minWidth - leftViewWidth {
                                    rightViewWidth = viewWidth - minWidth - leftViewWidth
                                }
                                
                                frameWidth = max(viewWidth - leftViewWidth - rightViewWidth, minWidth)
                            })
                            .onEnded { value in
                              
                            }
                    )
                 
            }
            .foregroundColor(.black)
            .font(.title3.weight(.semibold))
          
            .background {
                
                HStack(spacing:0) {
                    Rectangle().fill(Color.red)
                        .frame(width: 70, height: 60)
                    Rectangle().fill(Color.cyan)
                        .frame(width: 70, height: 60)
                    Rectangle().fill(Color.orange)
                        .frame(width: 70, height: 60)
                    Rectangle().fill(Color.brown)
                        .frame(width: 70, height: 60)
                    Rectangle().fill(Color.purple)
                        .frame(width: 70, height: 60)
                }
                
            }
            .frame(width: frameWidth)
            .clipped()
            
            Color.black
                .frame(width: rightViewWidth)
                .frame(height: 70)
        }
        .frame(width: viewWidth, alignment: .leading)
    }
}

#Preview {
    SampleTimeline()
}

更新:我已通过给缩略图设置偏移的方式解决了问题,但感觉这是临时方案。如果有更优的实现思路(比如利用蒙版同时控制trimmer框架宽度的方案),请提供参考。

我的临时解决方案代码:

import SwiftUI

struct SampleTimeline: View {
    
    let viewWidth:CGFloat = 340 //Width of HStack container for Timeline
   
    @State var frameWidth:CGFloat = 280 //Width of trimmer
    
    var minWidth: CGFloat {
        2*chevronWidth + 10
    } //min Width of trimmer
    
    @State private var leftViewWidth:CGFloat = 20
    @State private var rightViewWidth:CGFloat = 20
    @GestureState private var leftEndPanned = false
    @GestureState private var rightEndPanned = false
    
    var chevronWidth:CGFloat {
        return 24
    }
    
    var body: some View {
        
        HStack(spacing:0) {
            Color.clear
                .frame(width: leftViewWidth)
                .frame(height: 70)
            
            HStack(spacing: 0) {
            
                Image(systemName: "chevron.compact.left")
                    .frame(width: chevronWidth, height: 70)
                    .background(Color.blue)
                    .gesture(
                        DragGesture(minimumDistance: 0)
                            .updating($leftEndPanned, body: { _, state, _ in
                                state = true
                            })
                            .onChanged({ value in
                                leftViewWidth = max(leftViewWidth + value.translation.width, 0)
                                
                                if leftViewWidth > viewWidth - minWidth - rightViewWidth {
                                    leftViewWidth = viewWidth - minWidth - rightViewWidth
                                }
                                   
                                frameWidth = max(viewWidth - leftViewWidth - rightViewWidth, minWidth)
                                
                            })
                            .onEnded { value in
                               
                            }
                    )
        
                Spacer()
                
                Image(systemName: "chevron.compact.right")
                    .frame(width: chevronWidth, height: 70)
                    .background(Color.blue)
                    .gesture(
                        DragGesture(minimumDistance: 0)
                            .updating($rightEndPanned, body: { _, state, _ in
                                state = true
                            })
                            .onChanged({ value in
                                rightViewWidth = max(rightViewWidth - value.translation.width, 0)
                                
                                if rightViewWidth > viewWidth - minWidth - leftViewWidth {
                                    rightViewWidth = viewWidth - minWidth - leftViewWidth
                                }
                                
                                frameWidth = max(viewWidth - leftViewWidth - rightViewWidth, minWidth)
                            })
                            .onEnded { value in
                              
                            }
                    )
                 
            }
            .foregroundColor(.black)
            .font(.title3.weight(.semibold))
            .background {
                
                HStack(spacing:0) {
                    Rectangle().fill(Color.red)
                        .frame(width: 70, height: 60)
                    Rectangle().fill(Color.cyan)
                        .frame(width: 70, height: 60)
                    Rectangle().fill(Color.orange)
                        .frame(width: 70, height: 60)
                    Rectangle().fill(Color.brown)
                        .frame(width: 70, height: 60)
                    Rectangle().fill(Color.purple)
                        .frame(width: 70, height: 60)
                }
                .frame(width: viewWidth - leftViewWidth - rightViewWidth, alignment: .leading)
                .offset(x: -leftViewWidth)
                .background(Color.yellow)
                .clipped()
                
            }
            
            
            Color.clear
                .frame(width: rightViewWidth)
                .frame(height: 70)
        }
        .frame(width: viewWidth, alignment: .leading)
    }
}

#Preview {
    SampleTimeline()
}

内容的提问来源于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 05:35:56