更改锚点时保持视图的位置

Maintaining the location of a view when changing its anchor point

我有一个以编程方式创建并设置了约束的 UILabel:

NSLayoutConstraint.activate([
            theLabel.topAnchor.constraint(equalTo: otherView.topAnchor),
            theLabel.leadingAnchor.constraint(equalTo: otherView.trailingAnchor, constant: otherView.frame.width/2),
            theLabel.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5),
            theLabel.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.2),
])

然后我想将标签添加到场景中:

theLabel.transform = CGAffineTransform(scaleX: 0.0001, y: 0.0001)
theLabel.isHidden = false

UIView.animate(withDuration: 0.8, delay: 0.0, options: UIViewAnimationOptions(), animations: {
    self.theLabel.transform = CGAffineTransform(scaleX: 1.0, y: 1.0) //or CGAffineTransform.identity
}, completion: nil)

但标签是左对齐的,它从中心开始生长,所以我更改了锚点以修复问题,但这导致标签出现在比应有的位置更靠右的位置。 (我理解这是因为我现在描述它的位置是在视图的左侧,而不是在中心)

theLabel.layer.anchorPoint = CGPoint(x: 0.0, y: 0.5)

我试图在设置锚点后只替换它的框架和中心点(另外,我先尝试框架,然后尝试中心)但是这些解决方案都没有对视图在应用程序中的显示方式产生任何影响:

let oldFrame = theLabel.frame
theLabel.layer.anchorPoint = CGPoint(x: 0.0, y: 0.5)
theLabel.frame = oldFrame

let oldCenter = theLabel.center
theLabel.layer.anchorPoint = CGPoint(x: 0.0, y: 0.5)
theLabel.center = oldCenter

还有其他建议或解释可以帮助我理解如何在更改锚点时有效地保留视图的位置吗?谢谢大家!

一种方法:将标签的前导约束保存在一个变量中,然后更改其常量以说明锚点的变化。

您应该可以 运行 这个 as-is。点击按钮使标签动画化。

class LabelTransformViewController: UIViewController {

    let btn: UIButton = {
        let b = UIButton()
        b.translatesAutoresizingMaskIntoConstraints = false
        b.setTitle("Tap Me", for: .normal)
        b.backgroundColor = .red
        return b
    }()

    let theLabel: UILabel = {
        let v = UILabel()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = UIColor.cyan
        v.text = "This is a label"
        return v
    }()

    let otherView: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = UIColor.yellow
        return v
    }()

    var leadingConstraint = NSLayoutConstraint()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor(red: 1.0, green: 0.85, blue: 0.5, alpha: 1.0)

        view.addSubview(btn)

        btn.addTarget(self, action: #selector(didTap(_:)), for: .touchUpInside)

        btn.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        btn.topAnchor.constraint(equalTo: view.topAnchor, constant: 20.0).isActive = true

        view.addSubview(otherView)

        NSLayoutConstraint.activate([
            otherView.topAnchor.constraint(equalTo: view.topAnchor, constant: 100.0),
            otherView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0),
            otherView.widthAnchor.constraint(equalToConstant: 30.0),
            otherView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.2),
            ])

        view.addSubview(theLabel)

        NSLayoutConstraint.activate([
            theLabel.topAnchor.constraint(equalTo: otherView.topAnchor),
            theLabel.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5),
            theLabel.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.2),
            ])

        leadingConstraint = theLabel.leadingAnchor.constraint(equalTo: otherView.trailingAnchor, constant: otherView.frame.width/2)
        leadingConstraint.isActive = true

        // presumably, you would start with theLabel hidden
        // but we're leaving it visible so we can see its original size and position
//      theLabel.isHidden = true

    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        // move the anchorPoint from the label frame's center (the default)
        // to the left edge, vertical center
        theLabel.layer.anchorPoint = CGPoint(x: 0.0, y: 0.5)

        // adjust the label's leading constraint by 1/2 of its width
        // to account for the anchorPoint change
        // need this in viewDidAppear so the frame is already set
        leadingConstraint.constant -= (theLabel.frame.size.width * 0.5)
    }

    @objc func didTap(_ sender: Any?) -> Void {

        theLabel.transform = CGAffineTransform(scaleX: 0.0001, y: 0.0001)
        theLabel.isHidden = false

        UIView.animate(withDuration: 0.8, delay: 0.0, options: UIViewAnimationOptions(), animations: {
            self.theLabel.transform = CGAffineTransform(scaleX: 1.0, y: 1.0) //or CGAffineTransform.identity
        }, completion: nil)

    }

}