web-dev-qa-db-fra.com

Comment créer UIStackView avec un espacement variable entre les vues?

J'ai un simple UIStackView horizontal avec plusieurs UIViews empilés à l'intérieur. Mon objectif est de créer un espacement variable entre les vues. Je suis bien conscient que je peux créer un espace constant entre les sous-vues en utilisant la propriété "espacement". Cependant, mon objectif est de créer un espace variable. Veuillez noter que, dans la mesure du possible, j'aimerais éviter d'utiliser des vues invisibles qui agissent comme des entretoises.

Le mieux que j’ai proposé était de placer ma UIViews dans une UIStackView séparée et d’utiliser layoutMarginsRelativeArrangement = YES pour respecter les marges de la disposition de ma pile interne. J'espérais pouvoir faire quelque chose de similaire avec n'importe quelle UIView sans recourir à cette horrible solution de contournement. Voici mon exemple de code:

// 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];

Le résultat est une pile avec des vues côte à côte avec espacement:

 enter image description here

49
yura

Mise à jour pour iOS 11, StackViews avec espacement personnalisé

Apple a ajouté la possibilité de définir l'espacement personnalisé dans iOS 11. Vous devez simplement spécifier l'espacement après chaque sous-vue arrangée. Malheureusement, vous ne pouvez pas spécifier d'espacement auparavant.

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

Encore mieux que d’utiliser vos propres vues.

Pour iOS 10 et inférieur

Vous pouvez simplement ajouter des vues transparentes dans votre vue de pile et leur ajouter des contraintes de largeur.

(Label - UIView - Label - UIView -Label)

et si vous laissez distribution pour remplir, vous pouvez configurer des contraintes de largeur variable sur vos vues UIV.

Mais je considérerais si c'est la bonne situation pour utiliser des vues de pile si c'est le cas. La fonction Autolayout facilite grandement l’installation de largeurs variables entre les vues.

76
Rob Norback

De Rob's response j'ai créé une extension UIStackView qui pourrait aider:

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)
      }
    }
  }
}

Vous pouvez l'utiliser et le modifier à votre guise, par exemple si vous voulez la référence "separatorView", vous pouvez simplement renvoyer l'UIView:

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

Swift 4

Après la réponse de lilpit, voici une extension de UIStackView pour ajouter un espacement supérieur et inférieur à votre vue arrangée 

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)
            }
        }
    }
}

Ensuite, vous l'utiliseriez comme ceci: 

//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)
1
Ugo Marinelli