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

Swift中如何创建可拖动UILabel?新增标签时保留已有标签位置

问题:新增可拖动UILabel时,已有标签回到初始位置

点击按钮添加可拖动的UILabel,单个标签运行正常,但创建第二个标签时,第一个标签会回到初始位置,如何保留已有标签的当前位置?

原代码

@IBAction func btnTextAction(_ sender: UIButton) {
    let label = UILabel()
    label.text = "Hello"
    label.textColor = .black
    label.textAlignment = .center
    label.translatesAutoresizingMaskIntoConstraints = false
    vMain.addSubview(label)

    NSLayoutConstraint.activate([
        label.centerXAnchor.constraint(equalTo: vMain.centerXAnchor),
        label.centerYAnchor.constraint(equalTo: vMain.centerYAnchor),
    ])
    
    let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))
    label.addGestureRecognizer(panGestureRecognizer)
    label.isUserInteractionEnabled = true
    
}

@objc func handlePanGesture(_ sender: UIPanGestureRecognizer) {
    guard sender.view != nil else { return }

    let translation = sender.translation(in: view)
    
    sender.view!.center = CGPoint(x: sender.view!.center.x + translation.x, y: sender.view!.center.y + translation.y)
    sender.setTranslation(CGPoint.zero, in: view)
}

问题原因

你给每个标签添加了固定的centerX/centerY约束,当新增标签激活约束时,Auto Layout会重新触发布局计算。由于拖动时只是直接修改了view.center属性,并没有更新约束,Auto Layout会优先遵循约束规则,把之前拖动过的标签拉回约束指定的初始位置。

解决方案

方案1:脱离Auto Layout管理(快速简单)

放弃给标签添加Auto Layout约束,直接用frame设置初始位置,让标签完全由代码控制位置,避免Auto Layout的布局干扰。

修改后的btnTextAction代码:

@IBAction func btnTextAction(_ sender: UIButton) {
    let label = UILabel()
    label.text = "Hello"
    label.textColor = .black
    label.textAlignment = .center
    // 初始位置设为vMain中心,手动计算frame
    let labelWidth: CGFloat = 100
    let labelHeight: CGFloat = 40
    label.frame = CGRect(
        x: vMain.center.x - labelWidth/2,
        y: vMain.center.y - labelHeight/2,
        width: labelWidth,
        height: labelHeight
    )
    vMain.addSubview(label)
    
    let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))
    label.addGestureRecognizer(panGestureRecognizer)
    label.isUserInteractionEnabled = true
}

拖动方法无需修改,直接修改center属性即可,不会被Auto Layout覆盖。

方案2:通过更新约束保留位置(规范的Auto Layout方案)

如果需要保留Auto Layout的布局能力,可通过保存每个标签的约束,拖动时更新约束的constant值来实现位置保留。

  1. 先定义一个字典用来关联标签和对应的约束:
var labelToConstraints: [UILabel: (NSLayoutConstraint, NSLayoutConstraint)] = [:]
  1. 修改btnTextAction,保存每个标签的center约束:
@IBAction func btnTextAction(_ sender: UIButton) {
    let label = UILabel()
    label.text = "Hello"
    label.textColor = .black
    label.textAlignment = .center
    label.translatesAutoresizingMaskIntoConstraints = false
    vMain.addSubview(label)
    
    // 创建并保存约束
    let centerXConstraint = label.centerXAnchor.constraint(equalTo: vMain.centerXAnchor)
    let centerYConstraint = label.centerYAnchor.constraint(equalTo: vMain.centerYAnchor)
    NSLayoutConstraint.activate([centerXConstraint, centerYConstraint])
    labelToConstraints[label] = (centerXConstraint, centerYConstraint)
    
    let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))
    label.addGestureRecognizer(panGestureRecognizer)
    label.isUserInteractionEnabled = true
}
  1. 修改拖动方法,更新约束的偏移量:
@objc func handlePanGesture(_ sender: UIPanGestureRecognizer) {
    guard let label = sender.view as? UILabel,
          let (centerXConstraint, centerYConstraint) = labelToConstraints[label] else { return }

    let translation = sender.translation(in: vMain)
    
    // 更新约束的constant值
    centerXConstraint.constant += translation.x
    centerYConstraint.constant += translation.y
    
    sender.setTranslation(CGPoint.zero, in: vMain)
}

这种方式下,每个标签的位置由约束的偏移量控制,新增标签时Auto Layout不会重置已有标签的约束,位置会被正确保留。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 02:42:47