iOS 自动布局:UITextView 在 UIScrollView 中的奇怪展开动画

iOS Autolayout: Oddly expand-animation of UITextView inside an UIScrollView

我正在尝试为 UIScrollView 中的 UITextView 的高度约束设置动画。当用户点击 "toggle" 按钮时,文本应该以动画形式从上到下显示。但不知何故,UIKit 在完整视图中消失了。

为了确保 "dynamic" 高度取决于内在内容大小,我停用了设置为零的高度限制。

 @IBAction func toggle() {
    layoutIfNeeded()
    UIView.animate(withDuration: 0.6, animations: { [weak self] in

        guard let self = self else {
            return
        }

        if self.expanded {
            NSLayoutConstraint.activate([self.height].compactMap { [=11=] })
        } else {
            NSLayoutConstraint.deactivate([self.height].compactMap { [=11=] })
        }
        self.layoutIfNeeded()
    })
    expanded.toggle()
}

此示例的完整代码可在我的 GitHub 存储库中找到:ScrollAnimationExample

正在审查您的 GitHub 存储库...

此问题是由于动画 view 引起的。您想要 运行 .animate() 在层次结构中的 "top-most" 视图上。

为此,您可以创建 ExpandableView 的新 属性,例如:

var topMostView: UIView?

然后从您的视图控制器设置 属性,或者...

为了保持您的 class 封装,让它找到最顶层的视图。将您的 toggle() 函数替换为:

@IBAction func toggle() {

    // we need to run .animate() on the "top" superview

    // make sure we have a superview
    guard self.superview != nil else {
        return
    }

    // find the top-most superview
    var mv: UIView = self
    while let s = mv.superview {
        mv = s
    }

    // UITextView has subviews, one of which is a _UITextContainerView,
    //  which also has a _UITextCanvasView subview.
    // If scrolling is disabled, and the TextView's height is animated to Zero,
    //  the CanvasView's height is instantly set to Zero -- so it disappears instead of animating.

    // So, when the view is "expanded" we need to first enable scrolling,
    //  and then animate the height (to Zero)
    // When the view is NOT expanded, we first disable scrolling
    //  and then animate the height (to its intrinsic content height)

    if expanded {
        textView.isScrollEnabled = true
        NSLayoutConstraint.activate([height].compactMap { [=11=] })
    } else {
        textView.isScrollEnabled = false
        NSLayoutConstraint.deactivate([height].compactMap { [=11=] })
    }

    UIView.animate(withDuration: 0.6, animations: {
        mv.layoutIfNeeded()
    })

    expanded.toggle()

}