StackViews в StackViews заполняют все одинаково на основе наименьшего элемента

#swift #uikit #uistackview

#swift #uikit #UIStackView

Вопрос:

Я пытаюсь заполнить неизвестное, но вычисленное количество StackViews, заполненное неизвестным, но вычисленным количеством UIImageViews, одинаково, с равным интервалом.

У меня есть основной вертикальный StackView с ограничением высоты и ширины 75. Выравнивание: по центру; Распределение: заполнение равномерно.

Для каждого subStackView:

         newStack.axis = .horizontal
        newStack.alignment = .center
        newStack.distribution = .fillEqually
        newStack.spacing = 1
        newStack.sizeToFit()
        mainStack.addArrangedSubview(newStack)
 

Затем у меня есть другой цикл, который проходит и добавляет правильное количество просмотров изображений в каждый вложенный вид:

             let image = UIImageView()
            image.contentMode = .scaleAspectFit
            image.image = UIImage(systemName: R.Image.System.circle)
            subStackView.addArrangedSubview(image)
 

Ниже приведен скриншот результата. Похоже, что все ImageViews имеют одинаковый размер, но по какой-то причине они кажутся расположенными одинаково, а не имеют интервал 1.

Все нижние 3 изображения должны быть пятиугольниками, но, как вы можете видеть, чем больше добавленных элементов, тем более искаженными становятся фигуры.

введите описание изображения здесь

Ответ №1:

Это немного сложно…

Нам нужно разрешить «точечным рядам» (горизонтальным представлениям стека) центрироваться, а не растягивать ширину основного представления стека.

Нам также нужно фактически рассчитать ширину точек вместо использования, .fillEqually чтобы в итоге не получить паритальные размеры.

Например:

  • ширина основного стека составляет 75
  • с интервалом в 1 пт
  • 3 горизонтальные точки

Доступная ширина — это ширина стека минус количество «пробелов» x интервал:

 75 - 2 = 73
73 / 3 = 24.333333...
 

На @2x масштабном устройстве фактическая ширина 3 видов будет:

 24.5 : 24 : 24.5
 

Не очень заметно всего с 3 «точками», но это становится очень заметным, когда мы переходим к 1, 3, 5, 7, 9, 8, 7, 6, 5 шаблону.

.fillEqually находится слева, вычисленные размеры целых чисел справа:

введите описание изображения здесьвведите описание изображения здесь

Вот несколько примеров кода:

 class DotsViewController: UIViewController {
    
    let patterns: [[Int]] = [
        [3, 3, 3],
        [3],
        [1, 2, 3],
        [1, 3, 5, 4, 3],
        [1, 3, 5, 7, 9, 8, 7, 6, 5],
    ]
    
    let mainStack: UIStackView = {
        let v = UIStackView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.axis = .vertical
        v.alignment = .fill
        v.distribution = .equalSpacing
        return v
    }()
    
    // space between dots
    let dotSpacing: CGFloat = 1
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.addSubview(mainStack)
        
        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            // constrain main stack view 20-pts from top / leading / bottom
            mainStack.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            mainStack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            mainStack.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
            
            // width: 75
            mainStack.widthAnchor.constraint(equalToConstant: 75.0),
        ])
        
        patterns.forEach { a in
            // create a vertical stack view
            //  to hold the "rows" of horizontal stack views (containing the dots)
            let vBlockStack = UIStackView()
            vBlockStack.axis = .vertical
            vBlockStack.alignment = .center
            vBlockStack.distribution = .fill
            vBlockStack.spacing = dotSpacing
            // add it to the main stack view
            mainStack.addArrangedSubview(vBlockStack)

            // calculate dot size
            //  needs to be a whole number so we don't get
            //  half-point sizes
            let maxDots:CGFloat = CGFloat(a.max()!)
            let availableWidth:CGFloat = 75.0 - ((maxDots - 1) * dotSpacing)
            let w:CGFloat = floor(availableWidth / maxDots)
            
            a.forEach { numDots in
                // create a horizontal stack view
                let hDotStack = UIStackView()
                hDotStack.axis = .horizontal
                hDotStack.alignment = .fill
                hDotStack.distribution = .fill
                hDotStack.spacing = dotSpacing
                // add it to the vertical block stack view
                vBlockStack.addArrangedSubview(hDotStack)
                for _ in 0..<numDots {
                    let v = UIImageView()
                    v.contentMode = .scaleAspectFit
                    v.image = UIImage(systemName: "circle.fill")
                    v.tintColor = .red
                    // add view to dot stack view
                    hDotStack.addArrangedSubview(v)
                    // set dots image view size constraints
                    v.widthAnchor.constraint(equalToConstant: w).isActive = true
                    v.heightAnchor.constraint(equalTo: v.widthAnchor).isActive = true
                }
            }
            
        }
        
    }

}
 

Это приводит к такому выводу:

введите описание изображения здесь

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

1. Спасибо, DonMag. Еще один замечательный и чрезвычайно подробный ответ, который вы мне дали. Еще раз для рефакторинга.