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值来实现位置保留。
- 先定义一个字典用来关联标签和对应的约束:
var labelToConstraints: [UILabel: (NSLayoutConstraint, NSLayoutConstraint)] = [:]
- 修改
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 }
- 修改拖动方法,更新约束的偏移量:
@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
相关产品推荐
相关产品推荐

