Detail-oriented UI with Layout Margins @NSSpain 2019

9b4d0f03e7d36e05eddbc6fbbf1b7fec?s=47 Marina
September 20, 2019

Detail-oriented UI with Layout Margins @NSSpain 2019

9b4d0f03e7d36e05eddbc6fbbf1b7fec?s=128

Marina

September 20, 2019
Tweet

Transcript

  1. Detail-oriented UI with Layout Margins Marina Gornostaeva Lead iOS developer

    @ Storytel @hybridcattt @NSSpain, 18-20 September 2019
  2. What is actually the problem? Layout margins to the rescue

    Numberless layout Agenda
  3. None
  4. the problem

  5. None
  6. label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10) label.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10) label.topAnchor.constraint(equalTo: view.topAnchor,

    constant: 5) label.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -5) let buttonFrame = CGRect(x: 10, y: 5, width: self.width - 10 * 2, height: 44)
  7. var horizontalMargin: CGFloat { 
 return IS_IPAD ? 20.0 :

    10.0 } 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 ... }
  8. Layout Margins

  9. – documentation “ [ Layout margins ] specify the desired

    amount of space between the edge of the view 
 and any subview ”
  10. 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 }
  11. Default values 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)
  12. myView.layoutMargins = NSDirectionalEdgeInsets(top: 5, 
 leading: 5,
 bottom: 5,
 trailing:

    5)
 myView.layoutMargins.top = 30
 myView.directionalLayoutMargins.leading = 20
 myView.layoutMargins = .zero Changing margins
  13. Auto Layout 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.widthAnchor …
  14. label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10) label.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10) let lm

    = self.layoutMarginsGuide
 label.leadingAnchor.constraint(equalTo: lm.leadingAnchor)
 label.trailingAnchor.constraint(equalTo: lm.trailingAnchor)
 label.topAnchor.constraint(equalTo: lm.topAnchor)
 label.bottomAnchor.constraint(equalTo: lm.bottomAnchor) … 
 myView.layoutMargins = UIEdgeInsets(top: 5, 
 left: 10, 
 bottom: 5, 
 right: 10)
  15. Manual layout class MyView: UIView {
 override func layoutSubviews() {


    super.layoutSubviews()
 // use self.layoutMargins property // or self.layoutMarginsGuide.layoutFrame } override func layoutMarginsDidChange() {
 super.layoutMarginsDidChange() self.setNeedsLayout() } }
  16. 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: 5, 
 left: 10, 
 bottom: 5, 
 right: 10) let buttonFrame = CGRect(x: 10, y: 5, width: self.width - 10 * 2, height: 44)
  17. Pretty good so far… ✅ Clean code ✅ Native ✅

    Easy to change ✅ Reusable
  18. None
  19. Layout margin propagation View’s layout margins can be increased automatically

    by UIKit based on: • margins of all superviews in the view hierarchy • safe area
  20. blue.addSubview(red) blue.layoutMargins.left = 60 red.preservesSuperviewLayoutMargins = true

  21. blue.frame = self.view.bounds blue.preservesSuperviewLayoutMargins = true blue.addSubview(red) red.preservesSuperviewLayoutMargins = true

    iPhone 7
  22. source

  23. myView. insets Layout Margins From Safe Area = false

  24. myView. insets Layout Margins From Safe Area = true default

  25. preservesSuperviewLayoutMargins + insetsLayoutMarginsFromSafeArea =

  26. blue.frame = self.view.bounds
 blue.preservesSuperviewLayoutMargins = true red.preservesSuperviewLayoutMargins = true red.frame

    = … iPhone X
  27. How can we use this?

  28. None
  29. component. preservesSuperviewLayoutMargins = true title. preservesSuperviewLayoutMargins = true slider. preservesSuperviewLayoutMargins

    = true
  30. component. preservesSuperviewLayoutMargins = true title. preservesSuperviewLayoutMargins = true slider. preservesSuperviewLayoutMargins

    = true
  31. component. preservesSuperviewLayoutMargins = true title. preservesSuperviewLayoutMargins = true slider. preservesSuperviewLayoutMargins

    = true
  32. // in title component self.layoutMarginsGuide …

  33. scrollView.contentInset = self.layoutMargins

  34. None
  35. There’s more!

  36. UITableView

  37. Even more… 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
 }
  38. Number-less layout • Follow system margins • Use layout margins

    propagation all the way • Use readableWidth • Use systemSpacing for spacing between views:
 extension NSLayoutXAxisAnchor { open func constraint(equalToSystemSpacingAfter anchor: NSLayoutXAxisAnchor, multiplier: CGFloat) -> NSLayoutConstraint }
  39. But…. designers? • Talk with your designers • Change layout

    margins on the root view of the VC
  40. Takeaways • Can use layoutMargins on per-view basis • Get

    meaningful margins propagated • Do you need that constant?
  41. WWDC 2018
 UIKit: Apps for Every Size and Shape https://developer.apple.com/wwdc18/235

  42. Adaptivity app https://apps.apple.com/app/id1054670022

  43. That’s it! Start your way to better app UI with

    UIKit layout margins @hybridcattt