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 Vatmakhter

May 29, 2019
Tweet

More Decks by Marina Vatmakhter

Other Decks in Programming

Transcript

  1. !2

  2. !3

  3. !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)
  4. !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)
  5. – documentation “ [ Layout margins ] specify the desired

    amount of space between the edge of the view 
 and any subview ” !8
  6. !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 }
  7. 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)
  8. 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 } }
  9. !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)
  10. 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 …
  11. !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)
  12. Pretty good so far… ✅ Clean code ✅ Native ✅

    Easy to change ✅ Reusable !15 !15
  13. !16

  14. 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
  15. !22

  16. !23

  17. !24 blue.frame = self.view.bounds red.frame = CGRect(x: 4, y: 90,

    width: 200, height: 280) red.preservesSuperviewLayoutMargins = true
  18. 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
 }
  19. 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 }
  20. !29 That’s it! Start your way to better app UI

    with UIKit layout margins @hybridcattt