In this video, go further into the API provided by LayoutTest to learn how to provide more custom logic when trying to test a view with different sizes. Plus, learn about edge cases where it may be necessary to use this more advanced sizing methodology in order to properly test your view.
- [Lecturer] In the previous video we learned two different methods for providing different sizes for LayoutTest to use to test our view. In this video, we will explore more advanced concepts around these sizing methodologies. In LayoutTest if you want to perform some function after the view is actually sized you need to implement a specific method called adjustViewSize. Let's open the workspace for 02_04. Let's open the InformationalViewLayoutTests file is the LayoutTest file for our new view, InformationalView.
Let's take a look at the XIB file first. This is what it looks like and what we want to do is dynamically size the height based on whatever text is specified in the Title and Description. Now let's take a look at the LayoutTest file. Let's hit Command + U just so that we have the errors resolved. And you'll see our test may fail. And we're going to fix that in this video.
You'll see that this time I changed how we're doing the implementation a little bit, instead of making our InformationalView plus LayoutTest file where we make our extension of ViewProvider and provide the data, I did it a little differently just to show you you could do it like this as well, if you'd like. So in the LayoutTests file I do the extension and then also have the LayoutTest here. Since the LayoutTest is so small you may find it easier and simpler to put them in the same file. Now, we're going to be implementing the adjustViewSize method.
This is also a part of the ViewProvider protocol. So, let's type this here, adjustViewSize. Again, let's make it public. This is called right after LayoutTest automatically sizes the view for you based on the ViewSize properties that you set, which, if you remember from the last video we are using BaseLayoutTestCase which has all of these sizes set for us already. In this method we can do anything based on the size, the thing we're going to be doing and which is the most common is changing the height of the view depending on the width.
Now we're going to click on InformationalView.swift. In here you'll see we have a method called height(forBoundingWidth). This gives you the height of the view for the given width. You'll see a nice helpful method in here called textHeight ofLabel forBoundingWidth. May find this useful in your apps as well. This gives you the height of a UI label based on the text and different properties that are set, such as the number of lines. In the height(forBoundingWidth) method we use the textHeight ofLabel method and the different properties and constraint constants to calculate how tall the view is based on the content that has been set.
We're going to use this in our LayoutTest's adjustViewSize method to calculate the height. Let's go back to the InformationalViewLayoutTests file. If we look at the erors in the Issue navigator you'll see that the failure is that the, "Bottom side extends past the superview for the UIButton." So that means that we need to be changing the size of the view, the height, based on the content, because right now it's being cut off. So let's implement this method to fix that. We're doing an if let getting the informationalView by casting it to the InformationalView.
We're also going to get the width if the size is not nil and if both of those things are not nil we're going to try to get the height from the informationalView. So we call our method, height(forBoundingWidth) with this width, we're going to store this as the height and now we're going to get the originalFrame of the view, informationalView.frame, and we're going to create a newFrame.
Let's put this on separate lines to make it more readable. And we can use Tab and Shift + Tab to go backwards. Now, our x and y coordinates and our width are all going to be the same as the originalFrame, so I'm just going to fill those in, originalFrame.x, I will copy this, paste it and do y. The width, again, is the same, so width and the height is going to be the new height that we have.
Now, you see an error here, I made a mistake that the origin is the property that has the x and y values, not the frame itself. Now we have our new frame, let's set the frame of the informationalView to this newFrame. And with that we have adjusted the size of the view based on the ViewSize that was passed in. Now let's try running it and make sure it all passes. Command + U to run our tests.
Success! Perfect. Now, if you find that your ViewSize isn't being changed when you set the frame, you can use the code in this file to create constraints for the height and width, instead of setting the frame directly. I've included this just for illustration purposes, the view that we have here doesn't actually need this, as you see, the test passed. So, if you do have a viewer that's not working and it uses Auto Layout you can use this code to fix the problem. When you do, your view provider in the view(forData) method, what you'll do is you'll add a heightConstraint like so, NSLayoutConstraint, height, equal, zero, and we set it zero just as a placeholder for now and a widthConstraint same way.
You can also use whatever different syntax you would like to use, then we call view.addConstraint and add the height and widthConstraint and then return the view. So now those constraints have been added with a zero constant, because we cannot change those until the adjustViewSize method. In the adjustViewSize method we then go and get the size that was passed in, we get the width, we get the view as informationalView and substitute this with any view you have, then we get the correct size by doing CGSize with the width and the height using whatever height calculation method that you have for your view, in this case the same one we used before, height(forBoundingWidth).
Now we go through the views.constraints and filter, based on the firstAttribute being height, which should only be one of them. So we get the first, that should be our heightConstraint and we're going to set the constant based on this correct size. I'm going to do the same thing for the width, widthConstraint, filter through, firstAttribute width, .first, we get the constant and set it to the correctSize.width. This is an alternate way to do it that uses Auto Layout that you may find necessary at some point.
In this video we learned how to work with the adjustViewSize method in order to perform functions on the ViewSized based on the size of the screen being tested. You must always make sure to write code that modifies the ViewSize in this method or else it will not work.
- Installing the library
- Specifying test data
- Reviewing property-based testing
- Using test data and writing the LayoutTest test
- Testing views at different sizes
- Debugging with snapshots
- Dealing with common errors
- Advanced debugging tips
- Exploring catalog view