/Margins-And-Safe-Area

13 examples with big coloured squares to understand margins and safe area in UIKit (iOS 11)

Margins And Safe Area

13 examples with big coloured squares to understand margins and safe area in UIKit (iOS 11)

  1. UIView's safeAreaLayoutGuide + UIView's layoutMarginsGuide
  2. UIView's layoutMargins
  3. UIView's directionalLayoutMargins
  4. UIView's directionalLayoutMargins + autoresizingMask
  5. UIViewController's additionalSafeAreaInsets
  6. UIView's insetsLayoutMarginsFromSafeArea
  7. UIViewController's viewRespectsSystemMinimumLayoutMargins
  8. UIView's preservesSuperviewLayoutMargins
  9. NSLayoutYAxisAnchor's constraintEqualToSystemSpacingBelow(_:multiplier:) + NSLayoutYAxisAnchor's constraintEqualToSystemSpacingAfter(_:multiplier:)
  10. UIStackView's isLayoutMarginsRelativeArrangement
  11. UIScrollView's contentInsetAdjustmentBehavior + UIScrollView's contentInset + UIScrollView's contentLayoutGuide
  12. UICollectionView's contentInsetAdjustmentBehavior
  13. UITableView's insetsContentViewsToSafeArea

1. UIView's safeAreaLayoutGuide + UIView's layoutMarginsGuide

override func viewDidLoad() {
    let yellowView = UIView()
    yellowView.backgroundColor = .yellow
    view.addSubview(yellowView)
    yellowView.translatesAutoresizingMaskIntoConstraints = false
    view.safeAreaLayoutGuide.topAnchor.constraint(equalTo: yellowView.topAnchor).isActive = true
    view.safeAreaLayoutGuide.leadingAnchor.constraint(equalTo: yellowView.leadingAnchor).isActive = true
    view.safeAreaLayoutGuide.trailingAnchor.constraint(equalTo: yellowView.trailingAnchor).isActive = true
    view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: yellowView.bottomAnchor).isActive = true
        
    let cyanView = UIView()
    cyanView.backgroundColor = .cyan
    yellowView.addSubview(cyanView)
    cyanView.translatesAutoresizingMaskIntoConstraints = false
    yellowView.layoutMarginsGuide.topAnchor.constraint(equalTo: cyanView.topAnchor).isActive = true
    yellowView.layoutMarginsGuide.leadingAnchor.constraint(equalTo: cyanView.leadingAnchor).isActive = true
    yellowView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: cyanView.trailingAnchor).isActive = true
    yellowView.layoutMarginsGuide.bottomAnchor.constraint(equalTo: cyanView.bottomAnchor).isActive = true
}


2. UIView's layoutMargins

override func viewDidLoad() {
    let brownView = UIView()
    brownView.layoutMargins = UIEdgeInsets(top: 40, left: 40, bottom: 40, right: 40)
    brownView.backgroundColor = .brown
    view.addSubview(brownView)
    brownView.translatesAutoresizingMaskIntoConstraints = false
    brownView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    brownView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    brownView.widthAnchor.constraint(equalToConstant: 200).isActive = true
    brownView.heightAnchor.constraint(equalToConstant: 200).isActive = true
    
    let blueView = UIView()
    blueView.backgroundColor = .blue
    brownView.addSubview(blueView)
    blueView.translatesAutoresizingMaskIntoConstraints = false
    brownView.layoutMarginsGuide.topAnchor.constraint(equalTo: blueView.topAnchor).isActive = true
    brownView.layoutMarginsGuide.leadingAnchor.constraint(equalTo: blueView.leadingAnchor).isActive = true
    brownView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: blueView.trailingAnchor).isActive = true
    brownView.layoutMarginsGuide.bottomAnchor.constraint(equalTo: blueView.bottomAnchor).isActive = true
}


3. UIView's directionalLayoutMargins

override func viewDidLoad() {
    let redView = UIView()
    redView.backgroundColor = .red
    redView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 70, leading: 70, bottom: 70, trailing: 70)
    view.addSubview(redView)
    redView.translatesAutoresizingMaskIntoConstraints = false
    view.safeAreaLayoutGuide.topAnchor.constraint(equalTo: redView.topAnchor).isActive = true
    view.safeAreaLayoutGuide.leadingAnchor.constraint(equalTo: redView.leadingAnchor).isActive = true
    view.safeAreaLayoutGuide.trailingAnchor.constraint(equalTo: redView.trailingAnchor).isActive = true
    view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: redView.bottomAnchor).isActive = true
    
    let greenView = UIView()
    greenView.backgroundColor = .green
    redView.addSubview(greenView)
    greenView.translatesAutoresizingMaskIntoConstraints = false
    redView.layoutMarginsGuide.topAnchor.constraint(equalTo: greenView.topAnchor).isActive = true
    redView.layoutMarginsGuide.leadingAnchor.constraint(equalTo: greenView.leadingAnchor).isActive = true
    redView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: greenView.trailingAnchor).isActive = true
    redView.layoutMarginsGuide.bottomAnchor.constraint(equalTo: greenView.bottomAnchor).isActive = true
}


4. UIView's directionalLayoutMargins + autoresizingMask

override func viewDidLoad() {
    view.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 100, leading: 100, bottom: 100, trailing: 100)
        
    let orangeView = UIView()
    orangeView.backgroundColor = .orange
    view.addSubview(orangeView)
    orangeView.frame = CGRect(
        x: view.directionalLayoutMargins.leading,
        y: view.directionalLayoutMargins.top,
        width: view.frame.width - view.directionalLayoutMargins.leading - view.directionalLayoutMargins.trailing,
        height: view.frame.height - view.directionalLayoutMargins.top - view.directionalLayoutMargins.bottom
    )
    orangeView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}


5. UIViewController's additionalSafeAreaInsets

override func viewDidLoad() {
    additionalSafeAreaInsets = UIEdgeInsets(top: 100, left: 100, bottom: 100, right: 100)
        
    let brownView = UIView()
    brownView.backgroundColor = .brown
    view.addSubview(brownView)
    brownView.translatesAutoresizingMaskIntoConstraints = false
    view.safeAreaLayoutGuide.topAnchor.constraint(equalTo: brownView.topAnchor).isActive = true
    view.safeAreaLayoutGuide.leadingAnchor.constraint(equalTo: brownView.leadingAnchor).isActive = true
    view.safeAreaLayoutGuide.trailingAnchor.constraint(equalTo: brownView.trailingAnchor).isActive = true
    view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: brownView.bottomAnchor).isActive = true
}


6. UIView's insetsLayoutMarginsFromSafeArea

override func viewDidLoad() {
    view.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)
    view.insetsLayoutMarginsFromSafeArea = false
        
    let magentaView = UIView()
    magentaView.backgroundColor = .magenta
    view.addSubview(magentaView)
    magentaView.translatesAutoresizingMaskIntoConstraints = false
    view.layoutMarginsGuide.topAnchor.constraint(equalTo: magentaView.topAnchor).isActive = true
    view.layoutMarginsGuide.leadingAnchor.constraint(equalTo: magentaView.leadingAnchor).isActive = true
    view.layoutMarginsGuide.trailingAnchor.constraint(equalTo: magentaView.trailingAnchor).isActive = true
    view.layoutMarginsGuide.bottomAnchor.constraint(equalTo: magentaView.bottomAnchor).isActive = true
}


7. UIViewController's viewRespectsSystemMinimumLayoutMargins

override func viewDidLoad() {
    viewRespectsSystemMinimumLayoutMargins = false
    view.insetsLayoutMarginsFromSafeArea = false
    view.layoutMargins = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
        
    let greenView = UIView()
    greenView.backgroundColor = .green
    view.addSubview(greenView)
    greenView.translatesAutoresizingMaskIntoConstraints = false
    view.layoutMarginsGuide.topAnchor.constraint(equalTo: greenView.topAnchor).isActive = true
    view.layoutMarginsGuide.leadingAnchor.constraint(equalTo: greenView.leadingAnchor).isActive = true
    view.layoutMarginsGuide.trailingAnchor.constraint(equalTo: greenView.trailingAnchor).isActive = true
    view.layoutMarginsGuide.bottomAnchor.constraint(equalTo: greenView.bottomAnchor).isActive = true
}


8. UIView's preservesSuperviewLayoutMargins

override func viewDidLoad() {
    viewRespectsSystemMinimumLayoutMargins = false
    view.insetsLayoutMarginsFromSafeArea = false
    view.layoutMargins = UIEdgeInsets(top: 140, left: 140, bottom: 140, right: 140)

    let orangeView = UIView()
    orangeView.preservesSuperviewLayoutMargins = true
    orangeView.backgroundColor = .orange
    view.addSubview(orangeView)
    orangeView.translatesAutoresizingMaskIntoConstraints = false
    view.topAnchor.constraint(equalTo: orangeView.topAnchor).isActive = true
    view.leadingAnchor.constraint(equalTo: orangeView.leadingAnchor).isActive = true
    view.trailingAnchor.constraint(equalTo: orangeView.trailingAnchor).isActive = true
    view.bottomAnchor.constraint(equalTo: orangeView.bottomAnchor).isActive = true
    
    let yellowView = UIView()
    yellowView.backgroundColor = .yellow
    orangeView.addSubview(yellowView)
    yellowView.translatesAutoresizingMaskIntoConstraints = false
    orangeView.layoutMarginsGuide.topAnchor.constraint(equalTo: yellowView.topAnchor).isActive = true
    orangeView.layoutMarginsGuide.leadingAnchor.constraint(equalTo: yellowView.leadingAnchor).isActive = true
    orangeView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: yellowView.trailingAnchor).isActive = true
    orangeView.layoutMarginsGuide.bottomAnchor.constraint(equalTo: yellowView.bottomAnchor).isActive = true
}


9. NSLayoutYAxisAnchor's constraintEqualToSystemSpacingBelow(_:multiplier:) + NSLayoutYAxisAnchor's constraintEqualToSystemSpacingAfter(_:multiplier:)

override func viewDidLoad() {
    let greenView = UIView()
    greenView.backgroundColor = .green
    view.addSubview(greenView)
    greenView.translatesAutoresizingMaskIntoConstraints = false
    greenView.topAnchor.constraintEqualToSystemSpacingBelow(view.topAnchor, multiplier: 1).isActive = true
    greenView.leadingAnchor.constraintEqualToSystemSpacingAfter(view.leadingAnchor, multiplier: 1).isActive = true
    view.trailingAnchor.constraintEqualToSystemSpacingAfter(greenView.trailingAnchor, multiplier: 1).isActive = true
    view.bottomAnchor.constraintEqualToSystemSpacingBelow(greenView.bottomAnchor, multiplier: 1).isActive = true
    
    let magentaView = UIView()
    magentaView.backgroundColor = .magenta
    view.addSubview(magentaView)
    magentaView.translatesAutoresizingMaskIntoConstraints = false
    view.layoutMarginsGuide.topAnchor.constraint(equalTo: magentaView.topAnchor).isActive = true
    view.layoutMarginsGuide.leadingAnchor.constraint(equalTo: magentaView.leadingAnchor).isActive = true
    view.layoutMarginsGuide.trailingAnchor.constraint(equalTo: magentaView.trailingAnchor).isActive = true
    view.layoutMarginsGuide.bottomAnchor.constraint(equalTo: magentaView.bottomAnchor).isActive = true
}


10. UIStackView's isLayoutMarginsRelativeArrangement

override func viewDidLoad() {
    let stackView = UIStackView()
    stackView.isLayoutMarginsRelativeArrangement = true
    stackView.distribution = UIStackViewDistribution.fillEqually
    view.addSubview(stackView)
    stackView.translatesAutoresizingMaskIntoConstraints = false
    stackView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
    stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
    stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
    stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
    
    let redView = UIView()
    redView.backgroundColor = .red
    stackView.addArrangedSubview(redView)
    
    let blueView = UIView()
    blueView.backgroundColor = .blue
    stackView.addArrangedSubview(blueView)
}


11. UIScrollView's contentInsetAdjustmentBehavior + UIScrollView's contentInset + UIScrollView's contentLayoutGuide

override func viewDidLoad() {
    let scrollView = UIScrollView()
    scrollView.backgroundColor = .orange
    scrollView.contentInsetAdjustmentBehavior = .scrollableAxes
    scrollView.contentSize.width = 1000
    scrollView.contentInset = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
    scrollView.contentOffset.x = -5
    view.addSubview(scrollView)
    scrollView.translatesAutoresizingMaskIntoConstraints = false
    scrollView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    view.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
    view.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
    scrollView.heightAnchor.constraint(equalToConstant: 80).isActive = true
    
    let blueView = UIView()
    blueView.backgroundColor = .blue
    scrollView.addSubview(blueView)
    blueView.translatesAutoresizingMaskIntoConstraints = false
    blueView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor).isActive = true
    blueView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor).isActive = true
    blueView.widthAnchor.constraint(equalToConstant: 70).isActive = true
    blueView.heightAnchor.constraint(equalToConstant: 70).isActive = true
}


12. UICollectionView's contentInsetAdjustmentBehavior

import UIKit

class CyanCollectionViewCell: UICollectionViewCell {
    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .cyan
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

class ViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
    
    let idealCellWidth: CGFloat = 100
    let margin: CGFloat = 5
    
    override func viewDidLoad() {
        collectionView?.contentInsetAdjustmentBehavior = .always
        
        guard let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout else { return }
        flowLayout.minimumInteritemSpacing = margin
        flowLayout.minimumLineSpacing = margin
        flowLayout.sectionInset = UIEdgeInsets(top: margin, left: margin, bottom: margin, right: margin)
        
        collectionView?.register(CyanCollectionViewCell.self, forCellWithReuseIdentifier: "cell")
    }
    
    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 25
    }
    
    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        return collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CyanCollectionViewCell
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        guard let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else { return .zero }
        let availableWidth = collectionView.frame.width - collectionView.safeAreaInsets.left - collectionView.safeAreaInsets.right - flowLayout.sectionInset.left - flowLayout.sectionInset.right
        let idealNumberOfCells = (availableWidth + flowLayout.minimumInteritemSpacing) / (idealCellWidth + flowLayout.minimumInteritemSpacing)
        let numberOfCells = idealNumberOfCells.rounded(.down)
        let cellWidth = (availableWidth + flowLayout.minimumInteritemSpacing) / numberOfCells - flowLayout.minimumInteritemSpacing
        return CGSize(width: cellWidth, height: cellWidth)
    }
    
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        collectionView?.collectionViewLayout.invalidateLayout()
        super.viewWillTransition(to: size, with: coordinator)
    }
    
}


13. UITableView's insetsContentViewsToSafeArea

import UIKit

class Cell: UITableViewCell {

    let label = UILabel()
    
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        contentView.backgroundColor = .yellow
        contentView.addSubview(label)
        
        label.translatesAutoresizingMaskIntoConstraints = false
        label.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor).isActive = true
        label.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor).isActive = true
        label.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor).isActive = true
        label.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor).isActive = true
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}

class ViewController: UITableViewController {

    override func viewDidLoad() {
        tableView.register(Cell.self, forCellReuseIdentifier: "cell")
        tableView.insetsContentViewsToSafeArea = true
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 10
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! Cell
        cell.label.text = "Lorem ipsum"
        return cell
    }
}