This workshop will be retired on May 31, 2020.
Differing Height Views: Code11:10 with Pasan Premaratne
Our goal with this recipe is to build a two column layout. In the first column we have a single view that occupies nearly the entire vertical space available, leaving some room for margins on either side. In the second column we have two views, one of which is 3/4 the height of the first view, and a last view occupying the remaining space with an 8 point margin on all sides.
Our goal with this recipe is to build a two-column layout that looks like this. 0:00 In the first column, we have a single view that occupies nearly the entire 0:05 vertical space available, leaving some room for margins on either side. 0:09 In the second column, we have two views, 0:15 one of which is three-quarters the height of the first view. 0:17 And the last view occupying the remaining space within eight point margin on 0:21 all sides. 0:25 Now I've gone ahead and added a new file to our project. 0:26 And in here I've defined a view controller subclass DifferingHeightViewController. 0:30 So in here there are three views as stored properties. 0:35 And I've gone ahead in viewDidLoad and 0:39 set their background colors to their relevant colors as well. 0:40 The first thing we want to do when setting constraints in code 0:44 is to disable auto resizing masks, which I've done here. 0:48 This is handled automatically in interface builder but 0:52 it is important to be aware of this in code, otherwise you end up with 0:54 constraints you didn't add which will definitely break you around. 0:58 Okay, so ensure that you always do this and then go ahead and 1:03 add the views as subviews before you activate any constraints. 1:06 Back in our diagram, the redView is the view on the left, 1:11 then occupies the entire vertical height minus the spacing, so let's start there. 1:14 Now I'm building this layout in Xcode 9 against the iOS 11 SDK, 1:20 which means I can make use of the new safe area layout guide APIs. 1:25 But I also need to ensure that this builds for versions prior to iOS 11. 1:30 So here, we're going to use the if 1:34 pound available if hash available syntax to target both cases. 1:38 So I'll say if #available (IOS 11), 1:42 then the guide will be the view.safeAreaLayoutGuide. 1:47 In the IOS 11 case, we'll use the view.safeAreaLayoutGuide. 1:55 Otherwise, we'll fall back to the standard top and bottom layout guides and margins. 1:59 The first set of constraints affected by this are the top and 2:04 bottom constraints on the redView. 2:08 So let's add those in. 2:10 So in here we'll say NSlayoutConstraint.activate. 2:12 This takes an array of constraints. 2:16 So we can define them in here and 2:18 the first one is redView.topAnchor.constraint equalTo, 2:20 and you'll say guide.topAnchor since we're using 2:26 safeAreaLayoutGuide, with a constant value of 8 points. 2:30 We also need to add the bottom anchor in this if clause. 2:41 So we'll say redview.bottomAnchor.constraint(equalTo 2:45 guide.bottomAnchor, again, 2:50 the constant is 8), but here it's negative to indicate the direction. 2:52 Now in the case of layout guide, so prior to iOS 11, 2:56 we'll do the same thing, but instead of targeting the layout guide, we can say. 3:01 So first, NSLayoutConstraint.activate, We'll 3:05 say redView.topAnchor.constraint( equalTo topLayoutGuide.bottomAnchor). 3:11 Now, you'll notice that it was crossed out in the auto-complete because 3:18 Xcode knows I'm targeting iOS 11 and that it is deprecated in iOS 11. 3:22 But the if clause will take care of that. 3:27 We won't ever hit this else clause unless we're in iOS 10. 3:29 And the constant here is 8 points. 3:33 Now I'll just go ahead and copy this whole thing, and paste it again. 3:35 Remember, the constant is -8 here, and instead of topLayoutGuide, 3:40 this is bottomLayoutGuide. 3:44 And then instead of topAnchor, this is bottomAnchor. 3:47 Now that gives the red view a height but not a width yet. 3:51 Let's continue by adding outside of this if else block and 3:56 we'll add constraints for the green and blue views. 4:00 So we're going to start in the horizontal direction, 4:04 which actually means starting with the redView. 4:06 Now we'll need another NSLayoutConstraint class method here. 4:08 And we'll start with the redView's leftAnchor and the constraint is going 4:15 to be equalTo the view's leftAnchor with a constant value of 8.0 points. 4:20 Now, if this were a view that contained content, we would do leading anchor. 4:26 We need a horizontal spacing constraint from the root view to the redView 4:31 with a value of 8.0 points and we got that with the line we just added. 4:36 Next, we'll set an 8 point horizontal space constraints between the red and 4:39 the green view. 4:44 So redView.rightAnchor.constraint equal to, never auto-completes the right thing. 4:46 Okay, greenView.leftAnchor, and 4:53 because the redView, which is the origin view essentially for this constraint, 4:56 is on the left of the greenView, we have to put a negative constant value. 5:00 The last horizontal spacing constraint we'll add is from the greenView 5:07 to the right edge of the root view. 5:10 This along with an equal width constraint should provide enough information to 5:13 layout and size the redView. 5:18 So greenView.rightanchor.constraint(equalTo 5:19 view.rightAnchor, constant value of -8. 5:26 Okay, with this, the greenView's right edge is positioned 8 points away 5:33 from the root view's right edge. 5:38 Okay, the last constraint we need now is to 5:41 specify that both of the redView and the greenView have the exact same widths. 5:45 So widthAnchor.constraint(equalTo redView.widthAnchor with 5:51 a multiplier of 1.0). 5:55 So an equal width constraint. 5:57 And with that, the system should be able to determine the width and the spacing for 6:00 both those views. 6:04 Next, let's set up constraints in the vertical direction for the greenView. 6:07 At the top, instead of adding an 8 point vertical spacing constraint, we're going 6:12 to align the top edge of the greenView with the top edge of the redView. 6:17 The reason for doing this is that I want the spacing between the green and 6:22 the blueView, which we haven't added yet, to stay constant at 8 points. 6:26 If the redView is resized to a different height, I'd rather the greenView 6:31 adjust by reducing its height from the top rather than from the bottom. 6:36 If it reduced it from the bottom, it would increase the spacing between the green and 6:40 the blue view, and we don't want that. 6:44 So here we'll say greenView.topAnchor.constraint(equalTo, 6:46 and we'll simply say, redview.topAnchor), and that should align the edges. 6:51 Next up is the height of the greenView. 7:00 And remember, we want it to be three-quarters the height of the red view. 7:02 We achieve this by defining a proportional height constraint. 7:06 So this is defined as an equal height constraint, except we modify the value of 7:10 the multiplier to get that proportion that we want. 7:14 So greenView.heightAnchor.constraint(equalTo 7:17 redView.heightAnchor and 7:23 the multiply here is 3 divided by 4). 7:27 The last constraint we need to add to the greenView to determine it's position 7:31 is a vertical spacing constraint to the blueView. 7:36 So we actually have a top anchor aligned to the redView's top anchor and 7:40 the height, so the greenView is good. 7:44 But this is so that we can space out the blueView. 7:46 So greenView.bottomAnchor.constraint(equalTo 7:48 blueView.topAnchor, with a constant value of -8). 7:54 Okay, so that's the redView and the greenView done. 8:03 For the blueView, instead of adding a vertical spacing constraint 8:06 to the bottom and then a horizontal spacing constraint on either side. 8:11 Let's simply align the edges to the surrounding views. 8:16 Since the other views, the red and the green, have been sized and positioned 8:19 where we want them to be, we can use this information to define the blueView. 8:23 So the blueView has a top space constraint to the greenView already. 8:28 For the bottom, we'll go ahead and align it with the bottom edge of the redView. 8:32 So blueView.bottomAnchor.constraint(equalTo: 8:36 redView's bottomAnchor). 8:39 For the left and right edges of the blueView, 8:41 we'll align the edges with the respective edges of the greenView. 8:45 So blueView.leftAnchor.constraint( equalTo greenView.leftAnchor). 8:50 And then for the last one, we'll say blueView.rightAnchor.constraint(equalTo 8:58 greenView's rightAnchor). 9:03 Okay, and that should be it. 9:06 So let's give this a try. 9:09 And to run this, we're going to go back to Main.storyboard and 9:10 we're going to select this view controller. 9:16 And in the identity inspector, we'll change the backing class 9:19 to the one we just added which is DifferingHeightViewController, and 9:23 that should run that custom sub class. 9:26 And let's see if this works. 9:31 Okay, there we go, so we have the redView, 9:35 it's sized to take up the entire vertical height, minus the safe area at the top. 9:38 There's an eight point spacing all around. 9:43 The green view is three-quarters the height of the red view, 9:45 the blue view takes up the remaining space. 9:49 Now the cool thing is if we go back to our view controller. 9:51 And let's modify one of these constraints. 9:54 So the redView's bottomAnchor, we'll make this, or from the top, we'll 9:57 make this a 48 point spacing, thereby reducing the height of that redView. 10:00 Now all the other views should adjust automatically and 10:05 still maintain those ratios that we defined. 10:08 So here we have the redView starting 48 points from the safe area's top guide. 10:11 The green view does the same, but 10:15 it still maintains this three-quarters ratio that we defined earlier. 10:17 The green view will always be three-quarters the height of the red view. 10:22 So adjust this again. 10:26 Now, you can modify the orientations as well, and 10:30 you should see that it works in all orientations and sizes. 10:33 As with all layouts, there is more than one way to achieve this. 10:36 The important part is the proportional width constraints that we get 10:40 by modifying the multiplier value. 10:44 For the other constraints, though, we could've achieved the same layout by using 10:47 spacing constraints instead of aligning edges. 10:51 As always, go with what makes the most sense, and 10:54 go with the least amount of constraints to get your desired layouts. 10:58 Don't do anything complicated. 11:01 If you're interested in how to implement this layout using Interface Builder, 11:03 check out the note section of this video for a link. 11:07
You need to sign up for Treehouse in order to download course files.Sign up