UIStackview `fillProportionally` проблема распространения - PullRequest
1 голос
/ 22 марта 2020

У меня есть представление стека контейнера, и у него есть 2 других просмотра стека, которые можно назвать стеками 1 и 2 стека. У Stackview 2 есть пользовательский UIView с некоторой меткой.
Моя проблема связана с видом стека 1 и имеет 2 подвиды. Ось этого UIStackView является вертикальной. Эти подпредставления являются пользовательскими UIView. Это настраиваемое представление может содержать текст или изображение, а высота этих настраиваемых представлений не предопределена, и они основаны на содержимом (т.е. основаны на высоте текста).

Добавлены настраиваемое представление 1 и настраиваемое представление 2 в представлении стека 1. Представление стека 1 и представление стека 2 добавляются в другое представление стека контейнера.

Моя проблема в том, что я не могу отобразить оба этих пользовательских представления (которые находятся внутри представления стека 1), даже если я вернусь intrinsicContentSize (на основе intrinsicContentSize текстовых представлений) для этих пользовательских представлений в fillProportionally дистрибутиве.

Если я возвращаю константу width и height в intrinsicContentSize для настраиваемого представления, тогда оба представления отображается правильно.

Внутри моего custom view

override var intrinsicContentSize: CGSize {
    let size = CGSize.init(width: 100, height: 100)
    return size
   }  

Снимок экрана показывает это поведение.

with constant intrinsicContentSize

Но если я верну размер, основанный на intrinsicContentSize из UITextView (подпредставление customView, я отключил прокрутку для просмотра текста), тогда только Пользовательский вид 2 отображается, а другой вид (пользовательский вид 1) не отображается.

Внутри моего Custom view

  override var intrinsicContentSize: CGSize {
       let size = textView.intrinsicContentSize
       return size
       }

Снимок экрана показывает это поведение.

with intrinsicContentSize of UItextView

Я хочу поведение fillProportionally, но я не могу заставить его работать так, как я хочу, это отображать оба вида (пользовательский вид 1 и пользовательский вид 2). Согласно документации fillProportionally использует intrinsicContentSize для пропорционального заполнения представлений. Но почему это не работает в моем случае, даже после того, как я переопределил intrinsicContentSize var?

Почему настраиваемое представление 1 имеет нулевую высоту даже после переопределения его внутреннего размера c?

Я хочу, чтобы оба этих настраиваемых представления отображались в представлении стека 1.

Я застрял здесь, поэтому я был бы очень признателен за любую помощь.

1 Ответ

1 голос
/ 22 марта 2020

Свойство .fillProportionally UIStackView (по моему опыту) является одним из самых неправильно понятых элементов автоматического макета.

Итак, я не совсем уверен, что это даст вам то, что Вы хотите, но попробуйте.

enter image description here enter image description here

Нажатие кнопки Text изменит "описание" «текст и нажатие кнопки Height изменит высоту представления« контейнер », так что вы можете увидеть, как он выглядит с различным количеством текста.

Кнопка Report выведет итоговую высоту и пропорциональные rat ios просмотров.

Все через код - без @IBOutlet или @IBAction соединений - поэтому просто начните с нового контроллера представления и назначьте его пользовательский класс ProportionalStackViewController:

class ProportionalHeightView: UIView {

    let myNonScrollTextView: UITextView = {
        let v = UITextView()
        v.isScrollEnabled = false
        v.setContentHuggingPriority(.required, for: .vertical)
        return v
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }

    func commonInit() -> Void {

        let padding: CGFloat = 0.0
        addSubview(myNonScrollTextView)
        myNonScrollTextView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            myNonScrollTextView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: padding),
            myNonScrollTextView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -padding),

            // if we want the text top-aligned
            //myNonScrollTextView.topAnchor.constraint(equalTo: topAnchor),

            // if we want the text vertically=sentered
            myNonScrollTextView.centerYAnchor.constraint(equalTo: centerYAnchor),
        ])

    }

    override func layoutSubviews() {
        super.layoutSubviews()
        myNonScrollTextView.sizeToFit()
        invalidateIntrinsicContentSize()
    }

    override var intrinsicContentSize: CGSize {
        return myNonScrollTextView.bounds.size
    }
}

class TitleView: ProportionalHeightView {

    override func commonInit() {
        super.commonInit()
        myNonScrollTextView.font = UIFont.systemFont(ofSize: 22.0, weight: .bold)
        myNonScrollTextView.backgroundColor = .cyan
        backgroundColor = .blue
    }

}
class DescView: ProportionalHeightView {

    override func commonInit() {
        super.commonInit()
        myNonScrollTextView.font = UIFont.systemFont(ofSize: 17.0, weight: .regular)
        myNonScrollTextView.backgroundColor = .yellow
        backgroundColor = .orange
    }

}

class ProportionalStackViewController: UIViewController {

    var titleView: ProportionalHeightView = {
        let v = ProportionalHeightView()
        v.myNonScrollTextView.font = UIFont.systemFont(ofSize: 22.0, weight: .bold)
        v.myNonScrollTextView.backgroundColor = .cyan
        v.backgroundColor = .blue
        return v
    }()
    var descView: ProportionalHeightView = {
        let v = ProportionalHeightView()
        v.myNonScrollTextView.font = UIFont.systemFont(ofSize: 16.0, weight: .regular)
        v.myNonScrollTextView.backgroundColor = .yellow
        v.backgroundColor = .orange
        return v
    }()

    let containerView: UIView = {
        let v = UIView()
        v.backgroundColor = .white
        return v
    }()

    let proportionalStackView: UIStackView = {
        let v = UIStackView()
        v.axis = .vertical
        v.distribution = .fillProportionally
        return v
    }()

    let changeTextButton: UIButton = {
        let b = UIButton()
        b.backgroundColor = .gray
        b.setTitle("Text", for: .normal)
        return b
    }()
    let changeHeightButton: UIButton = {
        let b = UIButton()
        b.backgroundColor = .gray
        b.setTitle("Height", for: .normal)
        return b
    }()
    let reportButton: UIButton = {
        let b = UIButton()
        b.backgroundColor = .gray
        b.setTitle("Report", for: .normal)
        return b
    }()
    let btnStack: UIStackView = {
        let v = UIStackView()
        v.distribution = .fillEqually
        v.spacing = 20
        return v
    }()

    var nLines = 0

    var containerH = NSLayoutConstraint()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .systemTeal

        btnStack.translatesAutoresizingMaskIntoConstraints = false
        proportionalStackView.translatesAutoresizingMaskIntoConstraints = false
        containerView.translatesAutoresizingMaskIntoConstraints = false

        // add a horizontal stack view with buttons at the top
        btnStack.addArrangedSubview(changeTextButton)
        btnStack.addArrangedSubview(changeHeightButton)
        btnStack.addArrangedSubview(reportButton)

        view.addSubview(btnStack)

        // set text for titleView
        titleView.myNonScrollTextView.text = "Pleasanton Panthers"
        descView.myNonScrollTextView.text = "A one stop destination for all the Panthers fans! Experience our new futuristic techology-enabled fan experience an much more!" // "Single line"

        proportionalStackView.addArrangedSubview(titleView)
        proportionalStackView.addArrangedSubview(descView)

        containerView.addSubview(proportionalStackView)
        view.addSubview(containerView)

        // respect safe area
        let g = view.safeAreaLayoutGuide

        containerH = containerView.heightAnchor.constraint(equalToConstant: 240.0)

        NSLayoutConstraint.activate([

            // buttons stack 20-pts from top / leading / trailing
            btnStack.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            btnStack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            btnStack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),

            // container view 20-pts from bottom of buttons, 20-pts from leading / trailing
            containerView.topAnchor.constraint(equalTo: btnStack.bottomAnchor, constant: 20.0),
            containerView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            containerView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),

            // container view height
            containerH,

            // constrain stack view 20-pts from top/bottom/leading/trailing to container
            proportionalStackView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 20.0),
            proportionalStackView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -20.0),
            proportionalStackView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 20.0),
            proportionalStackView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -20.0),

        ])

        changeTextButton.addTarget(self, action: #selector(changeText), for: .touchUpInside)
        changeHeightButton.addTarget(self, action: #selector(changeHeight), for: .touchUpInside)
        reportButton.addTarget(self, action: #selector(report), for: .touchUpInside)

        [titleView, titleView.myNonScrollTextView, descView, descView.myNonScrollTextView].forEach {
            v in
            // un-comment next line to clear background colors
            //v.backgroundColor = .clear
        }
    }

    @objc func report() -> Void {

        let titleTextH = titleView.myNonScrollTextView.frame.size.height
        let descTextH = descView.myNonScrollTextView.frame.size.height

        let titleViewH = titleView.frame.size.height
        let descViewH = descView.frame.size.height

        let tRatio = titleTextH / descTextH
        let vRatio = titleViewH / descViewH

        print("text heights:\t", titleTextH, descTextH)
        print("view heights:\t", titleViewH, descViewH)
        print("Text view ratio: \(tRatio) view ratio: \(vRatio)")

    }

    @objc func changeHeight() -> Void {
        if containerView.frame.origin.y + containerView.frame.size.height > view.frame.size.height - 20 {
            containerH.constant = 220
        }
        containerH.constant += 20
    }

    @objc func changeText() -> Void {
        nLines += 1
        if nLines > 10 {
            descView.myNonScrollTextView.text = "A one stop destination for all the Panthers fans! Experience our new futuristic techology-enabled fan experience an much more!" // "Single line"
            nLines = 0
            return
        }
        var s = ""
        for i in 1..<nLines {
            s += "Line \(i)\n"
        }
        s += "Line \(nLines)"
        descView.myNonScrollTextView.text = s
    }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...