3 Things I Was Afraid to Ask About Auto Layout

3 Things I Was Afraid to Ask About Auto Layout

Have you ever wondered why we do things a certain way in Auto Layout? Are you worried it's too late to ask these questions? In this talk, we'll look at some common patterns in Auto Layout and do a deeper dive into how they came to be. This talk will indirectly cover some best practices for making your app UI scale to many devices.

Ecbaec4f458de7172daff03e1f20e677?s=128

Alex Figueroa

July 10, 2018
Tweet

Transcript

  1. 3 Things I Was Afraid to Ask About Auto Layout

    Alex Figueroa Wealthsimple @externconst
  2. #1 translatesAutoResizingMasksIntoConstraints

  3. Design Requirement • A full screen image preview

  4. View Requirement • A name (of course) • UIImageView •

    Aligned to all edges of its superview
  5. let imageView = UIImageView(image: seagullImage) view.addSubview(imageView) NSLayoutConstraint.activate([ imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor), imageView.trailingAnchor.constraint(equalTo:

    view.trailingAnchor), imageView.topAnchor.constraint(equalTo: view.topAnchor), imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor), ]) Note: view here is assumed to be an already created parent view of the label • Initialize a UIImageView with an image of a seagull ̼ • Constraint the leading, trailing, top, and bottom anchors to match its super view Implementation
  6. let imageView = UIImageView(image: seagullImage) view.addSubview(imageView) NSLayoutConstraint.activate([ imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor), imageView.trailingAnchor.constraint(equalTo:

    view.trailingAnchor), imageView.topAnchor.constraint(equalTo: view.topAnchor), imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor), ]) Note: view here is assumed to be an already created parent view of the label • Initialize a UIImageView with an image of a seagull ̼ • Constraint the leading, trailing, top, and bottom anchors to match its super view Implementation
  7. let imageView = UIImageView(image: seagullImage) view.addSubview(imageView) NSLayoutConstraint.activate([ imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor), imageView.trailingAnchor.constraint(equalTo:

    view.trailingAnchor), imageView.topAnchor.constraint(equalTo: view.topAnchor), imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor), ]) Note: view here is assumed to be an already created parent view of the label • Initialize a UIImageView with an image of a seagull ̼ • Constraint the leading, trailing, top, and bottom anchors to match its super view Implementation
  8. Build and run... ⚠

  9. Classic Auto Layout warning 2018-07-09 00:59:00.773965-0400 PresentationPlayground[55474:1923914] [LayoutConstraints] Unable to

    simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) ( "<NSAutoresizingMaskLayoutConstraint:0x600000092d40 h=--& v=--& UIImageView:0x7f978ad04640.height == 1000 (active)>", "<NSLayoutConstraint:0x6000000926b0 V:|-(0)-[UIImageView:0x7f978ad04640] (active, names: '|':UIView:0x7f978ac05800 )>", "<NSLayoutConstraint:0x600000092700 UIImageView:0x7f978ad04640.bottom == UIView:0x7f978ac05800.bottom (active)>", "<NSLayoutConstraint:0x600000092e80 'UIView-Encapsulated-Layout-Height' UIView:0x7f978ac05800.height == 812 (active)>" ) Will attempt to recover by breaking constraint <NSLayoutConstraint:0x600000092700 UIImageView:0x7f978ad04640.bottom == UIView:0x7f978ac05800.bottom (active)> Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
  10. Classic Auto Layout warning 2018-07-09 00:59:00.773965-0400 PresentationPlayground[55474:1923914] [LayoutConstraints] Unable to

    simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) ( "<NSAutoresizingMaskLayoutConstraint:0x600000092d40 h=--& v=--& UIImageView:0x7f978ad04640.height == 1000 (active)>", "<NSLayoutConstraint:0x6000000926b0 V:|-(0)-[UIImageView:0x7f978ad04640] (active, names: '|':UIView:0x7f978ac05800 )>", "<NSLayoutConstraint:0x600000092700 UIImageView:0x7f978ad04640.bottom == UIView:0x7f978ac05800.bottom (active)>", "<NSLayoutConstraint:0x600000092e80 'UIView-Encapsulated-Layout-Height' UIView:0x7f978ac05800.height == 812 (active)>" ) Will attempt to recover by breaking constraint <NSLayoutConstraint:0x600000092700 UIImageView:0x7f978ad04640.bottom == UIView:0x7f978ac05800.bottom (active)> Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
  11. Classic Auto Layout warning 2018-07-09 00:59:00.773965-0400 PresentationPlayground[55474:1923914] [LayoutConstraints] Unable to

    simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) ( "<NSAutoresizingMaskLayoutConstraint:0x600000092d40 h=--& v=--& UIImageView:0x7f978ad04640.height == 1000 (active)>", "<NSLayoutConstraint:0x6000000926b0 V:|-(0)-[UIImageView:0x7f978ad04640] (active, names: '|':UIView:0x7f978ac05800 )>", "<NSLayoutConstraint:0x600000092700 UIImageView:0x7f978ad04640.bottom == UIView:0x7f978ac05800.bottom (active)>", "<NSLayoutConstraint:0x600000092e80 'UIView-Encapsulated-Layout-Height' UIView:0x7f978ac05800.height == 812 (active)>" ) Will attempt to recover by breaking constraint <NSLayoutConstraint:0x600000092700 UIImageView:0x7f978ad04640.bottom == UIView:0x7f978ac05800.bottom (active)> Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
  12. let imageView = UIImageView(image: seagullImage) view.addSubview(imageView) NSLayoutConstraint.activate([ imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor), imageView.trailingAnchor.constraint(equalTo:

    view.trailingAnchor), imageView.topAnchor.constraint(equalTo: view.topAnchor), imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor), ]) The Fix • Unless manually setting frame, always disable translatesAutoresizingMasksIntoConstraints • Storyboards does this for free
  13. let imageView = UIImageView(image: seagullImage) imageView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(imageView) NSLayoutConstraint.activate([

    imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor), imageView.trailingAnchor.constraint(equalTo: view.trailingAnchor), imageView.topAnchor.constraint(equalTo: view.topAnchor), imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor), ]) The Fix • Unless manually setting frame, always disable translatesAutoresizingMasksIntoConstraints • Storyboards does this for free
  14. translatesAutoResizingMasksIntoConstraints Auto Resizing Masks Constraints

  15. What is an Auto Resizing Mask?

  16. • iPhone 5 was debuting the fall of 2012 •

    Auto Layout was introduced in iOS 6 • Before that, there was Springs and Struts Life before Auto Layout
  17. • Springs define the view's height and width • Struts

    are the view's distance to its container Springs and Struts
  18. • Springs define the view's height and width • Struts

    are the view's distance to its container Springs and Struts
  19. • Springs define the view's height and width • Struts

    are the view's distance to its container Springs and Struts
  20. • Springs define the view's height and width • Struts

    are the view's distance to its container Springs and Struts
  21. • Springs define the view's height and width • Struts

    are the view's distance to its container Springs and Struts
  22. The autosizing mask determines what happens to a view when

    its superview changes size
  23. • Enum in Objective-C Autoresizing Mask typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {

    UIViewAutoresizingNone = 0, UIViewAutoresizingFlexibleLeftMargin = 1 << 0, UIViewAutoresizingFlexibleWidth = 1 << 1, UIViewAutoresizingFlexibleRightMargin = 1 << 2, UIViewAutoresizingFlexibleTopMargin = 1 << 3, UIViewAutoresizingFlexibleHeight = 1 << 4, UIViewAutoresizingFlexibleBottomMargin = 1 << 5 };
  24. • During migration, lots of views were still using autoresizing

    masks (AM) • For backwards compatibility, UIKit bridges AM to Auto Layout • UIKit will create these constraints if translatesAutoResizingMaskIntoConstraints is left enabled • Your constraints will likely conflict! Why does this matter?
  25. #2 Animations

  26. Design Requirement • The image should minimize and maximize when

    the user taps on the preview • Respect the safe area too!
  27. View Requirement • Maintain an aspect ratio of 3:2 •

    Adjust the size of the preview to some percentage of the views size • Reuse the existing imageView
  28. Constraints Requirement • We defined the constraints to have all

    its edges aligned to its superview ImageView
  29. Constraints Requirement ImageView

  30. Constraints Requirement ImageView imageView.top = superView.top imageView.trailing = superView.trailing imageView.leading

    = superView.leading imageView.bottom = superView.bottom
  31. Constraints Requirement • We need the image view to fit

    in the bottom right corner with new constraints Image View
  32. Constraints Requirement Image View imageView.bottom = superView.bottom imageView.trailing = superView.trailing

    imageView.width = 0.4 * superView.width imageView.height = 1.5 * imageView.width
  33. Constraints Requirement let imageView = UIImageView(image: seagullImage) view.addSubview(imageView) NSLayoutConstraint.activate([ imageView.leadingAnchor.constraint(equalTo:

    view.leadingAnchor), imageView.trailingAnchor.constraint(equalTo: view.trailingAnchor), imageView.topAnchor.constraint(equalTo: view.topAnchor), imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor), ])
  34. Constraints Requirement leading = imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor) trailing = imageView.trailingAnchor.constraint(equalTo: view.trailingAnchor)

    top = imageView.topAnchor.constraint(equalTo: view.topAnchor) bottom = imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor) NSLayoutConstraint.activate([ leading, trailing, top, bottom ]) Note: constraints are assumed to be defined as properties on the VC already
  35. Constraints Requirement leading = imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor) trailing = imageView.trailingAnchor.constraint(equalTo: view.trailingAnchor)

    top = imageView.topAnchor.constraint(equalTo: view.topAnchor) bottom = imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor) width = imageView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.4) height = imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: 1.5) NSLayoutConstraint.activate([ leading, trailing, top, bottom ])
  36. Minimization Animation UIView.animate(withDuration: 0.3) { leading.isActive = false top.isActive =

    false width.isActive = true height.isActive = true } • Deactivate the leading and top • Activate the new width and height constraints
  37. Minimization Animation UIView.animate(withDuration: 0.3) { leading.isActive = false top.isActive =

    false width.isActive = true height.isActive = true } • Deactivate the leading and top • Activate the new width and height constraints
  38. Minimization Animation UIView.animate(withDuration: 0.3) { leading.isActive = false top.isActive =

    false width.isActive = true height.isActive = true } • Deactivate the leading and top • Activate the new width and height constraints
  39. Animation Result... • Observe the frame change is abrupt •

    The frame is only updating after the animation finishes
  40. UIView.animate(withDuration: 0.3) { leading.isActive = false top.isActive = false width.isActive

    = true height.isActive = true } The Fix • Force any pending layout operations to finish • Capture the frame changes in the animation block (relative to parent view)
  41. view.layoutIfNeeded() UIView.animate(withDuration: 0.3) { leading.isActive = false top.isActive = false

    width.isActive = true height.isActive = true view.layoutIfNeeded() } The Fix • Force any pending layout operations to finish • Capture the frame changes in the animation block
  42. view.layoutIfNeeded() UIView.animate(withDuration: 0.3) { leading.isActive = false top.isActive = false

    width.isActive = true height.isActive = true view.layoutIfNeeded() } The Fix • Force any pending layout operations to finish • Capture the frame changes in the animation block
  43. What is layoutIfNeeded?

  44. - Apple Documentation for layoutIfNeeded Use this method to force

    the layout of subviews before drawing. Using the view that receives the message as the root view, this method lays out the view subtree starting at the root.
  45. Layout Pass • After events are handled, the system begins

    updating the layout, display, and constraints • If a change is requested while an event is being handled, the view gets marked as needing a layout update • At the next update cycle, the system will execute all changes on the marked views
  46. Layout Pass Layout update trigger System

  47. Layout Pass System Layout update trigger

  48. Layout Pass System

  49. Layout Pass System Can update currently? Call layoutSubviews if needed

    Mark with setNeedsLayout No Yes
  50. Layout Pass • setNeedsLayout • Informs the system that a

    view needs to be updated on the next layout pass • layoutIfNeeded • Updates the view immediately if possible • Both indirectly call layoutSubviews
  51. What triggers setNeedsLayout? • Adding or removing a subview •

    Resizing a view • Rotation • Updating constraints
  52. What triggers setNeedsLayout? • Adding or removing a subview •

    Resizing a view • Rotation • Updating constraints
  53. Recall • Auto Layout is only responsible for turning constraints

    into position and size
  54. Constraints to Position and Size ImageView imageView.top = superView.top imageView.trailing

    = superView.trailing imageView.leading = superView.leading imageView.bottom = superView.bottom
  55. Constraints to Position and Size ImageView minX minY height width

  56. view.layoutIfNeeded() UIView.animate(withDuration: 0.3) { leading.isActive = false top.isActive = false

    width.isActive = true height.isActive = true view.layoutIfNeeded() } Revisiting the fix • Start the layout pass with a fresh slate
  57. view.layoutIfNeeded() UIView.animate(withDuration: 0.3) { leading.isActive = false top.isActive = false

    width.isActive = true height.isActive = true view.layoutIfNeeded() } Revisiting the fix • Start the layout pass with a fresh slate • Update constraints which trigger frame* update via setNeedsLayout Note: Auto Layout modifies the alignment rect and not the frame
  58. view.layoutIfNeeded() UIView.animate(withDuration: 0.3) { leading.isActive = false top.isActive = false

    width.isActive = true height.isActive = true view.layoutIfNeeded() } Revisiting the fix • Start the layout pass with a fresh slate • Update constraints which trigger frame update via setNeedsLayout • Force a redraw so frame change can be animated and interpolated
  59. #3 Debugging

  60. Design Requirement • A text composer to add comments

  61. View Requirements • UIView (containerView) • UIImageView (profileImageView) • UITextField

    (textField) • UIButton (sendButton)
  62. Another Auto Layout warning 2018-07-10 01:11:44.748866-0400 PresentationPlayground[16868:534664] [LayoutConstraints] Unable to

    simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. ( "<NSLayoutConstraint:0x60c00009c160 H:|-(0)-[UIView:0x7fec7a809030] (active, names: '|':UIView: 0x7fec77502890 )>", "<NSLayoutConstraint:0x60c00009c3e0 UIView:0x7fec7a809030.trailing == UIView:0x7fec77502890.trailing (active)>", "<NSLayoutConstraint:0x60c00009c520 UIImageView:0x7fec7a815d40.width == 30 (active)>", "<NSLayoutConstraint:0x60c00009c5c0 H:|-(12)-[UIImageView:0x7fec7a815d40] (active, names: '|':UIView: 0x7fec7a809030 )>", "<NSLayoutConstraint:0x60c00009c430 UITextField:0x7fec7788b600.width == 300 (active)>", "<NSLayoutConstraint:0x60c00009c340 H:[UIImageView:0x7fec7a815d40]-(12)-[UITextField:0x7fec7788b600] (active)>", "<NSLayoutConstraint:0x60c00009bfd0 UIButton:0x7fec7740f270'Send'.trailing == UIView:0x7fec7a809030.trailing - 12 (active)>", "<NSLayoutConstraint:0x60c00009ba30 H:[UITextField:0x7fec7788b600]-(12)-[UIButton:0x7fec7740f270'Send'] (active)>", "<NSLayoutConstraint:0x60c000096800 'UIView-Encapsulated-Layout-Width' UIView:0x7fec77502890.width == 375 (active)>" ) Will attempt to recover by breaking constraint <NSLayoutConstraint:0x60c00009c430 UITextField:0x7fec7788b600.width == 300 (active)> Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
  63. Auto Layout Errors • There are three types: • Unsatisfiable

    Layouts • Ambiguous Layouts • Logic Errors • Hardest part is figuring out the error and reading Visual Format Language (VFL)
  64. The Auto Layout warning "<NSLayoutConstraint:0x60c00009c160 H:|-(0)-[UIView: 0x7fec7a809030] (active, names: '|':UIView:

    0x7fec77502890 )>", "<NSLayoutConstraint:0x60c00009c3e0 UIView: 0x7fec7a809030.trailing == UIView:0x7fec77502890.trailing (active)>", "<NSLayoutConstraint:0x60c00009c520 UIImageView: 0x7fec7a815d40.width == 30 (active)>", "<NSLayoutConstraint:0x60c00009c5c0 H:|-(12)-[UIImageView: 0x7fec7a815d40] (active, names: '|':UIView: 0x7fec7a809030 )>", "<NSLayoutConstraint:0x60c00009c430 UITextField: 0x7fec7788b600.width == 300 (active)>", "<NSLayoutConstraint:0x60c00009c340 H:[UIImageView: 0x7fec7a815d40]-(12)-[UITextField:0x7fec7788b600] (active)>", "<NSLayoutConstraint:0x60c00009bfd0 UIButton: 0x7fec7740f270'Send'.trailing == UIView: 0x7fec7a809030.trailing - 12 (active)>", "<NSLayoutConstraint:0x60c00009ba30 H:[UITextField: 0x7fec7788b600]-(12)-[UIButton:0x7fec7740f270'Send'] (active)>", "<NSLayoutConstraint:0x60c000096800 'UIView-Encapsulated- Layout-Width' UIView:0x7fec77502890.width == 375 (active)>"
  65. The Auto Layout warning "<NSLayoutConstraint:0x60c00009c160 H:|-(0)-[UIView: 0x7fec7a809030] (active, names: '|':UIView:

    0x7fec77502890 )>", "<NSLayoutConstraint:0x60c00009c3e0 UIView: 0x7fec7a809030.trailing == UIView:0x7fec77502890.trailing (active)>", "<NSLayoutConstraint:0x60c00009c520 UIImageView: 0x7fec7a815d40.width == 30 (active)>", "<NSLayoutConstraint:0x60c00009c5c0 H:|-(12)-[UIImageView: 0x7fec7a815d40] (active, names: '|':UIView: 0x7fec7a809030 )>", "<NSLayoutConstraint:0x60c00009c430 UITextField: 0x7fec7788b600.width == 300 (active)>", "<NSLayoutConstraint:0x60c00009c340 H:[UIImageView: 0x7fec7a815d40]-(12)-[UITextField:0x7fec7788b600] (active)>", "<NSLayoutConstraint:0x60c00009bfd0 UIButton: 0x7fec7740f270'Send'.trailing == UIView: 0x7fec7a809030.trailing - 12 (active)>", "<NSLayoutConstraint:0x60c00009ba30 H:[UITextField: 0x7fec7788b600]-(12)-[UIButton:0x7fec7740f270'Send'] (active)>", "<NSLayoutConstraint:0x60c000096800 'UIView-Encapsulated- Layout-Width' UIView:0x7fec77502890.width == 375 (active)>"
  66. Adding Identifiers • Identifiers can be added to constraints and

    layout guides • This can be done in Storyboards or progammatically leadingContstraint.identifier = "Leading Constraint" layoutGuide.identifier = "I'm a layout guide!"
  67. The Auto Layout warning "<NSLayoutConstraint:0x60800008e6f0 'Button Leading' H: [UITextField:0x7ff0f403da00]-(12)-[UIButton: 0x7ff0f2e07ce0'Send']

    (active)>", "<NSLayoutConstraint:0x60800008e6a0 'Button Trailing' UIButton:0x7ff0f2e07ce0'Send'.trailing == UIView: 0x7ff0f2f0b1a0.trailing - 12 (active)>", "<NSLayoutConstraint:0x60800008e150 'Container Leading' H:|- (0)-[UIView:0x7ff0f2f0b1a0] (active, names: '|':UIView: 0x7ff0f2f0c140 )>", "<NSLayoutConstraint:0x60800008e1a0 'Container Trailing' UIView:0x7ff0f2f0b1a0.trailing == UIView: 0x7ff0f2f0c140.trailing (active)>", "<NSLayoutConstraint:0x60800008e2e0 'Profile Image Leading' H:|-(12)-[UIImageView:0x7ff0f2f0a7a0] (active, names: '|':UIView:0x7ff0f2f0b1a0 )>", "<NSLayoutConstraint:0x60800008e240 'Profile Image Width' UIImageView:0x7ff0f2f0a7a0.width == 30 (active)>", "<NSLayoutConstraint:0x60800008e3d0 'TextField Leading' H: [UIImageView:0x7ff0f2f0a7a0]-(12)-[UITextField: 0x7ff0f403da00] (active)>", "<NSLayoutConstraint:0x60800008e380 'TextField Width' UITextField:0x7ff0f403da00.width == 300 (active)>", "<NSLayoutConstraint:0x60c00008e650 'UIView-Encapsulated- Layout-Width' UIView:0x7ff0f2f0c140.width == 375 (active)>"
  68. The Auto Layout warning "<NSLayoutConstraint:0x60800008e6f0 'Button Leading' H: [UITextField:0x7ff0f403da00]-(12)-[UIButton: 0x7ff0f2e07ce0'Send']

    (active)>", "<NSLayoutConstraint:0x60800008e6a0 'Button Trailing' UIButton:0x7ff0f2e07ce0'Send'.trailing == UIView: 0x7ff0f2f0b1a0.trailing - 12 (active)>", "<NSLayoutConstraint:0x60800008e150 'Container Leading' H:|- (0)-[UIView:0x7ff0f2f0b1a0] (active, names: '|':UIView: 0x7ff0f2f0c140 )>", "<NSLayoutConstraint:0x60800008e1a0 'Container Trailing' UIView:0x7ff0f2f0b1a0.trailing == UIView: 0x7ff0f2f0c140.trailing (active)>", "<NSLayoutConstraint:0x60800008e2e0 'Profile Image Leading' H:|-(12)-[UIImageView:0x7ff0f2f0a7a0] (active, names: '|':UIView:0x7ff0f2f0b1a0 )>", "<NSLayoutConstraint:0x60800008e240 'Profile Image Width' UIImageView:0x7ff0f2f0a7a0.width == 30 (active)>", "<NSLayoutConstraint:0x60800008e3d0 'TextField Leading' H: [UIImageView:0x7ff0f2f0a7a0]-(12)-[UITextField: 0x7ff0f403da00] (active)>", "<NSLayoutConstraint:0x60800008e380 'TextField Width' UITextField:0x7ff0f403da00.width == 300 (active)>", "<NSLayoutConstraint:0x60c00008e650 'UIView-Encapsulated- Layout-Width' UIView:0x7ff0f2f0c140.width == 375 (active)>"
  69. The Auto Layout warning "<NSLayoutConstraint:0x60800008e6f0 'Button Leading' H: [UITextField:0x7ff0f403da00]-(12)-[UIButton: 0x7ff0f2e07ce0'Send']

    (active)>", "<NSLayoutConstraint:0x60800008e6a0 'Button Trailing' UIButton:0x7ff0f2e07ce0'Send'.trailing == UIView: 0x7ff0f2f0b1a0.trailing - 12 (active)>", "<NSLayoutConstraint:0x60800008e150 'Container Leading' H:|- (0)-[UIView:0x7ff0f2f0b1a0] (active, names: '|':UIView: 0x7ff0f2f0c140 )>", "<NSLayoutConstraint:0x60800008e1a0 'Container Trailing' UIView:0x7ff0f2f0b1a0.trailing == UIView: 0x7ff0f2f0c140.trailing (active)>", "<NSLayoutConstraint:0x60800008e2e0 'Profile Image Leading' H:|-(12)-[UIImageView:0x7ff0f2f0a7a0] (active, names: '|':UIView:0x7ff0f2f0b1a0 )>", "<NSLayoutConstraint:0x60800008e240 'Profile Image Width' UIImageView:0x7ff0f2f0a7a0.width == 30 (active)>", "<NSLayoutConstraint:0x60800008e3d0 'TextField Leading' H: [UIImageView:0x7ff0f2f0a7a0]-(12)-[UITextField: 0x7ff0f403da00] (active)>", "<NSLayoutConstraint:0x60800008e380 'TextField Width' UITextField:0x7ff0f403da00.width == 300 (active)>", "<NSLayoutConstraint:0x60c00008e650 'UIView-Encapsulated- Layout-Width' UIView:0x7ff0f2f0c140.width == 375 (active)>"
  70. The Auto Layout warning "<NSLayoutConstraint:0x60800008e6f0 'Button Leading' H: [UITextField:0x7ff0f403da00]-(12)-[UIButton: 0x7ff0f2e07ce0'Send']

    (active)>", "<NSLayoutConstraint:0x60800008e6a0 'Button Trailing' UIButton:0x7ff0f2e07ce0'Send'.trailing == UIView: 0x7ff0f2f0b1a0.trailing - 12 (active)>", "<NSLayoutConstraint:0x60800008e150 'Container Leading' H:|- (0)-[UIView:0x7ff0f2f0b1a0] (active, names: '|':UIView: 0x7ff0f2f0c140 )>", "<NSLayoutConstraint:0x60800008e1a0 'Container Trailing' UIView:0x7ff0f2f0b1a0.trailing == UIView: 0x7ff0f2f0c140.trailing (active)>", "<NSLayoutConstraint:0x60800008e2e0 'Profile Image Leading' H:|-(12)-[UIImageView:0x7ff0f2f0a7a0] (active, names: '|':UIView:0x7ff0f2f0b1a0 )>", "<NSLayoutConstraint:0x60800008e240 'Profile Image Width' UIImageView:0x7ff0f2f0a7a0.width == 30 (active)>", "<NSLayoutConstraint:0x60800008e3d0 'TextField Leading' H: [UIImageView:0x7ff0f2f0a7a0]-(12)-[UITextField: 0x7ff0f403da00] (active)>", "<NSLayoutConstraint:0x60800008e380 'TextField Width' UITextField:0x7ff0f403da00.width == 300 (active)>", "<NSLayoutConstraint:0x60c00008e650 'UIView-Encapsulated- Layout-Width' UIView:0x7ff0f2f0c140.width == 375 (active)>"
  71. The Auto Layout warning "<NSLayoutConstraint:0x60800008e6f0 'Button Leading' H: [UITextField:0x7ff0f403da00]-(12)-[UIButton: 0x7ff0f2e07ce0'Send']

    (active)>", "<NSLayoutConstraint:0x60800008e6a0 'Button Trailing' UIButton:0x7ff0f2e07ce0'Send'.trailing == UIView: 0x7ff0f2f0b1a0.trailing - 12 (active)>", "<NSLayoutConstraint:0x60800008e150 'Container Leading' H:|- (0)-[UIView:0x7ff0f2f0b1a0] (active, names: '|':UIView: 0x7ff0f2f0c140 )>", "<NSLayoutConstraint:0x60800008e1a0 'Container Trailing' UIView:0x7ff0f2f0b1a0.trailing == UIView: 0x7ff0f2f0c140.trailing (active)>", "<NSLayoutConstraint:0x60800008e2e0 'Profile Image Leading' H:|-(12)-[UIImageView:0x7ff0f2f0a7a0] (active, names: '|':UIView:0x7ff0f2f0b1a0 )>", "<NSLayoutConstraint:0x60800008e240 'Profile Image Width' UIImageView:0x7ff0f2f0a7a0.width == 30 (active)>", "<NSLayoutConstraint:0x60800008e3d0 'TextField Leading' H: [UIImageView:0x7ff0f2f0a7a0]-(12)-[UITextField: 0x7ff0f403da00] (active)>", "<NSLayoutConstraint:0x60800008e380 'TextField Width' UITextField:0x7ff0f403da00.width == 300 (active)>", "<NSLayoutConstraint:0x60c00008e650 'UIView-Encapsulated- Layout-Width' UIView:0x7ff0f2f0c140.width == 375 (active)>"
  72. The Auto Layout warning "<NSLayoutConstraint:0x60800008e6f0 'Button Leading' H: [UITextField:0x7ff0f403da00]-(12)-[UIButton: 0x7ff0f2e07ce0'Send']

    (active)>", "<NSLayoutConstraint:0x60800008e6a0 'Button Trailing' UIButton:0x7ff0f2e07ce0'Send'.trailing == UIView: 0x7ff0f2f0b1a0.trailing - 12 (active)>", "<NSLayoutConstraint:0x60800008e150 'Container Leading' H:|- (0)-[UIView:0x7ff0f2f0b1a0] (active, names: '|':UIView: 0x7ff0f2f0c140 )>", "<NSLayoutConstraint:0x60800008e1a0 'Container Trailing' UIView:0x7ff0f2f0b1a0.trailing == UIView: 0x7ff0f2f0c140.trailing (active)>", "<NSLayoutConstraint:0x60800008e2e0 'Profile Image Leading' H:|-(12)-[UIImageView:0x7ff0f2f0a7a0] (active, names: '|':UIView:0x7ff0f2f0b1a0 )>", "<NSLayoutConstraint:0x60800008e240 'Profile Image Width' UIImageView:0x7ff0f2f0a7a0.width == 30 (active)>", "<NSLayoutConstraint:0x60800008e3d0 'TextField Leading' H: [UIImageView:0x7ff0f2f0a7a0]-(12)-[UITextField: 0x7ff0f403da00] (active)>", "<NSLayoutConstraint:0x60800008e380 'TextField Width' UITextField:0x7ff0f403da00.width == 300 (active)>", "<NSLayoutConstraint:0x60c00008e650 'UIView-Encapsulated- Layout-Width' UIView:0x7ff0f2f0c140.width == 375 (active)>"
  73. The Auto Layout warning "<NSLayoutConstraint:0x60800008e6f0 'Button Leading' H: [UITextField:0x7ff0f403da00]-(12)-[UIButton: 0x7ff0f2e07ce0'Send']

    (active)>", "<NSLayoutConstraint:0x60800008e6a0 'Button Trailing' UIButton:0x7ff0f2e07ce0'Send'.trailing == UIView: 0x7ff0f2f0b1a0.trailing - 12 (active)>", "<NSLayoutConstraint:0x60800008e150 'Container Leading' H:|- (0)-[UIView:0x7ff0f2f0b1a0] (active, names: '|':UIView: 0x7ff0f2f0c140 )>", "<NSLayoutConstraint:0x60800008e1a0 'Container Trailing' UIView:0x7ff0f2f0b1a0.trailing == UIView: 0x7ff0f2f0c140.trailing (active)>", "<NSLayoutConstraint:0x60800008e2e0 'Profile Image Leading' H:|-(12)-[UIImageView:0x7ff0f2f0a7a0] (active, names: '|':UIView:0x7ff0f2f0b1a0 )>", "<NSLayoutConstraint:0x60800008e240 'Profile Image Width' UIImageView:0x7ff0f2f0a7a0.width == 30 (active)>", "<NSLayoutConstraint:0x60800008e3d0 'TextField Leading' H: [UIImageView:0x7ff0f2f0a7a0]-(12)-[UITextField: 0x7ff0f403da00] (active)>", "<NSLayoutConstraint:0x60800008e380 'TextField Width' UITextField:0x7ff0f403da00.width == 300 (active)>", "<NSLayoutConstraint:0x60c00008e650 'UIView-Encapsulated- Layout-Width' UIView:0x7ff0f2f0c140.width == 375 (active)>"
  74. The Auto Layout warning "<NSLayoutConstraint:0x60800008e6f0 'Button Leading' H: [UITextField:0x7ff0f403da00]-(12)-[UIButton: 0x7ff0f2e07ce0'Send']

    (active)>", "<NSLayoutConstraint:0x60800008e6a0 'Button Trailing' UIButton:0x7ff0f2e07ce0'Send'.trailing == UIView: 0x7ff0f2f0b1a0.trailing - 12 (active)>", "<NSLayoutConstraint:0x60800008e150 'Container Leading' H:|- (0)-[UIView:0x7ff0f2f0b1a0] (active, names: '|':UIView: 0x7ff0f2f0c140 )>", "<NSLayoutConstraint:0x60800008e1a0 'Container Trailing' UIView:0x7ff0f2f0b1a0.trailing == UIView: 0x7ff0f2f0c140.trailing (active)>", "<NSLayoutConstraint:0x60800008e2e0 'Profile Image Leading' H:|-(12)-[UIImageView:0x7ff0f2f0a7a0] (active, names: '|':UIView:0x7ff0f2f0b1a0 )>", "<NSLayoutConstraint:0x60800008e240 'Profile Image Width' UIImageView:0x7ff0f2f0a7a0.width == 30 (active)>", "<NSLayoutConstraint:0x60800008e3d0 'TextField Leading' H: [UIImageView:0x7ff0f2f0a7a0]-(12)-[UITextField: 0x7ff0f403da00] (active)>", "<NSLayoutConstraint:0x60800008e380 'TextField Width' UITextField:0x7ff0f403da00.width == 300 (active)>", "<NSLayoutConstraint:0x60c00008e650 'UIView-Encapsulated- Layout-Width' UIView:0x7ff0f2f0c140.width == 375 (active)>"
  75. Why The Failure (WTFAutoLayout)? • Website that lets you view

    AL error logs visually • https://www.wtfautolayout.com
  76. Summary • Always disable translatesAutoResizingMaskIntoConstraints if you plan to add

    constraints to a view • Call layoutIfNeeded before and within an animation block for animating constraints • Use a methodical approach when debugging AL logs. Use identifiers!