如何在堆栈视图中动态调整文本视图的大小

How to dynamically resize text view inside a stack view

我正在尝试在堆栈视图中显示动态大小的 UITextView,但文本视图未根据内容的大小进行调整。

首先我有排列好的子视图:

class InfoView: UIView {
    private var title: String!
    private var detail: String!
    private var titleLabel: UILabel!
    private var detailTextView: UITextView!
    
    init(infoModel: InfoModel) {
        self.title = infoModel.title
        self.detail = infoModel.detail
        super.init(frame: .zero)
        
        configure()
        setConstraint()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func configure() {
        titleLabel = UILabel()
        titleLabel.text = title
        titleLabel.font = .rounded(ofSize: titleLabel.font.pointSize, weight: .bold)
        titleLabel.textColor = .lightGray
        titleLabel.sizeToFit()
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(titleLabel)
        
        detailTextView = UITextView()
        detailTextView.sizeToFit()
        detailTextView.text = detail
        detailTextView.font = UIFont.systemFont(ofSize: 19)
        detailTextView.isEditable = false
        detailTextView.textColor = .lightGray
        detailTextView.isUserInteractionEnabled = false
        detailTextView.isScrollEnabled = false
        detailTextView.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(detailTextView)
    }
    
    private func setConstraint() {
        NSLayoutConstraint.activate([
            titleLabel.topAnchor.constraint(equalTo: self.topAnchor),
            titleLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 5),
            titleLabel.heightAnchor.constraint(equalToConstant: 40),
            
            detailTextView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor),
            detailTextView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
            detailTextView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
            detailTextView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
        ])
    }
}

然后我在视图控制器中实现堆栈视图:

class MyViewController: UIViewController {
    var infoModelArr: [InfoModel]!
    var stackView: UIStackView!
    var scrollView: UIScrollView!
    
    init(infoModelArr: [InfoModel]) {
        self.infoModelArr = infoModelArr
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        var infoViewArr = [InfoView]()
        for infoModel in infoModelArr {
            let infoView = InfoView(infoModel: infoModel)
            infoViewArr.append(infoView)
        }
        
        stackView = UIStackView(arrangedSubviews: infoViewArr)
        stackView.axis = .vertical
        stackView.spacing = 10
        stackView.distribution = .fillProportionally
        stackView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(stackView)
        
        NSLayoutConstraint.activate([
            stackView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            stackView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor),
            stackView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor),
        ])
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        scrollView.contentSize = stackView.bounds.size
    }
}

最后,我调用视图控制器如下:

let myVC = MyViewController(infoModelArr: [InfoModel(title: "title", detail: "detail"), InfoModel(title: "title", detail: "detail")])
self.present(myVC, animated: true, completion: nil)

值得注意的是,如果我用单个排列的子视图实例化堆栈视图,堆栈视图的高度似乎是动态调整的,但是一旦引入 2 个或更多子视图,高度就不会反映内容。

当我尝试设置 InfoView

的固有大小时
override func layoutSubviews() {
    super.layoutSubviews()
    height = titleLabel.bounds.height + detailTextView.bounds.height
}

var height: CGFloat! = 200 {
    didSet {
        self.invalidateIntrinsicContentSize()
    }
}

override var intrinsicContentSize: CGSize {
    let originalSize = super.intrinsicContentSize
    return CGSize(width: originalSize.width, height: height)
}

detailTextView.bounds.height returns 0.

fillProportionally 分布尝试根据其内在内容大小缩放排列的子视图的高度,作为堆栈视图高度的比例。例如如果堆栈视图的高度为 120,排列的子视图 A 的固有高度为 10,排列的子视图 B 的固有高度为 20,则堆栈视图中 A 和 B 的高度分别为 40 和 80。

您的堆栈视图没有定义高度,因此 fillProportionally 在这里没有多大意义。

相反,fill 的分布应该可以完成这项工作:

stackView.distribution = .fill

(作为实验,您可以尝试向堆栈视图添加高度约束,您将看到 fillProportionally 的工作原理)