首页 > 解决方案 > 我的 textView bottomAnchor 似乎不起作用?

问题描述

我有一个 textView 和一条线,我设置了没有约束的线框,并设置了带有约束的 textView 框架。简单地说,我想要的是 textView 跟随这条线,所以我把一个 bottomAnchor 放到 textView 等于该线的 topAnchor。然而,当我为该行设置动画时,textView 不跟随?我究竟做错了什么?

    var button = UIButton()
    var testLine = UIView()
    let textView = UITextView()
    var textViewBottomAnchorConstraint: NSLayoutConstraint?

    override func viewDidLoad() {
        super.viewDidLoad()

        testLine.backgroundColor = .black
        testLine.frame = CGRect(x: 0, y: 335, width: UIScreen.main.bounds.width, height: 10)
        view.addSubview(testLine)

        view.addSubview(textView)


        textView.frame = .zero//CGRect(x: CGFloat(integerLiteral: 16), y: CGFloat(integerLiteral: 300), width: CGFloat(integerLiteral: 282), height: CGFloat(integerLiteral: 35))
        textView.backgroundColor = UIColor.yellow
        textView.text = ""
        textView.font = UIFont(name: "Arial Rounded MT Bold", size: 15)
        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.isHidden = false

        textView.translatesAutoresizingMaskIntoConstraints = false
//        textView.bottomAnchor.constraint(equalTo: testLine.topAnchor, constant: 0).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor, constant: 20).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor, constant: -20).isActive = true
        textView.heightAnchor.constraint(equalToConstant: 40).isActive = true
        textViewBottomAnchorConstraint = textView.bottomAnchor.constraint(equalTo: testLine.topAnchor, constant: 0)
        textViewBottomAnchorConstraint?.isActive = true


        UIView.animate(withDuration: 2, delay: 2, options: .curveEaseIn, animations: {

            self.testLine.transform = CGAffineTransform.identity.translatedBy(x: 0, y: 30)

        }) { (true) in
            self.view.layoutIfNeeded()

        }
    }

标签: iosswiftconstraintsswift4

解决方案


正如@Vollan 正确所说,动画transform属性不是最佳选择。这是来自Apple 文档的引用:“在 iOS 8.0 及更高版本中,transform 属性不会影响 Auto Layout。自动布局会根据其未转换的框架计算视图的对齐矩形。” 因此属性的动画transform不会改变textView. 我建议您为frame属性设置动画,而不是transform.

但是,如果您切换到frame动画,它并不能解决您的所有问题。如果您将动画保留在viewDidLoad方法中,您可能会遇到非常奇怪的行为。原因是viewDidLoad视图本身尚未正确布局。在里面开始动画viewDidLoad可能会导致意想不到的结果。

最后你需要调整你的动画块。Apple 建议在动画块layoutIfNeeded 内应用。或者至少他们曾经推荐它,然后引入了自动布局 - 观看这个 WWDC 视频(从第 30 分钟开始)了解更多详细信息。

如果您在代码上方应用所有建议,则应如下所示:

    var button = UIButton()
    var testLine = UIView()
    let textView = UITextView()
    var textViewBottomAnchorConstraint: NSLayoutConstraint?
    var triggeredAnimation = false

    override func viewDidLoad() {
        super.viewDidLoad()

        testLine.backgroundColor = .black
        testLine.frame = CGRect(x: 0, y: 335, width: UIScreen.main.bounds.width, height: 10)
        view.addSubview(testLine)

        view.addSubview(textView)


        textView.frame = .zero//CGRect(x: CGFloat(integerLiteral: 16), y: CGFloat(integerLiteral: 300), width: CGFloat(integerLiteral: 282), height: CGFloat(integerLiteral: 35))
        textView.backgroundColor = UIColor.yellow
        textView.text = ""
        textView.font = UIFont(name: "Arial Rounded MT Bold", size: 15)
        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.isHidden = false

        textView.translatesAutoresizingMaskIntoConstraints = false
        //        textView.bottomAnchor.constraint(equalTo: testLine.topAnchor, constant: 0).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor, constant: 20).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor, constant: -20).isActive = true
        textView.heightAnchor.constraint(equalToConstant: 40).isActive = true
        textViewBottomAnchorConstraint = textView.bottomAnchor.constraint(equalTo: testLine.topAnchor, constant: 0)
        textViewBottomAnchorConstraint?.isActive = true
    }

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

        // viewDidAppear may be called several times during view controller lifecycle
        // triggeredAnimation ensures that animation will be called just once
        if self.triggeredAnimation {
            return
        }
        self.triggeredAnimation = true

        let oldFrame = self.testLine.frame
        UIView.animate(withDuration: 2, delay: 2, options: .curveEaseIn, animations: {
            self.testLine.frame = CGRect(x: oldFrame.minX, y: oldFrame.minY + 30, width: oldFrame.width,
                                         height: oldFrame.height)
            self.view.layoutIfNeeded()
        })
    }

推荐阅读