This workshop will be retired on May 31, 2020.
Simple Stack Views: Code11:46 with Pasan Premaratne
In this recipe we're going to write code to create a simple stack view containing three nested views
In this recipe, we are going to lay out an image, a label and 0:00 a button vertically on screen using a stackView. 0:04 This is a fairly simple layout that is achievable without stackViews, but 0:07 getting a handle on how to do this with stackViews, particularly in code, 0:12 should allow you to build more complex lay outs in the future. 0:16 I have an existing Xcode project here, and I've added a new Swift file for 0:19 this recipe named SimpleStackViews. 0:24 In the file, there's some starter code, 0:27 which you can grab from the link in the notes section of this video. 0:29 In here, we're creating an ImageView with an image, a label, and a button. 0:33 Now that I've initialized the image view with an image literal named mountains, 0:38 that refers to an image inside of my assets catalog. 0:44 For this recipe, we'll need an image to work with, and 0:48 you can grab anything from Flickr. 0:51 They have a handy search that lets you specify the orientation of the picture and 0:53 the license applied to the image. 0:57 I have selected a portrait picture here that is allowed for 0:59 commercial use for example. 1:02 Now once you have your image, doesn't have to be Flickr. 1:04 You can get it from anywhere. 1:06 Download the appropriate size, and drag it over to the Assets catalog in Xcode. 1:08 Once you have it in there, you can substitute your image for 1:13 mine over here to get it to compile. 1:17 Typically, when positioning in sizing views, 1:20 we first add it as a Subview of the route view, but 1:23 since we're going to use a stackView here, we're not going to do that. 1:26 Inside view we'll lay out Subviews. 1:30 Let's create a new stack view. 1:32 The designated initializer for UI stackView 1:35 takes an array of view instances that it will lay out internally. 1:40 The order of these views are important, hence the name arranged subviews for 1:45 the underlying property. 1:49 We need to list out the subviews in the order that we want them layed out, so 1:51 it's imageView first, I need to put this inside an array. 1:56 Then the label, and then the button. 2:01 Now, we're going to add this stackView as the Subview of the root view. 2:07 Now, one important thing to note here. 2:13 You'll notice with all the views that I defined in starter code, I've sent 2:14 translatesAutoresizingMaskIntoConstraints to false for these Subviews. 2:19 And this is so 2:24 we don't end up with implicit constraints that break our layouts. 2:25 So we need to do the same for the stackView that we just created as well. 2:28 So stackView.translatesAutoresizingMaskIntoC- 2:32 onstraints is false. 2:36 And then we'll say view.addSubview(stackView). 2:38 With stackViews, 2:43 while we don't have to add constraints to views contained within the stackView, 2:44 we do have to define constraints that position and size the stackView itself. 2:49 Once the stackView has a size, 2:53 it can use this information to size its arranged Subviews. 2:55 Let's call the activate class method on NS layout constraint, and define 2:59 constraints to position the stackView eight points from the edges on all sides. 3:05 If we're in iOS 11 and up, edges here refers to the safe area on layout guides, 3:10 while in versions of iOS prior to 11, we're going to use the top and 3:15 bottom layout guides for the vertical constraints along with the super view for 3:19 the horizontal. 3:23 So here, before we actually call the class method we'll say if available. 3:24 if #available. 3:28 Platform is iOS 11. 3:31 So anything iOS 11 and up, then we'll use the layout guides. 3:32 So we'll call NSLayoutConstraint.activate. 3:37 And this takes an array. 3:39 We'll start with the top anchor of the stackView and just work our way around. 3:43 So stackView.topAnchor. 3:46 So we want constraint equalTo, that takes another anchor and a constant. 3:50 And here we’ll say view.safeAreaLayoutGuide.topAnchor. 3:54 Constant is 8 points. 3:59 Now, to speed things up, I'm going to select this line, copy it, and 4:01 paste it three more times, and then I'm going to modify these carefully. 4:06 So next we have rightAnchor. 4:10 And this is going to the safeAreaLayoutGuides rightAnchor. 4:13 The constant here, because direction matters, is -8.0, and we have the bottom 4:17 anchor to the safeAreaLayoutGuide's bottom anchor as well. 4:23 And the constant here is the -8.0, cuz the stackView above the safeAreaLayoutGuide. 4:27 And then finally, we have the right anchor or left anchor rather, 4:33 and the constant here stays at a positive 8.0. 4:38 And the else case of this if clause, we'll do the same thing, but 4:44 because don't have safe areas here, we're going to use a top and 4:48 bottom layout guides along with the super views edges. 4:52 Now again, I'm going to copy all of this, paste it inside here. 4:56 And we'll work through this, so stackView.topAnchor.constraint equal to, 5:02 and this is, we'll just get rid of all of this here. 5:08 And it becomes topLayoutGuide.bottomAnchor, so 5:12 the constant value is the same. 5:17 The second one, so from view.safeAreaLayoutGuide.rightAnchor 5:19 we're just going to say view.rightAnchor. 5:23 The third one, we'll say view.safeAreaLayoutGuide.bottomAnchor 5:26 we'll get rid of all this, and we're going to say bottomLayoutGuide.topAnchor. 5:32 And then the last one we're gonna change it to view.leftAnchor. 5:37 Okay, so now stackView should be positioned and sized, and since it’s going 5:41 to handle all of its Subviews that means we should be done, hopefully. 5:46 Let’s navigate to Main.storyboard, and 5:51 this is the default scene that’s going to load when we run the app. 5:54 So we’re gonna select this view control and set its backing class, so 5:57 the one we just created which is, may not be in here. 6:01 There it is, simple stackView controller. 6:08 I'm gonna build and run. 6:10 So we can see this. 6:14 Okay, cool. 6:20 So the stackView seems to be laid out, but it's a horizontal stack view. 6:21 Let's go back to our code here, and 6:25 right after we create the stack view let's modify some of its properties. 6:27 To change the stackView to vertical one, we modify the axis properties. 6:32 So let's say stackView.axis = .vertical. 6:37 Rerun the project, and we should now have a vertical stackView. 6:42 Okay, but we have different issues now, the label and 6:49 buttons seem to be in different spots. 6:52 A stackView's default alignment value is fill, which makes the arranged view 6:54 fill the available space perpendicular to the axis, so horizontal in this case. 6:59 Since we didn't center align the text, it is left aligned by default, and 7:05 fills up the horizontal space and starts to the left. 7:10 To fix this, instead of center aligning our labels, 7:14 what we can do is change the stackView's alignment to center. 7:17 We'll say stackView.alignment = .center. 7:21 In fact, in all alignment values, except fulfill, 7:27 the static view uses the intrinsic content size of the view to size it. 7:31 So despite the labels text still being left aligned, the width of the label is 7:35 now the same as the width of the text, so it appears centered. 7:39 The last thing we need to do, 7:44 is to increase the spacing between each of the arranged Subviews. 7:45 Again, stackViews make this easy, we just need to tweak the spacing property. 7:49 So we can set it to, for example, 32.0 points. 7:53 Now when we do this, you'll notice, as expected, 7:58 the spacing does increase, but the spacing between the stack view and 8:01 the root view of the top decreases, at least on the top edge. 8:05 The stack view seems to be pushing the views out and this pushes the image up and 8:09 out of the stackView's bounds, essentially breaking our layout. 8:14 If you capture the view hierarchy at this point to debug, you'll also notice, and 8:18 I can go ahead and do that, so I'll click on third, 8:23 actually I'll just hover until it says, debug view hierarchy. 8:27 There's a bunch of icons there, so, once you do that, you'll see in the console and 8:31 in the sidebar here, you have these purple exclamation points, and all these 8:38 basically say that the layout is ambiguous and auto layout cannot layout resolve it. 8:43 So while going back here it seems visually okay, 8:48 right here in the simulator, I'm sure it's going to break somewhere. 8:52 Now, the reason this is happening, 8:57 is because the stackView is actually respecting the content hugging, and 8:58 content compression resistance values on the arranged Subviews. 9:02 So rather than shrinking the Subviews to add space between them, 9:07 because they all have equal compression resistance and content hunging values and 9:11 it can't decide on its own, it just adds the space anyway and breaks the layout. 9:15 And by it, I mean the stackView. 9:20 The stackView can decide which view to shrink to accommodate all of our rules, 9:21 our constraints. 9:26 So as the spacing the views grow too large and we break our lay out. 9:27 To fix this, we're going to indicate that the view we want the stack view to modify 9:32 is the image view. 9:37 We're going to indicate that we're okay with the stack view resizing the image 9:38 view, so that everything else fits. 9:42 And we'll do this by lowering both the content hugging, and 9:44 the compression resistance priority values on the image view. 9:48 Now, we only need to do this in the vertical axis though, 9:52 since we have no issues horizontally. 9:54 And we'll do this back up at the top where I've defined the view, so 9:56 inside this closure that creates and returns the view. 9:59 We'll add two more lines of code. 10:03 We'll say imageView, actually you know what, we'll keep this all in one place. 10:04 Let's go back down here. 10:08 Before we add this to the stackView, we'll say imageView.setContentHuggingPriority. 10:09 So UILayoutPriority. 10:16 With a value of 249 for the vertical orientation. 10:18 Now we also need to modify the imageView's content compression resistence and again, 10:26 it's setContentCompressionResistence. 10:31 The value, which we'll wrap in this UI layout priority struct is 749. 10:35 Since we want it to be lower than any of the other views, and 10:41 those have a value of 750. 10:44 And we'll set it to the vertical axis. 10:47 Now, when we run the app layout is fixed both in that it looks visually more or 10:49 less the same, but if you capture the layout hierarchy now the system, 10:54 the stackView, 10:59 knows it can resize the image view in case things need to be shrunk down. 10:59 It's important to note the advantage the stackView offers here, 11:05 even with such a simple layout. 11:08 Had we attempted this without stackViews, achieving the equal spacing between 11:10 all the views would have necessitated either spacer views or layout guides. 11:15 More over, tweaking the amount of spacing would have meant modifying several 11:20 constraints, where as with stackViews all we have to do is tweak a single value. 11:24 The stackView currently has a distribution value of fill with an alignment of center. 11:29 Modifying this to have, for 11:34 example, a fill proportionally distribution is pretty easy. 11:35 If you're interested in implementing this recipe in interface builder, 11:40 check the notes section for link to the relevant video. 11:43
You need to sign up for Treehouse in order to download course files.Sign up