web-dev-qa-db-ja.com

ビュー間の可変間隔でUIStackViewを作成するにはどうすればよいですか?

いくつかのUIViewが内部にスタックされた単純な水平UIStackViewがあります。私の目標は、ビュー間に可変間隔を作成することです。 「spacing」プロパティを使用して、サブビュー間に一定のスペースを作成できることをよく知っています。しかし、私の目標は可変スペースを作成することです。可能な限り、スペーサーとして機能する不可視のビューの使用を避けたいことに注意してください。

私が思いついたのは、UIViewsを別のUIStackViewでラップし、layoutMarginsRelativeArrangement = YES内側のスタックのレイアウトマージンを尊重します。このい回避策に頼らずに、どの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

56
yura

iOS 11の更新、カスタム間隔のStackViews

Appleは、iOS 11でカスタム間隔を設定する機能を追加しました。配置されたサブビューごとに間隔を指定するだけです。残念ながら、前に間隔を指定することはできません。

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

それでも、独自のビューを使用するよりもはるかに優れています。

iOS 10以下の場合

スタックビューに透明なビューを追加し、幅の制約を追加するだけです。

(ラベル-UIView-ラベル-UIView -Label)

distributionを埋めておくと、UIViewに可変幅の制約を設定できます。

しかし、これがスタックビューを使用するのに適切な状況かどうかを検討します。自動レイアウトを使用すると、ビュー間の可変幅を非常に簡単に設定できます。

92
Rob Norback

Rob's responseから、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?
2
Enrique

Swift 4

Lilpitの回答に続いて、arrangedSubviewに上部と下部の間隔を追加するUIStackViewの拡張があります

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)
2
Ugo Marinelli