Подтвердить что ты не робот

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

У меня есть простой горизонтальный UIStackView с несколькими UIViews сложены внутри. Моя цель - создать переменный интервал между представлениями. Мне хорошо известно, что я могу создать постоянное пространство между подпредставлениями, используя свойство "spacing". Однако моя цель - создать переменное пространство. Пожалуйста, обратите внимание, если это вообще возможно, я хотел бы избежать использования невидимых представлений, которые действуют как разделители.

Лучшее, что я придумал, - это обернуть мои UIViews в отдельный UIStackView и использовать layoutMarginsRelativeArrangement = YES чтобы соблюдать поля макета моего внутреннего стека. Я надеялся, что смогу сделать что-то подобное с любым UIView не прибегая к этому уродливому UIView. Вот мой пример кода:

// Create stack view
UIStackView *stackView = [[UIStackView alloc] init];
stackView.translatesAutoresizingMaskIntoConstraints = NO;
stackView.axis = UILayoutConstraintAxisHorizontal;
stackView.alignment = UIStackViewAlignmentCenter;
stackView.layoutMarginsRelativeArrangement = YES;

// Create subview
UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
// ... Add Auto Layout constraints for height / width
// ...
// I was hoping the layoutMargins would be respected, but they are not
view1.layoutMargins = UIEdgeInsetsMake(0, 25, 0, 0);

// ... Create more subviews
// UIView view2 = [[UIView alloc] init];
// ...

// Stack the subviews
[stackView addArrangedSubview:view1];
[stackView addArrangedSubview:view2];

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

enter image description here

4b9b3361

Ответ 1

Обновление для iOS 11, StackViews с пользовательским интервалом

Apple добавила возможность устанавливать пользовательский интервал в iOS 11. Вам просто нужно указать интервал после каждого организованного подсмотра. К сожалению, вы не можете указать интервал раньше.

stackView.setCustomSpacing(10.0, after: firstLabel)
stackView.setCustomSpacing(10.0, after: secondLabel)

Еще лучше, чем использовать собственные представления.

Для iOS 10 и ниже

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

(Label - UIView - Label - UIView -Label)

и если вы сохраните distribution для заполнения, тогда вы можете установить ограничения ширины переменной в своих UIViews.

Но я бы подумал, если это правильная ситуация для использования stackviews, если это случай. Autolayout упрощает настройку переменной ширины между представлениями.

Ответ 2

Это сохранит согласованное поведение между pre и post ios11, когда интервал отличается от 0:

extension UIStackView {
    func addCustomSpacing(_ customSpacing: CGFloat, after arrangedSubview: UIView) {
        if #available(iOS 11.0, *) {
            setCustomSpacing(customSpacing, after: arrangedSubview)
        } else {
            guard let arrangedSubviewIndex = arrangedSubviews.firstIndex(of: arrangedSubview) else {
                return
            }

            let separatorView = UIView(frame: .zero)
            separatorView.translatesAutoresizingMaskIntoConstraints = false

            //calculate spacing to keep a coherent spacing with the ios11 version
            let isBetweenExisitingViews = arrangedSubviewIndex != arrangedSubviews.count - 1
            let existingSpacing = isBetweenExisitingViews ? 2 * spacing : spacing
            let separatorSize = customSpacing - existingSpacing

            guard separatorSize > 0 else {
                return
            }

            switch axis {
            case .horizontal:
                separatorView.widthAnchor.constraint(equalToConstant: separatorSize).isActive = true
            case .vertical:
                separatorView.heightAnchor.constraint(equalToConstant: separatorSize).isActive = true
            }

            insertArrangedSubview(separatorView, at: arrangedSubviewIndex + 1)
        }
    }
}

Ответ 3

Из ответа Роба я создал расширение UIStackView, которое может помочь:

extension UIStackView {
  func addCustomSpacing(_ spacing: CGFloat, after arrangedSubview: UIView) {
    if #available(iOS 11.0, *) {
      self.setCustomSpacing(spacing, after: arrangedSubview)
    } else {
      let separatorView = UIView(frame: .zero)
      separatorView.translatesAutoresizingMaskIntoConstraints = false
      switch axis {
      case .horizontal:
        separatorView.widthAnchor.constraint(equalToConstant: spacing).isActive = true
      case .vertical:
        separatorView.heightAnchor.constraint(equalToConstant: spacing).isActive = true
      }
      if let index = self.arrangedSubviews.firstIndex(of: arrangedSubview) {
        insertArrangedSubview(separatorView, at: index + 1)
      }
    }
  }
}

Вы можете использовать и изменять его по своему усмотрению, например, если вам нужна ссылка " separatorView ", вы можете просто вернуть UIView:

  func addCustomSpacing(_ spacing: CGFloat, after arrangedSubview: UIView) -> UIView?

Ответ 4

SWIFT 4

После ответа lilpit, вот расширение UIStackView, чтобы добавить верхний и нижний интервал к вашему организованному Subview

extension UIStackView {
    func addCustomSpacing(top: CGFloat, bottom: CGFloat) {

        //If the stack view has just one arrangedView, we add a dummy one
        if self.arrangedSubviews.count == 1 {
            self.insertArrangedSubview(UIView(frame: .zero), at: 0)
        }

        //Getting the second last arrangedSubview and the current one
        let lastTwoArrangedSubviews = Array(self.arrangedSubviews.suffix(2))
        let arrSpacing: [CGFloat] = [top, bottom]

        //Looping through the two last arrangedSubview to add spacing in each of them
        for (index, anArrangedSubview) in lastTwoArrangedSubviews.enumerated() {

            //After iOS 11, the stackview has a native method
            if #available(iOS 11.0, *) {
                self.setCustomSpacing(arrSpacing[index], after: anArrangedSubview)
                //Before iOS 11 : Adding dummy separator UIViews
            } else {
                guard let arrangedSubviewIndex = arrangedSubviews.firstIndex(of: anArrangedSubview) else {
                    return
                }

                let separatorView = UIView(frame: .zero)
                separatorView.translatesAutoresizingMaskIntoConstraints = false

                //calculate spacing to keep a coherent spacing with the ios11 version
                let isBetweenExisitingViews = arrangedSubviewIndex != arrangedSubviews.count - 1
                let existingSpacing = isBetweenExisitingViews ? 2 * spacing : spacing
                let separatorSize = arrSpacing[index] - existingSpacing

                guard separatorSize > 0 else {
                    return
                }

                switch axis {
                case .horizontal:
                    separatorView.widthAnchor.constraint(equalToConstant: separatorSize).isActive = true
                case .vertical:
                    separatorView.heightAnchor.constraint(equalToConstant: separatorSize).isActive = true
                }

                insertArrangedSubview(separatorView, at: arrangedSubviewIndex + 1)
            }
        }
    }
}

Тогда вы бы использовали это так:

//Creating label to add to the UIStackview
let label = UILabel(frame: .zero)

//Adding label to the UIStackview
stackView.addArrangedSubview(label)

//Create margin on top and bottom of the UILabel
stackView.addCustomSpacing(top: 40, bottom: 100)

Ответ 5

Я создал библиотеку LRCustomSpacingStackView для поддержки пользовательского интервала UIStackView на iOS 9 и выше.

Просто замените встроенные методы UIStackView в своем коде на методы расширения этой библиотеки и укажите интервал, задав любое свойство lr_stackSpacing:

import LRCustomSpacingStackView
stackView.lr_addArrangedSubview(view1)
view1.lr_stackSpacing = UIEdgeInsets(top: 20, left: 30, bottom: 10, right: 0)

Все методы UIStackView имеют свои замены:

lr_arrangedSubviews для arrangedSubviews lr_arrangedSubviews

lr_addArrangedSubview(_:) для addArrangedSubview(_:)

lr_removeArrangedSubview(_:) для removeArrangedSubview(_:)

lr_insertArrangedSubview(_:at:) для insertArrangedSubview(_:at:)

lr_stackSpacing более гибкий и мощный, чем setCustomSpacing(_:after:). Вы можете указать интервал просмотра в 4 направлениях: верхний, правый, нижний, левый.