There are few common ways to specify the layout of elements in a UIView, you always can use interface builder, or you can create your own code with NSLayoutConstraints and arrange your view with Auto Layout, I am not sure if there is a “right way to do it”, but for me this two approaches are complicated and sometimes very frustrating.
That’s why in this post I want to share with you a different way to layout views based on coordinates and sizes!
CGRect is a structure defined in the CoreGraphics framework and contains the location and dimensions of a rectangle, and we are going to use this and some really basic math to create super flexible layouts that adjust in every device.
Because this is a simple tutorial based in laying out views, we are going to keep it simple, we are going to have three views laid out like this, no matter the size of the subViews, they are always going to be aligned one to each other and also positioned in the center of the screen.
So, let’s begin creating a new project in Xcode and make sure to select “Universal” in devices if you want to test this also in iPads.
You should have one ViewController in your storyboard and a class file attached to it, ready? cool …let’s open the file and add three UIViews properties like this…
Now, let’s give this views a background color and add them to the main view. So, add this inside
If you are not familiar adding UI elements programmatically, this is how you do it, you initialize the element like any other object, and after declaring some properties of it (in this case it’s background color), you just add it to the parent view.
Now run the project…
What just happened, why is it blank? well, we forgot something, when you add a view in code you must declare it’s frame, if not, the element won’t show up because it doesn’t have a size or location.
So let’s do it right now, we are going to add some code inside a method declared in UIKit called
viewWillLayoutSubviews, this method is called to notify the view controller that its view is about to layout its subviews.
But first, just a reminder, as you know the best code is the one that you can reuse, but also the one that let you make fast modifications in your app without the necessity to make changes in different places of your project, you will see what I mean with more clarity at the end of this tutorial; based on this assertion I am going to start declaring some variables that will allow us to change the layout as we want very easy. Add
viewWillLayoutSubviews with this variables…
If you are new with Xcode, the warnings “unused variable” are probably annoying you, don’t worry it means that you didn’t use these variables yet. I will go line by line, but first it’s important to understand that every UI element it’s just a “rectangle”, and have a frame and bounds, we can use its frame to layout a view based on the coordinates of it parent view or the bounds to layout elements within it. Every “rectangle” has a width and height and also X and Y coordinate point, and these values are floats.
The first line is a variable called innerSpace of type CGFloat, this will determine the space between the elements in the UI.
The second and third lines are variables for the height and the width of the screen.
The fourth and fifth, are the height and width of a subView, in this stage, I decide to declare the height of a subView as the 40% of the whole view, and the width the 80%.
Now, let’s start laying out the redView, remember that at the beginning of this post I said that I am going to layout all the views dynamically taking as reference the center of the screen, and for that, we are going to need very simple math.
Add this below …
We are initializing a CGRect variable with the frame of the redView, in objective C this is how you can declare a frame or you can also use the
CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height) to do it in one line, I prefer the first approach because at least for me it’s more readable.
First, we are declaring the frame variable of type CGRect with the frame of the redView, which at this moment its (0 X , 0 Y, 0 width, 0 height).
Then we are assigning the values of the subViewWidth and subViewHeight variables that we declare at first, to be the values for frame.size.height and frame.size.width respectively.
For the origin X and Y it’s where things get interesting. Let’s start with the X coordinate, here we are subtracting the frame.size.width value (which at this moment is the value of subViewWidth or the 80% width of the whole screen), to the value of the mainViewWidth (which is the 100% of the total width of the screen), and finally dividing the result by 2, this will give us a coordinate point that will align the view in the center X of the screen. For the origin Y we want to place the view in some way that when we add the missing views, all of them are aligned to each other and also to the center of the screen.
Based on the simulator screenshot we are going to have three views, one big and two smaller ones, the bigger on top and the smaller ones bellow, good for us we already know the height of each subView, also the height of the innerSpace and the total height of the screen. Remember that every height’s subView its the 40% of the total height of the screen? well, let’s multiply that for 2 (because vertically we are going to add two views with the same height) let’s add to that the value of innerSpace, the result it’s going to be the total height of the elements in the main View, one more time just subtract this value to the value of the MainViewHeight and divide it by 2. (line 49)
Finally, assign the frame to the redView.frame and build the project…
As expected the redView occupies the 40% of the height of the screen and it’s origin Y it’s where we wanted!
Let’s add the rest of the code to layout the green and yellow views.
When you layout elements using frames always take as a reference the first element that you add to your view, in this case, the redView.
Because we are going to add two smaller views one next to the other and we want them to be aligned, let’s start updating the value of the subViewWidth in line 52. Taking as a point of reference the redView let’s set a new width for the subViews, so start subtracting the innerSpace value to the redView width value and divide the result by 2, and you will have a new width for this new elements.(line 52)
After that, let’s start with the yellowView, start with a frame as we did it before, add the values of the new subViewWidth and the subViewHeight.
For the X and Y coordinates we are going to use the
CGRectGetMaxY() , these methods take as a parameter a frame (which is just a CGrect) and returns the min coordinates X or Y of a view, or the max X or Y coordinate of a view, depends on the method, this are the secret sauce for my layouts, I strongly recommend to read their documentation.
As an example of how to use them let me explain what it’s going on in line 56, by getting the redView origin x, we are assigning that value to the origin X of the yellowView frame, so now, both views start at the same X coordinate, make sense?
And in line 57 we get the maxY coordinate of redView, which is where “vertically ” the redView ends, or in better words, the value of it’s maximum Y coordinate, and add the innerSpace value to add some space between views.
Let’s do the same for the greenView, this time using the max X coordinate value of the yewllowView frame (where the yellowView ends horizontally, the maximum X coordinate value) and add to it the value of innerSpace, finally for the Y coordinate lets do the same that we just did for the yellowView origin Y coordinate in line 57.
Run the project and this time also press
CMD + right Arrow and voila! your views are perfectly layout even in landscape mode!
I know, I know, that’s not enough to impress you but if you didn’t knew how cool is to use CGRect to layout views, now you have another tool to do it, one last trick…
Remember when I said that good code is the one that let you make changes with less effort? …well, let’s prove it, go to your code and play around with the value of the variables that we create at the beginning, for example, change the value of innerSpace to 30, the multiplier in subViewHeight for 0.3, and the multiplier of subviewWidth to 0.4.
Yes!, your views are still in the center of the mainView! try to run it in iPad or even in a 4s and will always look perfect, something less to worry about right?
Btw, if you want to see how to accomplish the same with AutoLayout check this tutorial of Ray Wenderlich.
For more follow me on Twitter.