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

如何在SwiftUI中实现拖动调整HStack内相邻视图的尺寸?

SwiftUI拖动调整视图尺寸导致CPU占用100%问题解决

我想实现这样的效果:拖动中间的绿色视图,左侧视图宽度随拖动距离增加而变宽,右侧视图宽度随拖动距离增加而变窄,松开拖动后视图保持调整后的尺寸。

但我写的代码会导致CPU占用率直接拉满到100%,代码如下:

import SwiftUI

struct ContentView: View {
    @State private var xMovement: CGFloat = 0.0
    @State private var completed: Bool = false
    
    var body: some View {
        HStack(spacing: 20) {
            ReactanceView(width: 100 + xMovement, color: .blue)
            DraggingView(xMovement: $xMovement)
                .gesture(DragGesture()
                    .onChanged { value in
                        // 记录拖动时的X轴偏移
                        xMovement = value.translation.width
                        completed = false
                    }
                    .onEnded { _ in
                        // 拖动结束时重置X轴偏移(注:当前已注释)
                        //xMovement = 0.0
                        completed = false
                    }
                )
            ReactanceView(width: 100 - xMovement, color: .blue)
        }
        .padding()
    }
}

struct ReactanceView: View {
    var width: CGFloat
    var color: Color
    
    var body: some View {
        Text("Reactance")
            .padding()
            .frame(width: width, height: 100) // 取消注释这行就会引发无限循环
            .background(color)
            .foregroundColor(.white)
    }
}

struct DraggingView: View {
    @Binding var xMovement: CGFloat
    
    var body: some View {
        Text("Drag me!")
            .padding()
            .frame(width: 100, height: 100)
            .background(Color.green)
            .foregroundColor(.white)
            .offset(x: xMovement, y: 0)
            .animation(.easeInOut)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

问题原因

核心问题在于**DragGesture的translation是相对于手势起始位置的实时偏移**。当左右两侧的ReactanceView宽度变化时,整个HStack的布局会重新计算,导致中间DraggingView的位置发生变化,进而让DragGesture的起始位置不断更新,translation的值也跟着波动。这会触发xMovement反复更新,再次引发布局重算,形成无限循环,直接把CPU跑满。

解决方案

我们需要记录拖动的累计偏移量,而不是依赖实时的translation。具体做法是:

  1. 新增一个状态变量存储拖动开始时的基准偏移量
  2. 拖动开始时,把当前的xMovement赋值给基准偏移量
  3. 拖动过程中,用基准偏移量加上实时的translation.width来更新xMovement

修改后的代码如下:

import SwiftUI

struct ContentView: View {
    @State private var xMovement: CGFloat = 0.0
    @State private var baseOffset: CGFloat = 0.0 // 新增基准偏移量
    
    var body: some View {
        HStack(spacing: 20) {
            ReactanceView(width: max(20, 100 + xMovement), color: .blue) // 限制最小宽度避免视图消失
            DraggingView(xMovement: $xMovement)
                .gesture(DragGesture()
                    .onChanged { value in
                        // 用基准偏移量 + 实时拖动距离计算最终偏移
                        xMovement = baseOffset + value.translation.width
                    }
                    .onEnded { _ in
                        // 拖动结束后更新基准偏移量,保存当前状态
                        baseOffset = xMovement
                    }
                )
            ReactanceView(width: max(20, 100 - xMovement), color: .blue) // 限制最小宽度
        }
        .padding()
    }
}

struct ReactanceView: View {
    var width: CGFloat
    var color: Color
    
    var body: some View {
        Text("Reactance")
            .padding()
            .frame(width: width, height: 100)
            .background(color)
            .foregroundColor(.white)
    }
}

struct DraggingView: View {
    @Binding var xMovement: CGFloat
    
    var body: some View {
        Text("Drag me!")
            .padding()
            .frame(width: 100, height: 100)
            .background(Color.green)
            .foregroundColor(.white)
            .offset(x: xMovement, y: 0)
            .animation(.easeInOut, value: xMovement) // 仅在xMovement变化时触发动画
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

额外优化点

  • 给左右视图添加max(20, ...)限制最小宽度,避免拖动时视图被压缩到消失
  • 把DraggingView的动画改为animation(_:value:),仅当xMovement变化时才触发动画,减少不必要的视图刷新

内容的提问来源于stack exchange,提问作者János

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 06:15:55