Как динамически изменять размер текстового представления внутри представления стека

#ios #swift #uistackview

Вопрос:

Я пытаюсь отобразить динамически изменяемый размер 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 возвращает 0.

Комментарии:

1. Вы пытаетесь изобрести UITableView что-то новое ? Похоже, вы используете UIScrollView его неправильно (вы даже не инициализировали его?). fillProportionally здесь тоже не кажется уместным. Должны же быть какие-то ограничения, верно? Вы должны включить конфликтующие ограничения в свой вопрос. Я думаю fill , что это более уместно, но почему вы вообще не используете a UITableView ?

2. @Sweeper упс отсутствие UIScrollView было ошибкой с моей стороны в копировании вопроса. fill похоже, это делает свое дело, поэтому, если вы хотите опубликовать ответ, я отмечу его. Я использую UIStackView , потому что я буду использовать только 1-3 элемента, но я определенно буду учитывать этот вариант.

Ответ №1:

На fillProportionally распределение пытается шкалы высот организованы подпанелей по их подлинным содержанием размер, как часть стека вид на высоте. например, если стек вид имеет высоту 120, а также организовать рабочую панель также имеет внутреннюю высоту 10, и устраивается панель Б имеет внутреннюю высоту 20, а затем A и B будет иметь высоту в 40 и 80 соответственно в стеке вид.

Ваш вид стека не имеет определенной высоты, поэтому fillProportionally здесь нет особого смысла.

Вместо этого распределение fill должно выполнять эту работу:

 stackView.distribution = .fill
 

(в качестве эксперимента вы можете попробовать добавить ограничение по высоте в представление стека, и вы увидите, как fillProportionally это работает)