SnapKit Tutorial: Easy Auto Layout Constraints iOS 14.07.2020

SnapKit is a tool allowing iOS developers to manipulate auto-layout constraints easily. By using SnapKit, you can create, update, remove and manage layout constraints of the UI views.

snp.makeConstraint method is used to add constraints, including: margin, width, height, left and right distance. SnapKit also supports deletion constraints, update constraints, and relative position (inset, offset) and multiple correction (multipleBy and divideBy).

SnapKit supports the following properties:

ViewAttribute Layout Attribute
view.snp.left NSLayoutAttribute.Left
view.snp.right NSLayoutAttribute.Right
view.snp.top NSLayoutAttribute.Top
view.snp.bottom NSLayoutAttribute.Bottom
view.snp.leading NSLayoutAttribute.Leading
view.snp.trailing NSLayoutAttribute.Trailing
view.snp.width NSLayoutAttribute.Width
view.snp.height NSLayoutAttribute.Height
view.snp.centerX NSLayoutAttribute.CenterX
view.snp.centerY NSLayoutAttribute.CenterY
view.snp.baseline NSLayoutAttribute.Baseline

Without SnapKit, the code would look similar to the following:

child.translatesAutoresizingMaskIntoConstraints = false

NSLayoutConstraint.activate([
    child.leadingAnchor.constraint(equalTo: parent.leadingAnchor),
    child.topAnchor.constraint(equalTo: parent.topAnchor),
    child.trailingAnchor.constraint(equalTo: parent.trailingAnchor),
    child.bottomAnchor.constraint(equalTo: parent.bottomAnchor),
])

SnapKit represents those constraints like this:

child.snp.makeConstraints { make in
    make.leading.equalToSuperview()
    make.top.equalToSuperview()
    make.trailing.equalToSuperview()
    make.bottom.equalToSuperview()
}

First of all, you need to install SnapKit via CocoaPods. Add this to your Podfile:

pod 'SnapKit'

Let’s display a UIView pinned to the four edges of its superview.

import UIKit
import SnapKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let child = UIView()
        child.backgroundColor = .red

        let child1 = UIView()
        child1.backgroundColor = .green

        self.view.addSubview(child)
        self.view.addSubview(child1)

        child.snp.makeConstraints { (make) in
            make.edges.equalTo(self.view)
        }

        child1.snp.makeConstraints { (make) in
            make.size.equalTo(CGSize(width: 300, height: 300))
            make.top.equalTo(self.view.snp.top).offset(100)
            make.centerX.equalTo(self.view)
        }
    }
}

Let’s try sme other things in SnapKit:

child.snp.makeConstraints { (make) in
    make.size.equalTo(CGSize(width: 300, height: 300))
    make.top.equalTo(self.view.snp.top).offset(100)
    make.centerX.equalTo(self.view)
}

child2.snp.makeConstraints { (make) in
    make.size.equalTo(child)
    make.top.equalTo(child.snp.bottom).offset(50)
    make.centerX.equalTo(self.view)
}

child3.snp.makeConstraints { (make) -> Void in
   make.top.left.right.equalTo(0)
   make.height.equalTo(self.view.snp_height).multipliedBy(0.5)
}

child4.snp.makeConstraints { (make) -> Void in
    make.center.equalTo(topView.center)
    make.width.equalTo(100)
    make.height.equalTo(110)
}   

child5.snp.makeConstraints { make in
    make.edges.equalToSuperview().inset(16)
    //make.edges.equalTo(child2).inset(UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16))
}

child6.snp.makeConstraints { make in
    make.leading.trailing.equalTo(view.safeAreaLayoutGuide)
}

child7.snp.makeConstraints { make in
    make.leading.trailing.equalTo(child6)
    make.top.equalTo(child6.snp.bottom).offset(16)
    make.height.equalTo(80)
}

Aspect ratio

view.snp.makeConstraints { make in
    make.width.equalTo(view.snp.height).multipliedBy(1.0 / 1.0)
}    

UITableView example

import UIKit
import SnapKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    lazy var tbl: UITableView = {
        let v = UITableView()
        v.rowHeight = 100
        v.separatorStyle = .none
        return v
    }()

    var items: [String] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        items.append(contentsOf: (1...20).map { index in "Item \(index)"})
        setupUI()
    }

    func setupUI() {
        tbl.delegate = self
        tbl.dataSource = self
        tbl.register(CustomCell.self, forCellReuseIdentifier: CustomCell.cellId)
        self.view.addSubview(tbl)
        tbl.snp.makeConstraints { (make) in
            make.edges.equalTo(self.view.safeAreaLayoutGuide)
        }
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return items.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: CustomCell.cellId, for: indexPath) as! CustomCell
        cell.lblTitle.text = items[indexPath.row]
        return cell
    }
}

class CustomCell: UITableViewCell {
    static var cellId = "cell"

    let lblTitle: UILabel = {
        let v = UILabel()
        v.backgroundColor = .systemGreen
        v.textColor = .white
        v.textAlignment = .center
        v.layer.cornerRadius = 5
        v.layer.masksToBounds = true
        return v
    }()

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        setupUI()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func setupUI() {
        self.addSubview(lblTitle)
        lblTitle.snp.makeConstraints { (make) in
            make.top.left.equalTo(20)
            make.right.bottom.equalTo(-20)
        }
    }
}