Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Detail-oriented UI with Layout Margins

Detail-oriented UI with Layout Margins

In iOS 8 Apple introduced layout margins to UIKit, as a way to streamline view alignment easily even for most complex layouts. In this talk I share insights about layout margins and concepts around them.

Marina

May 29, 2019
Tweet

More Decks by Marina

Other Decks in Programming

Transcript

  1. Detail-oriented UI
    with Layout Margins
    Marina Gornostaeva

    @hybridcattt

    Copenhagen Cocoa, 29 May 2019
    !1

    View Slide

  2. !2

    View Slide

  3. !3

    View Slide

  4. source
    !4

    View Slide

  5. !5
    class BookCell: UITableViewCell {
    // Layout constants
    let coverImagePadding: CGFloat = 10
    let coverImageHeightPaddings: CGFloat = 30
    let titleLabelTopMargin: CGFloat = 15
    let labelPadding: CGFloat = -16
    let authorTopMargin: CGFloat = 0
    let narratorLabelBottomMargin: CGFloat = 15
    ...
    }
    label.leadingAnchor.constraint(equalTo: view.leadingAnchor,
    constant: 10)
    label.trailingAnchor.constraint(equalTo: view.trailingAnchor,
    constant: -10)

    View Slide

  6. !6
    func horizontalMargin() -> CGFloat { 

    return IS_IPAD ? 20.0 : 10.0
    }
    let buttonFrame = CGRect(x: 10,
    y: 10,
    width: self.width - 10 * 2,
    height: 44)

    View Slide


  7. Layout Margins
    !7

    View Slide

  8. – documentation
    “ [ Layout margins ] specify the desired amount of
    space between the edge of the view 

    and any subview ”
    !8

    View Slide

  9. !9
    open class UIView {


    @available(iOS 8.0, *)

    open var layoutMargins: UIEdgeInsets
    @available(iOS 11.0, *)

    open var directionalLayoutMargins: NSDirectionalEdgeInsets

    }
    public struct NSDirectionalEdgeInsets {
    public var top: CGFloat
    public var leading: CGFloat
    public var bottom: CGFloat
    public var trailing: CGFloat
    }
    public struct UIEdgeInsets {
    public var top: CGFloat
    public var left: CGFloat
    public var bottom: CGFloat
    public var right: CGFloat
    }

    View Slide

  10. Default values
    !10
    view.layoutMargins = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
    view.layoutMargins.top = 30
    view.directionalLayoutMargins.leading = 20
    Plain UIView:
    UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
    Plain UIViewController ’s view:
    UIEdgeInsets(top: 8, left: 15, bottom: 8, right: 15)
    UIViewController ’s view in UINavigationController:
    UIEdgeInsets(top: 8, left: 16/20, bottom: 8, right: 16/20)

    View Slide

  11. Manual layout
    !11
    class MyView: UIView {
    override func layoutMarginsDidChange() {
    super.layoutMarginsDidChange()
    self.setNeedsLayout()
    }
    override func layoutSubviews() {
    super.layoutSubviews()
    // read self.layoutMargins property
    // for frame calculations
    }
    }

    View Slide

  12. !12
    let lm = self.layoutMargins
    let buttonFrame = CGRect(x: lm.left,
    y: lm.top,
    width: self.width - lm.left - lm.right,
    height: 44);

    myView.layoutMargins = UIEdgeInsets(top: 10,
    left: 10,
    bottom: 10,
    right: 10)
    let buttonFrame = CGRect(x: 10,
    y: 10,
    width: self.width - 10 * 2,
    height: 44)

    View Slide

  13. Auto Layout
    !13
    open class UIView {
    @available(iOS 9.0, *)

    open var layoutMarginsGuide: UILayoutGuide { get }
    }
    self.layoutMarginsGuide.leadingAnchor
    self.layoutMarginsGuide.trailingAnchor
    self.layoutMarginsGuide.topAnchor
    self.layoutMarginsGuide.bottomAnchor
    self.layoutMarginsGuide.width

    View Slide

  14. !14
    label.leadingAnchor.constraint(equalTo: view.leadingAnchor,
    constant: 10)
    label.trailingAnchor.constraint(equalTo: view.trailingAnchor,
    constant: -10)
    let layoutGuide = self.layoutMarginsGuide
    subview.leadingAnchor.constraint(equalTo: layoutGuide.leadingAnchor)
    subview.trailingAnchor.constraint(equalTo: layoutGuide.trailingAnchor)
    subview.topAnchor.constraint(equalTo: layoutGuide.topAnchor)
    subview.bottomAnchor.constraint(equalTo: layoutGuide.bottomAnchor)

    myView.layoutMargins = UIEdgeInsets(top: 10,
    left: 10,
    bottom: 10,
    right: 10)

    View Slide

  15. Pretty good so far…
    ✅ Clean code

    ✅ Native

    ✅ Easy to change

    ✅ Reusable
    !15
    !15

    View Slide

  16. !16

    View Slide

  17. Layout margin propagation
    View’s layout margins can be increased automatically by
    UIKit based on:

    • safe area

    • margins of all superviews in the view hierarchy
    !17

    View Slide

  18. !18 source

    View Slide

  19. !19
    view.insetsLayoutMarginsFromSafeArea
    false true

    View Slide

  20. !20
    blue.addSubview(red)
    blue.layoutMargins.left = 60
    red.preservesSuperviewLayoutMargins = true

    View Slide

  21. preservesSuperviewLayoutMargins
    +
    insetsLayoutMarginsFromSafeArea
    =

    !21

    View Slide

  22. !22

    View Slide

  23. !23

    View Slide

  24. !24
    blue.frame = self.view.bounds
    red.frame = CGRect(x: 4, y: 90, width: 200, height: 280)
    red.preservesSuperviewLayoutMargins = true

    View Slide

  25. There’s more!
    !25
    open class UIView {

    @available(iOS 9.0, *)

    open var readableWidthGuide: UILayoutGuide

    }
    open class UIStackView {

    open var isLayoutMarginsRelativeArrangement: Bool

    }
    open class UITableView {

    @available(iOS 9.0, *)

    open var cellLayoutMarginsFollowReadableWidth: Bool

    }

    View Slide

  26. Number-less layout
    • Use layout margins propagation all the way

    • Use readableWidth

    • Use systemSpacing for spacing between views:

    !26
    extension NSLayoutXAxisAnchor {
    open func constraint(equalToSystemSpacingAfter anchor: NSLayoutXAxisAnchor,
    multiplier: CGFloat) -> NSLayoutConstraint
    }

    View Slide

  27. But…. designers?
    • educate your designers

    • change layout margins on the root view of the VC
    !27

    View Slide

  28. Adaptivity app
    https://itunes.apple.com/dk/app/adaptivity-b/id1054670026
    !28

    View Slide

  29. !29
    That’s it!
    Start your way to better app UI with UIKit layout margins
    @hybridcattt

    View Slide