Search code examples
iosswiftuikitsnapkit

Equal Spacing for several views using SnapKit


How can I use snap kit to achieve the same spacing between views (and the superview for firstSubview.left and lastSubview.right) like for the bars in the picture below (and using SnapKit instead of stack view or so)?

enter image description here

Thanks!


Solution

  • If you don't want to or cannot use a UIStackView you can achieve the same layout by setting centerX constraints to the superview and add different multipliers for each subview:

    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            view.backgroundColor = .gray
    
            let containerView = UIView()
            containerView.backgroundColor = .lightGray
            view.addSubview(containerView)
    
            let numberOfLines = 8
            for _ in 0..<numberOfLines {
                let lineView = UIView()
                lineView.backgroundColor = .cyan
                containerView.addSubview(lineView)
            }
    
            containerView.snp.makeConstraints { (make) in
                make.top.equalTo(60)
                make.left.equalTo(20)
                make.right.equalTo(-20)
                make.height.equalTo(150)
            }
    
            let centerXFactor: CGFloat = 2 / CGFloat(containerView.subviews.count + 1)
            containerView.subviews.enumerated().forEach { (index, lineView) in
                lineView.snp.makeConstraints({ (make) in
                    make.top.bottom.equalTo(0)
                    make.width.equalTo(4)
                    make.centerX.equalTo(containerView).multipliedBy(CGFloat(index + 1) * centerXFactor)
                })
            }
        }
    }
    

    The only "magic" here is how to calculate the lineViews centerX multipliers: You know that when you have 8 lineViews you'll have 9 spaces inbetween them (also counting the spaces between the containerView's left and right edge and the first and last lineView).

    To place a lineView's centerX on the right edge of the containerView, you would have to set its centerX constraint to the containerView's centerX constraint multiplied by 2 (You don't want to do that, but you need that value for the calculation).

    Now, to get the multiplier for each lineView you divide 2 by the number of lineViews plus 1 (remember that we have one more space than we have lineViews). And then you set the centerX constraints on the lineViews as follows (for 8 lineViews the multiplier is 2 / 9 = 0.2222):

    (pseudo code)
    lineView 1: centerX = containerView.centerX multiplied by (1 * 0.2222)
    lineView 2: centerX = containerView.centerX multiplied by (2 * 0.2222)
    lineView 3: centerX = containerView.centerX multiplied by (3 * 0.2222)
    etc.

    Which gives you this:

    enter image description here