This workshop will be retired on May 31, 2020.
Why Write Code?10:37 with Pasan Premaratne
There are two ways you can position and size your views - in Interface Builder and in code. As you will see throughout this series and in examples that follow, it often requires much more work to do this in code so why would we go down that route in the first place? In this video let's explore some of the reasons why developers write and often prefer to write layout code
[MUSIC] 0:00 Hi, and welcome to programmatic auto layout. 0:09 My name is Pasan, and I'm an IOS developer and instructor here at Treehouse. 0:12 In this series, we're going to cover how to create constraints and 0:17 use Auto Layout to position incisor views in code as opposed to Interface Builder. 0:20 Right off the bat, 0:27 you might have a few questions as to why we would even want to do this. 0:28 Interface Builder offers a lot of advantages over doing this in code. 0:32 It's visual, you can see and modify the constraints you've added to your views. 0:37 It allows you to quickly inspect your layout across different device sizes and 0:42 orientations, and you can debug constraints at compile time. 0:46 There are three main reasons for 0:50 wanting to write layout code over using Interface Builder. 0:52 The first is that for many developers this is their preferred way of working. 0:56 They are much faster in code and don't like using a GUI or 1:01 graphical user interface editor, which is perfectly fine. 1:04 The second is collaboration. 1:09 Now this is one place where I don't have as much experience with the problem, but 1:11 during the early auto layout days, when multiple people work on an app and shared 1:15 code through platforms like GitHub, there were issues where merging a developer's 1:19 Interface Builder of layout files into your project would cause conflicts. 1:24 Interface Builder stores the layouts that we build as X-amount, and X-code 1:29 machine generates a series of unique identifiers for Interface Builder objects. 1:33 When working across different computers and 1:39 collaborating, these identifiers can cause unnecessary merge conflicts. 1:41 For this reason, developers stuck to writing layout code, and as we all know, 1:47 once you get used to a practice, it's hard to shake off. 1:51 Interface Builder has come a long way since then, but 1:54 many developers prefer to stick to code. 1:57 Now the last reason is auto layout libraries. 2:00 This is a topic that we'll get into in the future. 2:03 But when you write layout code, you can write functions, operators, 2:06 helper classes, and even entire frameworks to simplify repetitive tasks. 2:10 In fact, people have done this, and there are lots of useful third-party 2:15 libraries that make writing auto layout code much easier. 2:18 With a good library, auto layout code becomes quite easy to write. 2:22 Now that we know why we would write layout code, let's talk about the how. 2:27 There are actually several ways to do this, for historical reasons, primarily. 2:31 When Apple first introduced layout based APIs, they got the job done, but 2:36 they were pretty verbose. 2:40 Remember, this was objective C as that time, so 2:42 over time they introduce newer APIs to both reflect the updates to iOS UI and 2:45 to make it easier to define layouts in code. 2:50 We're going to start with the original APIs. 2:53 You may still encounter this code as you browse online or 2:56 work in an older code base, so it's important to be familiar. 3:00 Every constraint that we add in Interface Builder is backed by a class N code, 3:04 NSLayoutConstraint. 3:09 When a constraint is created in Interface Builder, 3:11 what we're essentially doing is creating an instance of NSLayoutConstraint. 3:14 So instead of letting Interface Builder handle that for us implicitly, 3:18 let's write this code directly. 3:22 I have a new single view application template her, it's pretty empty. 3:24 And inside ViewController.swift, 3:28 we're going to add our constraints to build a very simple layout. 3:31 We're not going to go over the basics of auto layout and 3:36 how constraints work in this series. 3:38 If those concepts are unfamiliar to you, 3:41 check out the teachers notes to find a link to the relevant topics. 3:43 Instead, we’re going to build in here some of the layouts 3:47 we built in the auto layout basic series. 3:51 Since we use Interface Builder in those videos by writing layout code to achieve 3:54 the same goals here, we should be able to compare and contrast both approaches. 3:58 The layout we're going to build here looks like this. 4:04 This is a very simple layout, but since you already know the basics, 4:07 this is just an exercise in getting familiar with the relevant code. 4:10 In this ViewController, I 've already defined an empty view for us to work with, 4:16 it's just a view subclass, nothing else going on. 4:20 Let's go ahead and, in viewDidLoad, the first thing we'll do is add this 4:24 view as a subview of the main view, so view.addSubview redView. 4:29 Now, you can put layout code in viewDidLoad, but 4:35 it is most common to find it in viewWillLayoutSubviews. 4:40 So we override this method, it is a method on the ViewController class, and 4:46 we can provide an implementation. 4:51 The first thing we need to do, as always when overriding methods, 4:53 is called supers implementation of viewWillLayoutSubviews. 4:56 The first constraint we want to add is to horizontally center this view 5:01 on the screen. 5:05 But before we can do that, 5:06 there's an extremely important line of code we need to add. 5:08 Views, and therefore any view subclasses define a Boolean property, 5:12 translates autoresizing masks into constraints. 5:16 When this property is set to true, auto layout creates constraints that 5:20 duplicate the behavior set by a views autoresizing mask. 5:24 If you're unfamiliar with the concept, check the link in the teachers notes. 5:28 The reason for this behavior is to allow developers to size and 5:33 position a view by using a views frame, bounds, or center property, and 5:36 then allow auto layout to generate those constraints implicitly. 5:41 When constraints are generated this way, 5:45 any constraints we specify explicitly will be in conflict with those generated, 5:48 leading to an ambiguous and unsatisfiable layout. 5:53 If you took the auto layout basics content, you might 5:57 not remember us having talked about this, and you'd be right, because we didn't. 5:59 When you add views to Interface Builder documents, this property is set to false 6:05 automatically and you don't have to worry about it. 6:08 In code, though, you need to be explicit. 6:11 For every view or view subclass that we create, that we intend to position and 6:13 size using auto layout, we need to set this property to false, so it's 6:18 redView.translatesAutoresizingMaskIntoCon- straints = false. 6:22 Now the way I usually do this, and I won't do this for the remainder of the course, 6:28 but typically, since I like to group all my set up logic for 6:33 view in one place, is I'll create this stored property redView. 6:38 But instead of creating it like we just did, 6:42 this is going to be a lazy stored property. 6:43 So lazy var redView, 6:45 we're gonna give it a type that we intend this property to hold, so it's UIView. 6:46 And to that, we'll assign a closure that we call immediately. 6:52 So now in here we can define a view, let view = UIView. 6:56 This is the view instance. 7:00 We'll return this view. 7:01 And in here, I can go and set 7:03 view.translatesAutoresizingMaskIntoConstr- aints to false. 7:06 So now we can get rid of this and the end result is the same. 7:11 Okay, now we can proceed to writing constraint code. 7:16 We're going to create our first constraint and 7:19 assign it to a local constant, so we'll say let horizontalCenter. 7:23 To create this constraint and code, 7:29 we're going to use the initializer on NSLayoutConstraint. 7:30 This is a pretty long initializer because it specifies every bit of 7:35 information encapsulated in the constraint equation. 7:39 Okay, so first step, we have the item argument. 7:46 And here we're going to specify the view that we're adding constraints to, so 7:49 the redView in this case. 7:53 Next we need to specify the attribute on this view that we're 7:56 creating a relationship with. 7:59 The argument accepts values of NSLayoutAttribute. 8:01 This type is an and the value we're looking for here is .centerX. 8:06 To specify the horizontal center, check the notes for 8:12 a link to documentation to refer to all the possible values for this type. 8:15 After the attribute on the first item is the type 8:20 of relationship this constraint will be. 8:23 This is an equal relationship, 8:26 which in code is defined as the equal value on the NSLayoutRelation. 8:28 Constraint relationships are defined on two views or items. 8:34 So for the second part of this initializer, 8:37 we need to specify the item the redView is forming a relationship with. 8:40 And here that item is the route view. 8:44 The attribute on it is also centerX. 8:47 Since we're aligning the horizontal center of the redView 8:51 with the route views horizontal center. 8:54 For the last part of the equation, we need to specify a multiplier and a constant. 8:58 The multiplier here is 1.0 because this is a one-to-one relationship, and 9:03 since we're not going to offset the redView center from the rootView center, 9:08 the constant is 0 points. 9:12 And with that, we have our first constraint. 9:15 Pretty wordy, right? 9:18 Don't worry, this isn't how we always create constraints in code. 9:19 It's just one of the options, and honestly one that you'll rarely use, but 9:23 we'll work our way through the different APIs. 9:27 Another thing that's different between Interface Builder and 9:30 code is that when you create a constraint in code, just creating it is not enough, 9:34 it needs to be activated. 9:38 There are two ways we can do this. 9:40 We can activate constraints individually, so we can say horizontalCenter.isActive = 9:41 true, and this makes the constraint active, and 9:46 when the view is laying out its subviews, this is taken into account. 9:49 The second way is we can ask the root view to add a constraint. 9:54 And we can do both an individual constraint or an array of constraints. 9:58 So here we would pass in our constraint and would say horizontalCenter. 10:03 The final way, and the way I typically do it and 10:08 you'll see why we work through the APIs, is we can use a class method on 10:12 NSLayoutConstraint called activate that takes an array of constraints to activate. 10:16 So here we'll pass in the horizontalCenter. 10:22 Let's go with this one. 10:26 As we add more constrains, it'll be easier to activate them all at once. 10:28 Let's take a quick break here. 10:32 In the next video, we'll add the rest of our constraints. 10:34
You need to sign up for Treehouse in order to download course files.Sign up