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

如何将可拖拽文本限制在SwiftUI红色矩形框范围内?

实现SwiftUI拖拽文本在矩形框内的范围限制

要让文本只能在红色矩形框内拖拽,核心是先获取文本的实际尺寸,再计算出它在容器内的可移动边界,最后在拖拽过程中约束偏移量。直接看修改后的代码:

private struct TextSizeKey: PreferenceKey {
    static var defaultValue: CGSize = .zero
    static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
        value = nextValue()
    }
}

struct DraginContainerView: View {
    @State var viewState = CGSize.zero
    @State private var textSize = CGSize.zero
    // 把容器尺寸抽成常量,方便后续调整
    private let containerSize = CGSize(width: 200, height: 200)

    var body: some View {
        // 把原VStack改成ZStack,让文本叠加在矩形上方
        ZStack {
            Rectangle()
                .fill(.red)
                .frame(width: containerSize.width, height: containerSize.height)
            
            Text("My Text")
                .foregroundColor(.black)
                .offset(x: viewState.width, y: viewState.height)
                // 通过GeometryReader获取文本的实际尺寸
                .background(
                    GeometryReader { proxy in
                        Color.clear
                            .preference(key: TextSizeKey.self, value: proxy.size)
                    }
                )
                .gesture(
                    DragGesture()
                        .onChanged { value in
                            // 计算X方向的可移动边界:容器半宽减去文本半宽
                            let maxXOffset = (containerSize.width - textSize.width) / 2
                            let minXOffset = -maxXOffset
                            // 约束X偏移,不让文本超出容器
                            let clampedX = min(max(value.translation.width, minXOffset), maxXOffset)
                            
                            // Y方向同理
                            let maxYOffset = (containerSize.height - textSize.height) / 2
                            let minYOffset = -maxYOffset
                            let clampedY = min(max(value.translation.height, minYOffset), maxYOffset)
                            
                            viewState = CGSize(width: clampedX, height: clampedY)
                        }
                        .onEnded { value in
                            // 拖拽结束时也要做一次约束,防止手势收尾时超出范围
                            let maxXOffset = (containerSize.width - textSize.width) / 2
                            let minXOffset = -maxXOffset
                            let clampedX = min(max(value.translation.width, minXOffset), maxXOffset)
                            
                            let maxYOffset = (containerSize.height - textSize.height) / 2
                            let minYOffset = -maxYOffset
                            let clampedY = min(max(value.translation.height, minYOffset), maxYOffset)
                            
                            viewState = CGSize(width: clampedX, height: clampedY)
                        }
                )
        }
        // 接收文本尺寸的更新
        .onPreferenceChange(TextSizeKey.self) { newValue in
            textSize = newValue
        }
    }
}

关键修改点说明

  • 容器布局调整:把原代码的VStack换成ZStack,让文本和矩形重叠,确保拖拽区域在矩形内部。
  • 获取文本尺寸:用GeometryReader+自定义PreferenceKey拿到文本的实际宽高,这是计算拖拽边界的基础——毕竟要保证文本的边缘不超出矩形框。
  • 约束拖拽偏移:在拖拽的onChanged和onEnded阶段,用min和max函数把偏移量限制在允许范围内,确保文本始终在矩形内部移动。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 20:00:01