Views and Frames

When you initialize a view programmatically, you use its init(frame:) designated initializer. This method takes one argument, a CGRect, that will become the view’s frame, a property on UIView.

var frame: CGRect

A view’s frame specifies the view’s size and its position relative to its superview. Because a view’s size is always specified by its frame, a view is always a rectangle.

A CGRect contains the members origin and size. The origin is a structure of type CGPoint and contains two CGFloat properties: x and y. The size is a structure of type CGSize and has two CGFloat properties: width and height (Figure 3.5).

Figure 3.5  CGRect

Figure represents the CGRect diagram.

When the application is launched, the view for the initial view controller is added to the root-level window. This view controller is represented by the ViewController class defined in ViewController.swift. We will discuss what a view controller is in Chapter 5, but for now, it is sufficient to know that a view controller has a view and that the view associated with the main view controller for the application is added as a subview of the window.

Before you create the views for WorldTrotter, you are going to add some practice views programmatically to explore views and their properties and see how the interfaces for applications are created.

Open ViewController.swift and delete any methods that the template created. Your file should look like this:

import UIKit

class ViewController: UIViewController {

}

(UIKit, which you also saw in Chapter 1, is a framework. A framework is a collection of related classes and resources. The UIKit framework defines many of the UI elements that your users see, as well as other iOS-specific classes. You will be using a few different frameworks as you go through this book.)

Right after the view controller’s view is loaded into memory, its viewDidLoad() method is called. This method gives you an opportunity to customize the view hierarchy, so it is a great place to add your practice views.

In ViewController.swift, override viewDidLoad(). Create a CGRect that will be the frame of a UIView. Next, create an instance of UIView and set its backgroundColor property to blue. Finally, add the UIView as a subview of the view controller’s view to make it part of the view hierarchy. (Much of this will not look familiar. That is fine. We will explain more after you enter the code.)

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let firstFrame = CGRect(x: 160, y: 240, width: 100, height: 150)
        let firstView = UIView(frame: firstFrame)
        firstView.backgroundColor = UIColor.blue
        view.addSubview(firstView)
    }

}

To create a CGRect, you use its initializer and pass in the values for origin.x, origin.y, size.width, and size.height.

To set the backgroundColor, you use the UIColor class property blue. This is a computed property that initializes an instance of UIColor that is configured to be blue. There are a number of UIColor class properties for common colors, such as green, black, and clear.

Build and run the application (Command-R). You will see a blue rectangle that is the instance of UIView. Because the origin of the UIView’s frame is (160, 240), the rectangle’s top-left corner is 160 points to the right and 240 points down from the top-left corner of its superview. The view stretches 100 points to the right and 150 points down from its origin, in accordance with its frame’s size (Figure 3.6).

Figure 3.6  WorldTrotter with one UIView

Screenshot of the builder interface showing the target positioning of the UIView.

Note that these values are in points, not pixels. If the values were in pixels, then they would not be consistent across displays of different resolutions (i.e., Retina versus non-Retina). A point is a relative unit of a measure; it will be a different number of pixels depending on how many pixels are in the display. Sizes, positions, lines, and curves are always described in points to allow for differences in display resolution.

Figure 3.7 represents the view hierarchy that you have created.

Figure 3.7  Current view hierarchy

Diagrammatic representation of the Current View hierarchy created in this project.

Every instance of UIView has a superview property. When you add a view as a subview of another view, the inverse relationship is automatically established. In this case, the UIView’s superview is the UIWindow.

Let’s experiment with the view hierarchy. First, in ViewController.swift, create another instance of UIView with a different frame and background color.

override func viewDidLoad() {
    super.viewDidLoad()

    let firstFrame = CGRect(x: 160, y: 240, width: 100, height: 150)
    let firstView = UIView(frame: firstFrame)
    firstView.backgroundColor = UIColor.blue
    view.addSubview(firstView)

    let secondFrame = CGRect(x: 20, y: 30, width: 50, height: 50)
    let secondView = UIView(frame: secondFrame)
    secondView.backgroundColor = UIColor.green
    view.addSubview(secondView)
}

Build and run again. In addition to the blue rectangle, you will see a green square near the top-left corner of the window. Figure 3.8 shows the updated view hierarchy.

Figure 3.8  Updated view hierarchy with two subviews as siblings

Diagrammatic representation of the updated UI View.

Now you are going to adjust the view hierarchy so that one instance of UIView is a subview of the other UIView instead of the view controller’s view. In ViewController.swift, add secondView as a subview of firstView.

...
let secondView = UIView(frame: secondFrame)
secondView.backgroundColor = UIColor.green
view.addSubview(secondView)
firstView.addSubview(secondView)

Your view hierarchy is now four levels deep, as shown in Figure 3.9.

Figure 3.9  One UIView as a subview of the other

Diagrammatic representation of the view hierarchy after making one UI view as a subview of the other.

Build and run the application. Notice that secondView’s position on the screen has changed (Figure 3.10). A view’s frame is relative to its superview, so the top-left corner of secondView is now inset (20, 30) points from the top-left corner of firstView.

Figure 3.10  WorldTrotter with new hierarchy

Screenshot of the builder interface displaying the updated hierarchy. In the middle of the screen canvas, a green rectangle (small) is placed over a blue rectangle (big).

(If the green instance of UIView looks smaller than it did previously, that is just an optical illusion. Its size has not changed.)

Now that you have seen the basics of views and the view hierarchy, you can start working on the interface for WorldTrotter. Instead of building up the interface programmatically, you will use Interface Builder to visually lay out the interface, as you did in Chapter 1.

In ViewController.swift, start by removing your practice code.

override func viewDidLoad() {
    super.viewDidLoad()

    let firstFrame = CGRect(x: 160, y: 240, width: 100, height: 150)
    let firstView = UIView(frame: firstFrame)
    firstView.backgroundColor = UIColor.blue
    view.addSubview(firstView)

    let secondFrame = CGRect(x: 20, y: 30, width: 50, height: 50)
    let secondView = UIView(frame: secondFrame)
    secondView.backgroundColor = UIColor.green
    firstView.addSubview(secondView)
}

Now let’s add some views to the interface and set their frames.

Open Main.storyboard. At the bottom of the canvas, make sure the View as button is configured to display an iPhone 7 device.

From the object library, drag five instances of UILabel onto the canvas. Set their text to match Figure 3.11. As shown, space them out vertically on the top half of the interface and center them horizontally.

Figure 3.11  Adding labels to the interface

Screenshot shows the desired text for the five UI Labels in the WorldTrotter app. UILabel one is set to 212. UILabel two is set to degrees Fahrenheit. UILabel three is set to is really. UILabel four is set to 100. UILabel five is set to degrees Celsius.

Select the top label so you can see its frame in Interface Builder. Open its size inspector – the fifth tab in the utilities area. (The keyboard shortcuts for the utilities tabs are Command-Option plus the tab number. The size inspector is the fifth tab, so its keyboard shortcut is Command-Option-5.)

Under the View section, find Frame Rectangle. (If you do not see it, you might need to select it from the Show pop-up menu.) The values shown are the view’s frame, and they dictate the position of the view onscreen (Figure 3.12).

Figure 3.12  View frame values

Screenshot of the frame values menu. Show field (spin box) is set to Frame Rectangle. X coordinate is 173, Y coordinate is 30. Width is 28 and Height is 21. All data fields are spin boxes.

Build and run the application on the iPhone 7 simulator. The interface on the simulator will look identical to the interface that you laid out in Interface Builder.

Customizing the labels

Let’s make the interface look a little bit better by customizing the view properties.

In Main.storyboard, select the background view. Open the attributes inspector and give the app a new background color: Find and click the Background dropdown and click Other.... Select the second tab (the Color Sliders tab) and choose RGB Sliders from the dropdown. In the box near the bottom, enter a Hex Color # of F5F4F1 (Figure 3.13). This will give the background a warm gray color.

Figure 3.13  Changing the background color

Screenshot of the Colors panel of the Attributes inspector with its RGB slider tab selected.

You can customize attributes common to selected views simultaneously. You will use this to give many of the labels a larger font size as well as a burnt orange text color.

Select the top two and bottom two labels by Command-clicking them in the document outline. Make sure the attributes inspector is open and update the text color: Under the Label section, find Color and open the pop-up menu. Select the Color Sliders tab again and enter a Hex Color # of E15829.

Now let’s update the font. Select the 212 and 100 labels. Under the Label section in the attributes inspector, find Font and click on the text icon next to the current font. In the popover that appears, make the Size 70 (Figure 3.14). Select the remaining three labels. Open their Font pop-up and make the Size 36.

Figure 3.14  Customizing the labels’ font

Screenshot of the Label attribute inspector overlapped by the font pop-up menu.

Now that the font size is larger, the text no longer fits within the bounds of the label. You could resize the labels manually, but there is an easier way.

Select the top label on the canvas. From Xcode’s Editor menu, select Size to Fit Content (Command-=). This will resize the label to exactly fit its text contents. Repeat the process for the other four labels. (You can select all four labels to resize them all at once.) Now move the labels so that they are again nicely aligned vertically and centered horizontally (Figure 3.15).

Figure 3.15  Updating the label frames

Screenshot of the interface canvas showing the result of the selected preset. The text size in the five labels is bigger.

Build and run the application on the iPhone 7 simulator. Now build and run the application on the iPhone 7 Plus simulator. Notice that the labels are no longer centered – instead, they appear shifted slightly to the left.

You have just seen two of the major problems with absolute frames. First, when the contents change (like when you changed the font size), the frames do not automatically update. Second, the view does not look equally good on different sizes of screens.

In general, you should not use absolute frames for your views. Instead, you should use Auto Layout to flexibly compute the frames for you based on constraints that you specify for each view. For example, what you really want for WorldTrotter is for the labels to remain the same distance from the top of the screen and to remain horizontally centered within their superview. They should also update if the font or text of the labels change. This is what you will accomplish in the next section.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset