Tutorial: Improving Your iOS App Architecture – Part 2 of 2

This tutorial is an excerpt from our book series iOS App Development for Non-Programmers, available in the iBookStore for the iPad, and on Amazon.


Part 1 of this post looks at a sample App with standard iOS architecture and points out the pitfalls you need to avoid. Part 2 of this post demonstrates the same sample App with an improved architecture that helps make the App easier to design, create and maintain.


In Part 1 of this post, we learned that the iOS view controller is a user interface object that is tightly bound to the user interface. This is not a problem. The problem occurs when core logic is placed in the view controller because it prevents the code from being reused and extended as your App goes through its normal change life cycle.


So, how do you fix this problem? You need to put the Calculator’s core logic in some other place where you can access it from multiple Apps, or from multiple view controllers in a single App. If you shouldn’t put your core logic in a view controller, where should you put it? 


Business Controller Objects


The answer is, a business controller object (also known as a business object or domain object). If you have read Book 2: Flying with Objective-C, you have already seen business objects at work—for that matter, you have even seen a Calculator business object.


A business object provides a neutral place to put your core logic that can be reused from any user interface, which includes view controllers. As shown in Figure 11.24, you can create a Calculator business object in which you put all of the Calculator’s core logic methods.



Figure 11.24 Implementing a Calculator business object


In this figure, the Calculator and View Controller are tightly bound together—and that’s OK—they are both user-interface objects. When buttons are tapped on the calculator at run time, action methods in the view controller are called directly. 


The only code you should put in the view controller is code that has something to do with the user interface. However, when core logic needs to be performed, the view controller makes a call to an appropriate method in the Calculator business object.


With your core logic in the Calculator class, you can easily create a ScientificCalculator subclass, which inherits all the basic functionality of the Calculator and extends it by adding additional operations as shown in Figure 11.25.



Figure 11.25 You can easily extend the core logic of the Calculator when it resides in its own class.


Because you have elevated your core logic out of the user interface and into a Calculator business object, it makes it far easier to extend by creating a subclass.



Other Benefits of Using Business Objects


When you create business objects, you are creating a representation of objects in the real world. In the Calculator App, you are creating a representation of a real-world calculator. In a real estate App you can create House, Buyer, Owner, and RealEstateAgent business classes that represent real-world entities (Figure 11.26).



Figure 11.26 Business objects represent real world entities.


Creating business objects help you bridge something called the semantic gap. The semantic gap is the difference between real world entities and how you model, or describe these objects in your software applications. In many Apps, this gap is extremely wide because the developer has not created any business objects. You will find that when you narrow the semantic gap by creating business objects that represent real-world entities, your Apps are much easier to conceive, design, build, maintain, and extend. 


You model real-world entity attributes by means of properties, and you model their behavior by means of methods. For example, a house has attributes such address, number of bedrooms, number of baths, price, and so on which can be described, or modeled, as properties. A house also has behavior such as “put on the market,” “take off the market,” and so on which can be modeled as methods on a business object class.


Another benefit of using business objects is it helps you avoid playing the game of “where’s the code?” When your core-logic code is raised out of the weeds of the user interface and into business objects, it’s far easier to find the code you want. 


For example, all the core-logic code that has something to do with a homeowner is in the HomeOwner business object. All the code that has something to do with a real-estate agent is in the RealEstateAgent project, and all the code that has something to do with the user interface is in a view controller. When your code is segregated in this way, it makes it far easier to find the code you need.


In contrast, when your core logic code is tangled up in your user interface, it’s much harder to find the code you need. You can see a great example of this in the CalculatorDemo project. 


If you haven’t already downloaded the sample code from part 1 of this blog post, you can download it here:


Download Sample Code


  1. If it’s not already open, in Xcode, open the CalculatorDemo project again.

  2. In the Navigator panel on the left side of the Xcode window, select the Symbol Navigator by clicking the second button from the left in the Navigator toolbar, or by pressing Command+2
  3. As shown in Figure 11.27, there are only two classes in the project—AppDelegate and ViewController

    Figure 11.27 CalculatorDemo project classes

  4. Expand the ViewController node to view all of its members. As you can see, there are many methods in the class, some user interface related, and some containing core logic. This mix of methods can make it difficult to find the code you need.

In the next section, you will compare an improved Calculator sample project in the Symbol Navigator.


And one other thing—using business objects helps you avoid code redundancy. When you don’t use business objects, it’s easy to create duplicate code, because you often forget you have already created a piece of functionality in another view controller. When you are using business objects, it’s less likely you will create two methods on the same business object that perform the exact same function.


So, elevate your code, and use business objects.


Now let’s see how a Calculator business object can help in an improved Calculator sample App.



The Improved Calculator Sample App



To see this proper division of user interface and core logic, let’s check out another sample project for this chapter.



  1. In Xcode, open the CalculatorPlusDemo project located in this book’s sample code.

  2. Press the Run button in Xcode to run the project in the Simulator. Go ahead and test it out by performing calculations. As you can see, it works just like the CalculatorDemo App. 

  3. When you’re done, go back to Xcode, and press the Stop button.

  4. In the Project Navigator, drill down into the CalculatorPlusDemo node and then expand the User Interface and Core Logic groups. As you can see in Figure 11.28, under the User Interface group are the storyboard and view-controller user-interface files. Under the Core Logic group are the Calculator business-object class files.
  5. Figure 11.28 CalculatorPlusDemo project files

  6. Select the Symbol Navigator by clicking the second button from the left in the Navigator toolbar, or by pressing Command+2. Expand the Calculator and ViewController nodes to see their class members. As shown in Figure 11.29 (the Symbol Navigator is split in two to make it easier to view), there is a clear division of responsibilities between the Calculator class and the ViewController class.
  7. Figure 11.29 CalculatorPlusDemo project classes

  8. Let’s take a closer look at the source code for these classes. Go back to the Project Navigator by clicking the first button on the left in the Navigator toolbar, or by typing Command+1. Select the ViewController.h header file in the Project Navigator. As we look through the code in this class, remember that a view controller is a user-interface class.

  9. The first thing to take note of is that the Operation enumeration that was declared in the view controller in the CalculatorDemo project is missing. This is appropriate because it has nothing to do with the user interface.

    Take a look at the list of properties in the file:

    @property (strong, nonatomic) Calculator *calculator;

    @property (weak, nonatomic) IBOutlet UILabel *lblTotal;

    @property (strong, nonatomic) IBOutletCollection(UIImageView) NSArray *operationHighlightImages;

    Notice that the value property that was in the CalculatorDemo project’s view controller is missing. Again, this is appropriate, because it has nothing to do with the user interface—it contains the current value of the Calculator.

    Take note of the new Calculator property. This property holds a reference to the Calculator business object. This allows any method in the view controller to easily access the Calculator object and call its methods.

  10. Look below the property declarations to see the public method declarations:

    – (IBAction)highlightOperation:(UIButton *) sender;
    – (IBAction)numberTouched:(UIButton *) sender;
    – (IBAction)operationTouched:(UIButton *) sender;

    These are the same public action methods as found in the CalculatorDemo project. This is appropriate because these are all user-interface-specific methods that are tied to user-interface controls in the view.

  11. Go back to the Project Navigator and select the ViewController.m implementation file. At the top of the code file, check out the viewDidLoad method:

    – (void)viewDidLoad
    {
        [super viewDidLoad];

        // Create the calculator
         self.calculator = [[Calculator alloc] init];
    }

    This code creates an instance of the Calculator class and stores a reference to the object in the calculator property.

  12. Let’s contrast the message flow of the improved CalculatorPlusDemo App with the CalculatorDemo App. Figure 11.30 provides a high-level overview of what happens when a user touches a numeric button in the improved CalculatorPlusDemo App.


  13. Figure 11.30 Calculator numeric button and code interaction—the improved model

    1.  The user touches a numeric button.

    2. The numberTouched: action method in the view controller is called, which contains user-interface-specific code.

    3. After executing the user-interface processing, the view controller calls the Calculator object’s processNumber: method.

    4. The Calculator object’s processNumber: method executes the core logic for handling a new number and returns the Calculator’s current value.

    5. The view controller takes the value returned from the Calculator’s processNumber: method and stores it in the text property of the lblTotal label.

    This is very similar to the high-level overview in Figure 11.21, except the processNumber: method has been moved from the ViewController class to the Calculator class.

  14. Now let’s look at a high-level overview of what happens when the user touches an operation button as shown in Figure 11.31

    .

    Figure 11.31 Calculator operation button and code interaction—the improved model

    1. The user touches an operation button.

    2. The operationTouched: action method in the view controller is called, which contains user-interface-specific code.

    3. After executing the user-interface processing, the view controller calls the Calculator object’s performOperation: method.

    4. The Calculator object’s performOperation: method executes the core-operation logic and returns the Calculator’s current value.

    5. The view controller takes the value returned from the Calculator’s performOperation: method and stores it in the text property of the lblTotal label.

    Again, this is very similar to the high-level overview in Figure 11.22, except the processOperation method has been moved from the ViewController class to the Calculator class.


Although this simple App has just one business controller object referenced from a single view controller, more complex Apps may reference several business objects. For example, Figure 11.32 shows a view controller that references four different business controller objects.



Figure 11.32 A single view controller can reference multiple business objects.


You can add properties to the View Controller that reference the business controller objects just as the ViewController class references the Calculator object in the CalculatorPlusDemo project.


Business Objects are User Interface Agnostic


Before moving on, it’s important to note that the business object knows nothing about the user interface in which it’s being used. Within the business-object properties and methods there is no reference at all to any user-interface element. This means you can reuse this business object from any view controller in the App, from a completely different iOS App, or, for that matter, you could even use it in a Mac OSX desktop application!


Business Objects and the MVC Pattern



When it comes to the Model-View-Controller (MVC) design pattern, you have learned the View and View Controller comprise your user interface, and together, are the View in the MVC pattern.


You have also learned that your App’s core logic should be contained in business objects that represent real-world entities. These business objects are the Controller in the MVC pattern.


But what about the Model? Earlier in this chapter you learned that the Model is your App’s data and usually takes the form of business entities.


In Chapter 12: Working with Core Data, you are going to learn about an iOS technology known as Core Data that uses something called entity objects. These entity objects are part of the business-object picture—half of the picture to be precise. Entity objects contain attributes that describe real-world entities. Business controllers are the other half of the business object picture. They are used to model the behavior of a real-world entity as you saw with the Calculator and ScientificCalculator classes in Figure 11.25.


Figure 11.33 completes the picture and shows you how each of these pieces fit into the Model-View-Controller design pattern.



Figure 11.33 Each piece of the architecture fits into the MVC design pattern.


  • Model – The business entity is the Model in the MVC pattern. The properties of the business entity contain information that comprises the App’s data.

  • View – Both the View and the View Controller are user-interface objects and are therefore the View in MVC. All user-interface-specific code goes in the view controller.

  • Controller – The business-object controller is the Controller in the MVC pattern. All core logic goes into the business-object controller.

For each business entity you create for your App, you can create an associated business controller. For example, in Figure 11.34, for every business entity, there is a corresponding business object. The CustomerEntity class has a Customer business object, the ShoppingCartEntity class has a ShoppingCart business object, and so on.



Figure 11.34 For each business entity in your App, you can create a corresponding business controller.



As already mentioned, the entity class models a real-world entity’s attributes by means of properties. The associated business-controller object models its behavior by means of methods. 


Business controllers can also be used to retrieve and update entity objects. Unfortunately, Apple’s default core-data model places the business entity retrieval and update code in the view controller. As you might imagine, this is a bad design, because it ties your App’s data access to the user interface. Placing your App’s data access logic in the business controller allows you to use your business controller objects from any user interface. You will learn more about this in Chapter 12: Working with Core Data.



Kevin McNeish


Twitter: @kjmcneish


 


 


 

Tutorial: Improving Your iOS App Architecture – Part 1 of 2

This tutorial is an excerpt from our book series iOS App Development for Non-Programmers, available in the iBookStore for the iPad, and on Amazon.


Part 1 of this post looks at a sample App with standard iOS architecture and points out the pitfalls you need to avoid. Part 2 of this post demonstrates the same sample App with an improved architecture that helps make the App easier to design, create and maintain.


The Importance of Solid Architecture


Architecture. The word is usually associated with constructing a building. Whether it’s as small as a shed or as large as a skyscraper, architecture is important to make sure the design is sound. This is no less true of constructing software. Whether it’s a small iOS App or a large business application, a good architecture can make it easier to design, build, and extend your software.


Why should you care about your App’s architecture? A poorly designed App is more difficult to design, create, and maintain. A well-designed App is the gift that keeps on giving. You will save yourself, time, energy, and cost by creating an App with a solid architecture. 


Design Patterns to the Rescue


Recently, when I needed to build a horse shed, rather than starting from scratch, I went to the Internet to see what architectural plans other people were sharing. This was my first time building a horse shed, and I knew others had already learned from experience what to do, and what not to do. There were specific patterns for building this kind of structure that I wanted to learn rather than make all the mistakes myself.


Fortunately, others have come before you and have built many different types of software applications. What these developers and architects have learned has been distilled into some common design patterns. The great thing about these patterns is they work well regardless of the tools you are using—whether it’s Objective-C and iOS, Java and Android, or C# and Windows Phone. In this chapter, you will learn about some of these design patterns that Apple has implemented in iOS App development, and you will see how you can easily improve on some of these patterns.


Your App Will Change


Let’s be clear about this. Your App will change. Not just once or twice, but many times over—and that’s even before you release it to the App store for the first time. After it’s released, your App will change even more as others use it, provide feedback and suggest enhancements. You have to be prepared for that, because it’s a reality of writing Apps.


If your App is designed to anticipate change, this process is much easier. If your App is not designed to anticipate change, you are headed for a lot of tedious and unnecessary work. Keep in mind that the majority of your time is not spent in the initial creation of the App—the majority of your time is spent updating and enhancing the App as you roll out subsequent versions. Also, there are changes that come from Apple as they release new devices and add new features to iOS. 


If you get a change request and you find that you need to tear apart your App to implement the change, then you didn’t do your job as an architect! You want to make sure you have designed your App to anticipate change to make this process as smooth as possible.


Where Do You Put Your Code?

In Book 1: Diving In, you learned there are three main parts of an App:

  • User Interface

  • Core Logic

  • Data

Having these separate parts in mind when creating your App is a great start in creating a solid architecture. When these different parts are too tightly bound together, you create what is known as a monolithic architecture that is difficult to change. It creates a situation where you can’t change one part of the App without changing the other.

Fortunately, it’s not difficult to follow sensible architectural principles. Ultimately, where you put your code has everything to do with how easy it is to write your App, extend it, and maintain it. Putting your code in logical, predictable places also helps you avoid the game of “where’s the code?” that many developers play on a daily basis.

Model-View-Controller

A more formal way to look at the three main parts of an App is by means of the Model-View-Controller design pattern. This design pattern has been around for many years and helps you clearly partition the main parts of your App so it is easier to design, enhance, and maintain. 

Because of my background in software architecture, when I first came to the iOS platform back in 2008, I was intrigued to hear that Apple encouraged the use of the Model-View-Controller design pattern in iOS App development.

Here’s how the Model-View-Controller (also known as MVC) pattern maps to the main parts of your App:

  • Model →  Data

  • View → User Interface

  • Controller → Core Logic

Let’s break this down by first looking at a traditional implementation of the MVC pattern, and then we can look at Apple’s implementation.

Model

The Model is your application’s data and, in iOS, usually takes the form of entities. An entity represents an object in the real world. For example, if you are creating an App that handles customer orders, you might have a Customer entity, Order entity, and Product entity. 

An entity has properties that represent the attributes of a particular real-world objects.

For example, a Customer entity might have companyName, webAddress, phone, and address properties that hold this information for each customer as shown in Figure 11.1.

Figure 11.1 A CustomerEntity object

You will learn much more about entities in the next chapter on Core Data.

View

The View is the part of the App the user interacts with directly and contains user-interface objects such as buttons, text fields, sliders, and so on. Because of this, Apple refers to each screen full of information in an App as a view.

Controller

The Controller acts as an intermediary between the Model and the View. The Controller is where your core logic goes. Figure 11.2 shows the interaction that occurs between the Model, View, and Controller.


Figure 11.2 Model, View, Controller interaction


In the MVC pattern, the user interacts with the view—they touch, tap, pinch, or flick a user-interface object. In response, the View passes a call to the Controller, and the Controller does something based on that interaction. For example, the user might change information in a text field, touch a Save button and the controller could take that new information and update a Model entity.


Conversely, sometimes when you save a Model entity, it gets new or default values. For example, if you save a new invoice entity, it may be assigned an invoice number. So, the model can fire an event that tells the controller, “I’ve got a new invoice number.” The controller can then update the view and the new invoice number can be displayed in the user interface.


As already mentioned, the Controller is the place where you should put your App’s core logic. For example, in a Calculator App you could have a Calculator controller object that adds, subtracts, multiplies and divides (Figure 11.3). If you have a Customer Service App, you could have a Customer controller object that creates new customers, puts customers on credit hold, and so on.



Figure 11.3 A Calculator controller object


Separation of Concerns


So, MVC is a great pattern for “separating concerns”—keeping the three parts of your App separate. You want to be able to use your data with any user interface. You want your core logic to be used in multiple places. You want your data and core logic to be independent of your user interface.


In the Model View Controller diagram shown in Figure 11.2, there are communication lines between the Model and the Controller, and between the Controller and the View, but you will never see communication between the Model and the View. They never communicate directly with each other. This allows your data and core logic to be used independently of the user interface.


Apple’s MVC Implementation


Design patterns are great. However, I have found that, at times, the implementation of a particular pattern may not be true to the pattern’s original intent. I was actually surprised at how Apple implemented the MVC pattern, because it wasn’t a traditional implementation—and this isn’t just a matter of semantics, or theory. Unfortunately, Apple’s implementation of MVC doesn’t provide well-defined boundaries between the three main parts of your App—which is the whole purpose of the MVC pattern.


Nothing speaks louder than a real example, so I have created a few sample Apps that follow Apple’s MVC pattern. By way of comparison, I have also recreated the Apps using a better implementation of the MVC pattern.


Calculator Sample App User Interface


The first sample is a Calculator App that looks and acts just like the built-in iOS Calculator App. Follow the steps in this section to get a look at how the user interface of the App has been designed.


The sample code for this post can be downloaded at the following link:


Sample Code


  1. In Xcode, open the CalculatorDemo project in this book’s sample code.

  2. In the Project Navigator, select the MainStoryboard file. You should see the scene shown in Figure 11.4.
  3. Figure 11.4 The CalculatorDemo project’s main scene

    This looks dangerously like the built-in Calculator App, and intentionally so. Let’s see what it looks like at run time. 

  4. Make sure the Scheme control at the top left of the Xcode window is set to iPhone Simulator 6.0, and then click the Run button. No surprises here. It looks and behaves just like the built-in App.

  5. Click any number, and then click an operator such as the minus sign. Notice a highlight appears around the operator, just as it does in the built-in Calculator App (Figure 11.5). This indicates the currently selected operation.
  6. Figure 11.5 The minus operator is highlighted.

  7. Click another number and the equal sign, and you can see the Calculator is fully functioning. You can use the memory keys, add, subtract, read, and clear the Calculator memory. You can even use common Calculator shortcuts. For example, if you type 1 + =, and then repeatedly click the = key, the Calculator continually adds one to the previous number.

  8. Next, hold down any of the number or operator keys and notice the shading changes slightly to indicate the button has been pressed.

Now let’s take a look behind the scenes to see how the Calculator App works. 


  1. Go back to Xcode and click the Stop button. Look in the Project Navigator, and you can see there are just a few files as shown in Figure 11.6.
  2. Figure 11.6 The CalculatorDemo project files

    The ViewController class is associated with the MainStoryboard file, and contains the user-interface logic and core logic for the calculator.

  3. Let’s take a closer look at the user interface. Click the MainStoryboard file again and look in the Document Outline window on the left side of the Interface Builder editor where you can see a list of all user-interface objects. There are five images (identified by the “img” suffix) and, as you might expect, there are quite a few button objects (Figure 11.7).

  4. Figure 11.7 UI objects in the Document Outline pane

  5. Select imgCalculatorMain in the Document Outline pane, and then go to the Attributes Inspector pane (click the third button from the right in the Inspector toolbar). Notice the Image attribute is set to CalculatorBackground.png (Figure 11.8)
  6. Figure 11.8 CalculatorBackground is the main image file in the Calculator App.

  7. To see what this image looks like when it’s not on the view, go to the Project Navigator, expand the Supporting Files group and select the CalculatorBackground.png file. This displays the image in the Interface Builder editor (Figure 11.9).
  8. Figure 11.9 The Calculator background image

    You may be surprised to see the buttons are not separate images. The calculator background is a single image that includes all the buttons you see at run time! How is the illusion of separate buttons created when you run the App? There is an invisible, rectangular button over each of the buttons.

  9. To see this, go back and select the MainStoryboard file in the Project Navigator. Click over the number 9 button. As you can see in Figure 11.10, you have selected an invisible button that is positioned directly over the button.

    Figure 11.10 There is an invisible button located above each button in the main App image.

    With the invisible button selected, go to the Attributes Inspector. Notice in the header at the top of the Inspector pane it indicates the object you have selected is a Button as shown in Figure 11.10. By default, this button is invisible, allowing the number button on the image below it to show through.

    In the Attributes Inspector, the State Config list box allows you to select different button states. Each of these options represents a state the button can be in at run time:

    • Default – Indicates the state of the button when the user is not interacting with it.
    • Highlighted – Indicates the state of the button when the user is holding the button down.
    • Selected – Indicates the state of the button when it is selected.
    • Disabled – Indicates the state of the button when it is disabled.

    When you select a state from the State Config combo box, it displays the Title, Image, Background, Font, Text Color and Shadow Color for the selected button state. This allows you to specify different visual effects for each button state.

    With the Default option selected (Figure 11.10), notice there is no Image specified. That’s why this button is transparent at run time in its normal state.

  10. In the State Config list box, select the Highlighted state. Notice the Image attribute is set to CalculatorButtons.png as shown in Figure 11.11.
  11. Figure 11.11 The Highlighted attributes of the Calculator buttons

  12. To see what this image looks like, go to the Project Navigator and under the Support Files group, select the CalculatorButtons.png file. The image will look like Figure 11.12. It’s a gradient that changes from black at the top to light grey at the bottom, which is the opposite of the gradient you see when the image isn’t pressed as in Figure 11.10.
  13. Figure 11.12 The CalculatorButtons.png file

    At run time when the user presses their finger on the invisible, it enters the Highlighted state, and this image becomes visible.

  14. Now go back to the Project Navigator and select the MainStoryboard file again. If it’s not selected, reselect the invisible button over the number 9 button. Next, scroll down in the Inspector window until you see the Alpha setting (Figure 11.13). 
  15. Figure 11.13 The Alpha attribute specifies the transparency of a UI object.

    This setting specifies the transparency of an object. The value of Alpha can range between zero and 1. To make the effect more subtle, the Alpha value of the button is set to approximately .2 as shown in Figure 11.13.

  16. Also, notice in Figure 11.13 the Tag property of the invisible 9 button is set to 9. Each of the numeric buttons have their tag number set to the number they represent (the 8 button is set to 8, and so on). The decimal point button ( . ) has its Tag property set to -1. You will see in the next section how these tag numbers are used.

  17. When the user touches a number button at run time, even though it’s invisible, it fires the same events as a regular button. 
  18. To see this, with the invisible number button still selected, go to the Connections Inspector by clicking the button on the far right of the Inspector toolbar. As shown in Figure 11.14, there is a connection from the button’s Touch Up Inside event to the numberTouched action method of the view controller. All number buttons (as well as the decimal point button) are connected to the numberTouched action method.

    Figure 11.14 Calculator buttons are all connected to the numberTouched action method.

  19. Now let’s take a look at the operator buttons. Go back to the scene in the Interface Builder editor, select the plus ( + ) button, and then look at the Connections Inspector again. As shown in Figure 11.15, the button’s Touch Up Inside event is connected to two action methods:

    • highlightOperation: – This method adds the highlight around an operation button when it’s selected. This connection only exists for the add, subtract, multiply, and divide buttons.

    • operationTouched: – This method performs the function associated with the selected key (add, subtract, multiply, divide, and so on).
  20. Figure 11.15 Operation button connections

  21. Next, click on the Calculator’s numeric display at the top of the view as shown in Figure 11.16. 

  22. Figure 11.16 The Calculator’s numeric display is a label.

  23. Go to the Attributes Inspector by clicking the fourth button from the left in the Inspector toolbar. As you can see in Figure 11.16, the Calculator’s numeric display is a label whose Alignment is set to right justified. By default, the iOS label control doesn’t have a margin setting, so to make it look like the label does have a left and right margin, the label does not extend the full width of the view. Its Background is set to transparent so the color in the view behind it shows through.

This should give you a basic understanding of how the user interface works in the Calculator sample App.


Calculator Sample App Core Logic


Now let’s take a closer look at the view controller code files to see how the core logic code in the view controller interacts with the Calculator user interface.


  1. In the Project Navigator, select the ViewController.h file. At the top of the header file is a declaration of an Operation enumeration. This enumeration details all operations that can be performed by the Calculator:
  2. typedef enum {
    OperationNone,{
    OperationAdd,{
    OperationSubtract,{
    OperationMultiply,{
    OperationDivide,{
    OperationEquals,{
    OperationClear,{
    OperationMemoryPlus,{
    OperationMemoryMinus,{
    OperationMemoryRead,{
    OperationMemoryClear,{
    OperationPositiveNegative{
    } Operation;

  3. Below the Operation enum declaration are three property declarations, two of which are Interface Builder outlets. Figure 11.17 shows how the outlet properties connect to the Calculator.

    Figure 11.17 Calculator outlet connections

    • lblTotal – An IBOutlet property that is connected to the label comprising the Calculator’s display.

    • operationHighlightImages – An IBOutlet collection property that is connected to the highlight images of the add, subtract, multiply, and divide buttons.

    • value – A regular property that holds the current string value displayed in the Calculator’s numeric display label.

  4. There are also three action-method declarations in the ViewController.h file. Figure 11.18 shows how these action methods connect to the user-interface controls.

    • operationTouched: is an action method connected to the Touch Up Inside event of all operation buttons.

    • highlightOperation: is an action method connected to the Touch Up Inside event of the add, subtract, multiply, and divide buttons. This method adds the highlight around the selected button, and removes it from all other buttons (because only one operation can be highlighted at a time).

    • numberTouched: is an action method connected to the Touch Up Inside event of all number buttons, including the decimal point button.
  5. Figure 11.18 Calculator action-method connections

    These properties and methods are all user-interface-specific since they are tied directly to UI controls. 

  6. As already discussed, you should keep the user interface separate from the core logic. To help make a division between the user-interface-specific methods and the Calculator core logic methods in the view controller, I have physically grouped them together. To see this, go to the Project Navigator and select the ViewController.m file. In the jump bar at the top of the Code Editor, click the section on the far right as shown in Figure 11.19.
  7. Figure 11.19 Click the section on the far right of the jump bar to bring up a list of class members.

    At the bottom of the popup list, you can see two sections labeled Calculator User Interface and Calculator Core Logic (Figure 11.20). Notice the three user-interface-specific methods shown in Figure 11.18 are listed under the Calculator User Interface section of the popup.

    Figure 11.20 View controller code sections

    These sections exist in the popup because of the #pragma mark directives in the ViewController.m file (for more information, see Chapter 10: The Code Editor) Given the current architecture in this project, creating separate sections in the view controller is about the best you can do to separate user-interface-specific methods from core logic.

  8. Figure 11.21 provides a high-level overview of the interaction between the numeric buttons of the Calculator in the user interface and the methods of the ViewController class.
  9. Figure 11.21 Calculator numeric-button code interaction

    1. The user touches a numeric button.

    2. The view controller’s numberTouched: action method is called, which contains user-interface-specific code.

    3. In the numberTouched: method, after executing the user-interface processing, the view controller’s processNumber: method is called.

    4. The processNumber: method executes the Calculator’s core logic for handling a new number, and returns the new Calculator value.

    5. In the numberTouched method, the return value from the processNumber: method is stored in the text property of the lblTotal label.

  10. Now let’s take a closer look at these methods. In the jump bar at the top of the Code Editor, click the section on the far right again. From the member popup list, under the Calculator User Interface section, select the numberTouched: method to view it in the Code Editor. 

  11. You should see the following code:

    – (IBAction)numberTouched:(UIButton *)sender {

    NSString *numberString;
    NSInteger tagNumber = [sender tag];
    if (tagNumber == -1) {
    numberString = @”.”;
    }
    else {
    numberString = [NSString
    stringWithFormat:@”%i”, tagNumber];
    }
    [self hideOperationHighlight];

    self.lblTotal.text =
    [self processNumber:numberString];
    }

    Remember, this is the method that is immediately called when the user touches a numeric button in the Calculator. Everything that happens in this method is user-interface specific:

    • When this method is called, the button passes a reference to itself in the sender parameter—buttons are user-interface objects.

    • At the top of the method, the tag number is retrieved from the selected button, and converted to a string. If the tag number is -1, the string is set to the decimal point. Again, buttons and their tag numbers are part of the user interface.

    • Afterwards, the hideOperationHighlight method is called which hides any operation key highlight that may be visible on the user interface.

    • Next, at the bottom of the method, a call is made to the processNumber: method.

  12. If you look at Figure 11.20, you can see the processNumber: method is listed under Calculator Core Logic. That means the method should contain no user-interface logic—just pure Calculator logic. To see this method, in the Code Editor jump bar, click on the section to the far right again and select the processNumber: method from the popup list. Here are the key actions this method performs:

    • If the last action was an operation, clear the current value.

    • Make sure the user hasn’t entered the maximum number of digits.

    • If it’s a decimal point, make sure there isn’t already a decimal point in the number.

    • Append the new digit or decimal point to the current value.

    • Format the current value to include commas (if any are needed).

    Each of these actions is definitely a part of the Calculator’s core logic, and is separate from any user-interface considerations.

  13. Now let’s take a quick look at how the user interface and view controller code interact when an operation is selected by the user as outlined in Figure 11.22.

    Figure 11.22 Operation button and code interaction

    1. The user taps an operation button.

    2. The view controller’s operationTouched: action method is called, which contains user-interface-specific code.

    3. In the operationTouched: method, after executing the user-interface processing, the view controller’s performOperation: method is called.

    4. The performOperation: method executes the Calculator’s core logic for performing an operation and return’s the Calculator’s new value.

    5. In the operationTouched: method, the return value from the performOperation: method is stored in the text property of the lbTotal label.

  14. Let’s take a closer look at these methods. In the jump bar at the top of the Code Editor, click the section on the far right again. From the member popup list, under the Calculator User Interface section, select the operationTouched: method to view it in the Code Editor. You should see the following code:

    - (IBAction)operationTouched:(UIButton *)
    sender {

    NSInteger tagNumber = [sender tag];

    self.lblTotal.text =
    [self performOperation:tagNumber];
    }

    Again, this is the method that is immediately called when the user touches an operation button in the Calculator, so everything that happens in this method is user-interface specific. 

    • When this method is called, the button passes a reference to itself in the sender parameter—buttons are user-interface objects 

    • At the top of the method, the tag number is retrieved from the selected button. Again, buttons and their tag numbers are part of the user interface.

    • Next, at the bottom of the method, a call is made to the performOperation: method.

  15. If you look at Figure 11.20, you can see the performOperation: method is listed under the Calculator Core Logic section. Again, that means the method should contain no user-interface logic—just pure Calculator logic.

    To see this method, in the Code Editor jump bar, click on the section to the far right again and select the performOperation: method from the popup list. Here are the key actions this method performs:

    • The display value is converted from a string to a double value.

    • If the operation is the type that should be performed immediately (such as All Clear, Positive/Negative, or any of the memory operations), that operation is performed.

    • For all other operations, a check is performed to see if there is any previous operation, and if so, that operation is performed.

    Again, each of these actions is definitely a part of the Calculator’s core logic, and is separate from any user-interface considerations.


What’s Wrong With This Architecture?



The Calculator App seems to work well. It adds, subtracts, multiplies, divides, and performs all other operations properly. So what’s the problem?


Check out the strong link between the Calculator user interface and the view controller as shown in Figure 11.23. In object-oriented programming terms, this is known as tight coupling.

Figure 11.23 Tight coupling between the Calculator UI and core logic

 


Per Apple’s developer documentation, this is typical. A view is typically bound to a single view controller. Ultimately, the view controller is a user-interface object. It’s not the tight coupling between the view and the view controller that’s the problem—that’s perfectly fine. The problem is the core logic code that’s in the view controller.



A good App is often a victim of its own success. Let’s say you release the Calculator App with its current architecture. If it does well in the App store, you may consider creating a Scientific Calculator App. Wouldn’t it be great to reuse some of the functionality of the Calculator App, since the Scientific Calculator does everything the regular Calculator does and more?


Unfortunately, because the Calculator’s core logic is buried inside the view controller, there isn’t a clean way to reuse this logic in another App. It’s “stuck in the weeds” of the user interface.


A Better MVC Implementation

So, how do you fix this problem? You need to put the Calculator’s core logic in some other place where you can access it from multiple Apps, or from multiple view controllers in a single App. If you shouldn’t put your core logic in a view controller, where should you put it? 

Part 2 of this post provides the answer to these questions and helps you build a solid architecture for your iOS Apps!

Kevin McNeish


Twitter: @kjmcneish


 


 

Tutorial: iOS 6 Auto Layout versus Springs and Struts – Part 3 of 3

This tutorial is an excerpt from our book series iOS App Development for Non-Programmers, available in the iBookStore for the iPad, and on Amazon.


Part 1 discusses the two layout options, then dives in on springs and struts. Part 2 discusses the new Auto Layout option available in iOS 6 and later. Part 3 takes tutorial on Auto Layout to the next level!


Setting Constraints on Multiple Controls


Now that you have a basic understanding of how to set constraints on a single control, it’s time to learn how to set constraints on multiple controls in relation to the parent view and each other.


If you haven’t already downloaded the sample code for part 1 and part 2 of this tutorial, click on the following link:


Tutorial Samples


  1. If it’s not already open, in Xcode, open the AutolayoutDemo project. 

  2. If it’s not already selected, go to the Project Navigator and select the MainStoryboard file.

  3. Drag an Image View from the Object Library and position it on the left side of the scene directly below the label. When the guide lines appear as shown in Figure 8.43, let go of the mouse button to drop the label in place.

    Figure 8.43 Drop an image view on the scene.

    This adds four constraints to the scene, which you can see in the scene itself as well as the Size Inspector as shown in Figure 8.44:

    • Width Equals: 240

    • Height Equals: 128

    • Top Space to Label – Oliver! Equals: Default

    • Leading Space to: Superview Equals: Default

  4. Figure 8.44 Four constraints are created for the image view.

    The width and height constraints are getting added because there is no default guide line against which the right and bottom sides of the image view can be placed. This is perfectly acceptable.

    The top and left sides of the image view are positioned against guide lines, so their spacing is set to Default.

  5. Let’s resize the image view so we have room to place a text view next to it. 
  6. Go to the Size Inspector (second button from the right in the Inspector toolbar) and change the Width to 150 and the Height to 150.

    When you do this, it changes the width and height constraints to the new values.

  7. Now let’s put an image in the image view. With the image view still selected, go to the Attributes Inspector (third button from the right in the Inspector toolbar) and set its Image attribute to OliverCast.png. This displays the image in the image view as shown in Figure 8.45.
  8. Figure 8.45 The image is set in the image view.

  9. Next, drag a Text View control from the Object Library and position it on the right side of the scene directly below the label. When the guide lines appear as shown in Figure 8.46, let go of the mouse button to drop the text view in place.

    Figure 8.46 Drop a text view on the scene.

    This adds the four constraints shown in Figure 8.47:

    • Height Equals: 128

    • Width Equals: 240

    • Trailing Space to: Superview Equals: Default

    • Top Space to: Label – Oliver! Equals: Default

    Figure 8.47 Four constraints are created for the text view.

  10. We can get rid of the hard-coded height constraint by aligning the bottom of the text view with the image view. 
  11. To do this, grab the resizing handle at the bottom of the text view and drag it downward. When the guide line appears (

    Figure 8.48 Align the bottom of the text view with the bottom of the image view.

    Doing this removes the hard-coded height constraint and replaces it with a new Align Bottom to: Image View – OliverCast.png constraint.

  12. Let’s align the left edge of the text view right up against the right side of the image view. To do this, grab the resizing handle on the left side of the text view and drag it to the right (Figure 8.49). When the width of the text view is 130.0 points as shown in the width/height popup, then release the mouse.
  13. Figure 8.49 Adjust the width of the text view.

  14. Now let’s put some meaningful text in the text view by hooking it up to an outlet. To do this, first go to the Xcode toolbar at the top of the screen and click the center button in the Editor button group. This should automatically display the ViewController.h file in the Assistant Editor.
  15. Next, hold the Control key down, click the text view and drag down to the tvwTest outlet in the ViewController.h file. When the Connect Outlet popup appears (Figure 8.50), release the mouse key to create the connection to the outlet.

    Figure 8.50 Connect the text view to the tvwTest outlet.

  16. Now let’s see how these controls look at run time. Click Xcode’s Run button, and when the App appears in the Simulator, it looks just as it does at design time (except there is descriptive text in the text view).

  17. Go to the Simulator menu and select Hardware > Rotate Left. As shown in Figure 8.51, the image view is pinned to the left side of the view and the text view is pinned to the right.
  18. Figure 8.51 The text view & image view in landscape mode


This is exactly what is specified by the constraints, but not exactly what we want. There is a lot of empty space in the middle of the view that should be filled by increasing the width of the image view and text view. Let’s fix that now.


  1. Go back to Xcode and click the Stop button.

  2. To turn off the Assistant Editor, go to the Xcode toolbar at the top of the window and click the left button in the Editor button group.

  3. Click on the image view in the design surface to select it, then hold the Shift key down and click on the text view to select it too.

  4. At the bottom of the Interface Builder editor are several buttons that provide quick access to commonly used features. The three button group highlighted in Figure 8.52 provides access to Auto Layout options.
  5. Figure 8.52 Interface Builder Auto Layout buttons

    Click the center button in this button group. As you can see in Figure 8.53, this provides a variety of Pin options.

    Figure 8.53 Create a Horizontal Spacing constraint.

    Select Horizontal Spacing to pin the spacing between the image view and text view to its current value of zero points. This creates a new Horizontal Space constraint that you can see in the Document Outline pane (Figure 8.54), although oddly enough, it doesn’t appear in the Size Inspector.

    Figure 8.54 The new Horizontal Space constraint

  6. Let’s run the App to see how it looks at run time. Click Xcode’s Run button, and when the App appears in the Simulator, select Hardware > Rotate Left from the Simulator menu. As you can see in Figure 8.55, there is zero space between the image view and the text view!

  7. Figure 8.55 Zero space between the image view and text view.


In just a bit, you will learn how to evenly divide the available space between the image view and text view, but for now, we’re going to fix a problem with conflicting constraints.


Resolving Constraint Conflicts


Did you notice that when you rotated the App in the Simulator, the warnings shown in Figure 8.56 were displayed in the Console?

Figure 8.56 Conflicting constraints warning

Even though the final result of the resizing and positioning of controls was correct, the Auto Layout system found conflicts in the constraints.

The message at the top of Figure 8.56 says: “Unable to simultaneously satisfy constraints.” The next paragraph states “Probably at least one of the constraints in the following list is one you don’t want.” Further down, it lists the group of constraints in which there is a conflict, and near the bottom it tells you which constraint it broke to try and recover from the conflict. 

This is a great feature of Auto Layout. Even though there are conflicting constraints, it does its best to come up with a best guess for a solution by breaking one of the conflicting constraints.

Looking through the list of constraints should give you a pretty good idea where the conflict lies. The constraints are displayed in the syntax of Apple’s Visual Format Language. Here is some of the basic syntax of this language (for more information, check out the topic Visual Format Language in the Apple documentation):

  • A control is represented between square brackets.

  • A connection between controls and views is represented by a single hyphen, or two hyphens with a number in between representing the space between the objects in points.

  • The superview or parent view is represented by a single vertical line.

For example, look at the second half of the first constraint:

H:[UITextView:0x79f3000(130)]

  • The H indicates this constraint applies to a horizontal orientation.

  • The UITextView between square brackets indicates this constraint applies to a text view.

  • The (130) indicates the value associated with the constraint.

From this information you can derive that this is the text view’s width constraint. As you look through the other constraints, it’s pretty easy to see the problem. The constraints dictate that:

  • The text view must be 130 points wide

  • The image view must be 150 points wide

  • There must be 20 points between the left side of the view and the left side of the image view

  • There must be zero space between the image view and text view

  • There must be 20 points between the right side of the text view and the right side of the view.

When the device is in portrait orientation, all of these constraints can be satisfied. However, when the device is rotated to landscape orientation and the view becomes wider, not all of these constraints can be satisfied. For that reason, the Auto Layout system decided to break the image view’s width constraint:

Will attempt to recover by breaking constraint 

<NSLayoutConstraint:0x892e8f0 H:[UIImageView:0x892e790(150)]>

Even if you agree with the decision made by the Auto Layout system, you should still fix the conflict so your constraints are simplified and efficient. Let’s do that now.

  1. In Xcode, click the Stop button to stop the App from running in the Simulator.

  2. Select the image view in the design surface.

  3. Go to the Size Inspector and locate the Width Equals: 150 constraint.

  4. Click the small down arrow on the right side of the constraint and select Select and Edit… from the popup menu. This displays the Attributes Inspector.

  5. Change the Relation attribute to Greater Than or Equal (Figure 8.57). This indicates the image view can be greater than or equal to 150 points. Now the constraints can be satisfied in both portrait and landscape modes.
  6. Figure 8.57 Set the Relation to Greater Than or Equal.

  7. Let’s see how this work at run time. Click Xcode’s Run button, and when the App appears in the Simulator, select Hardware > Rotate Left from the menu. You should no longer see any constraint errors in the Console!

Let’s look at another example that will provide another opportunity to resolve a constraint conflict. In this example, you will make the image view and text view equal in width.

  1. Select the image view in the design surface, and then go to the Size Inspector (second button from the right in the Inspector toolbar).

  2. Change the image view’s Width attribute to 140. When you do this, the image view’s width constraint changes from a user constraint back to an automatic constraint of Width Equals: 140 (Figure 8.58).
  3. Figure 8.58 The image view’s width constraint

  4. Select the text view in the design surface. Grab the resizing handle on the left side of the text view and move it left until the text view’s width is 140 points (Figure 8.59).
  5. Figure 8.59 Resize the text view’s width to 140 points.

  6. When you resized the image view, the horizontal space constraint between the image view and text view was removed by Interface Builder. To put it back, select the text view in the design surface, hold down the Shift key, and then click the image view to select it. Rather than using the Auto Layout buttons at the bottom-right corner of the Interface Builder editor, go to Xcode’s menu and select Editor > Pin > Horizontal Spacing (it performs the exact same function, but I just wanted to show you how to select it from the menu). 

  7. When you create a constraint between two controls as you just did, they become unselected in the design surface, so let’s select both of them again. Click the image view to select it, then hold down the Shift key and click the text view to select it. 
  8. In the Xcode menu, select Editor > Pin > Widths Equally. When you do this, a new equal widths constraint is added to the view (Figure 8.60). Notice there is an equal sign inside a circle under the image view and the text view representing the equal width constraint.

    Figure 8.60 The new equal widths constraint.

  9. Let’s run the App to see how it looks. Click Xcode’s Run button. When the App appears in the Simulator, the image view and text view will be sized and positioned just as they are at design time.

  10. In the Simulator menu, select Hardware > Rotate Left, and the image view and text view should be equal sizes (Figure 8.61).
  11. Figure 8.61 The text view and image view are equal widths.

This works exactly as it should, but you may have noticed there were constraint conflict errors displayed in the Console again. Let’s take a closer look to see how these can be fixed.

Figure 8.62 shows the constraint debugging information displayed in the Console.

Figure 8.62 The new set of constraint conflicts

  • There must be 20 points between the left side of the view and the left side of the image view.

  • The image view and text view are the same width.

  • There must be 20 points between the right side of the view and the right side of the text view.

  • There must be zero space between the image view and the text view.

  • The text view must be 140 points wide.

Can you spot the problem? Ultimately, this is very similar to the constraint conflicts you saw earlier. The text view is constrained to be 140 points and the image view is constrained to be the same size as the text view. This is fine when in portrait mode, but it’s not possible in landscape mode.

When you fixed the problem last time, you changed the width constraint from Equal to Greater Than or Equal. This time we’re going to do something different.

  1. Go back to Xcode and click the Stop button.

  2. In the Document Outline pane, expand the Image View’s and Text View’s Constraints nodes. Notice each control has a Width constraint (Figure 8.63) that is blue, indicating it is a user control. The great thing about user controls, is they can be deleted (automatic constraints created by Interface Builder cannot be deleted).
  3. Figure 8.63 The Width constraints

  4. First of all, since there is a constraint that says the image view is the same width as the text view, there’s no need for the image view to have a width constraint. So, click on the Image View’s Width constraint in the Document Outline pane and press the Delete key to delete it.

  5. You may think that the text view still needs its width constraint, but because of the presence of the Equal Widths constraint, it doesn’t! 

  6. Let’s see how this works at run time. Click Xcode’s Run button, and when the App appears in the Simulator, select Hardware > Rotate Left from the menu. As you can see, the image view and text view take up an equal amount of space in the view, and there are no constraint warnings in the Console!
  7. Click on the Text View’s Width constraint in the Document Outline pane and then press Delete to delete it.

  8. Let’s see how this works at run time. Click Xcode’s Run button, and when the App appears in the Simulator, select Hardware > Rotate Left from the menu. As you can see, the image view and text view take up an equal amount of space in the view, and there are no constraint warnings in the Console!

As you lay out your own user interfaces in Xcode using Auto Layout, I recommend looking very carefully at the constraints that have been added to your scenes and remove those that are not necessary.

Kevin McNeish
Author: iOS App Development for Non-Programmers book series 
Twitter: @kjmcneish
 

Tutorial: iOS 6 Auto Layout versus Springs and Struts – Part 2 of 3

This tutorial is an excerpt from our book series iOS App Development for Non-Programmers, available in the iBookStore for the iPad, and on Amazon.


Part 1 discusses the two layout options, then dives in on springs and struts. Part 2 discusses the new Auto Layout option available in iOS 6 and later. Part 3 takes tutorial on Auto Layout to the next level!


Auto Layout


Auto Layout is a descriptive layout system that supports more complex user-interface layouts. As you will learn when we cover creating multi-lingual applications later in this series, it also makes internationalization much easier. Auto Layout is a complete replacement for springs and struts, but remember that it can only be used in Apps running on iOS 6 or later!


Understanding Constraints


With Auto Layout, you create descriptions known as constraints that specify how you want your controls positioned and resized. You can specify the relative size and position of a control with respect to the view that contains it or with respect to other controls in the view. You can also use constraints to specify geometric properties of a control such as height and width. From these constraints, Auto Layout calculates the appropriate position and size of your controls at run time when the view first loads and when the size of the view changes, such as when the user changes the device orientation.

Let’s dive in with an example so you can see first hand how this works. To get the sample code for this tutorial, click on the following link:

Tutorial Samples

  1. In Xcode, open the AutolayoutDemo project in this tutorial’s sample code.

  2. In the Project Navigator, select the MainStoryboard file and then go to the File Inspector (click the first button on the left in the Inspector toolbar). Note that the Use Autolayout option is selected.

  3. Drag a Round Rect Button from the Object Library and position it at the bottom of the main scene in the storyboard. When you see the horizontal and vertical guide lines as shown in Figure 8.14, let go of the mouse button to drop the button on the scene.

  4. Figure 8.14 Position the button at the bottom of the scene until you see horizontal and vertical guide lines.

    Notice the long vertical line in the center of the scene, as well as the small “I” bar between the bottom of the button and the bottom of the scene (Figure 8.15). These are constraints that Interface Builder automatically added to the view.

    Figure 8.15 When you select a UI control, its constraints are displayed in the design surface.

    If you select a constraint, you can see more details about it. Although you can click on a constraint directly in the design surface to select it, it’s usually easier to go to the Size Inspector where constraints for the currently selected control are also shown.

  5. Go to the Size Inspector (the second button from the right in the Inspector toolbar) and you will see two constraints at the bottom of the panel (Figure 8.16).

    • Align Center X to: Superview - This constraint specifies that the button should be vertically aligned to the center of the superview (the view that contains the button). 

    • Bottom Space to: Superview Equals: Default - This constraint specifies that the space between the bottom of the button and the superview is the default space recommended for UI objects.
  6. Figure 8.16 The Size Inspector displays constraints for the object currently selected in the design surface.

    These specific constraints were added by Interface Builder because you dropped the button on the view when the center-alignment and bottom-alignment guidelines appeared.


  1. Hover your mouse pointer over the Align Center constraint in the Size Inspector. The associated constraint is highlighted in the design surface as shown in Figure 8.17. This provides visual confirmation that you are looking at the correct constraint.
  2. Figure 8.17 Hover over a constraint in the Size Inspector to see the corresponding constraint in the design surface.

  3. Go to the Document Outline pane and you can see constraints are also listed there (Figure 8.18). Selecting a constraint in the Document Outline pane highlights the corresponding constraint and associated control(s) in the design surface.
  4. Figure 8.18 Constraints in the Document Outline pane

  5. Now let’s see how the button looks at run time. In Xcode, click the Run button. When the App appears in the Simulator, you will see the button is centered at the bottom of the view (Figure 8.19).
  6. Figure 8.19 The button centered at the bottom of the view

  7. Go to the Simulator menu and select Hardware > Rotate Left. Notice the button is still centered at the bottom of the view (Figure 8.20).

When you center a button at the bottom of a view using springs and struts, it hard-codes the position of the button to that location. In order to keep the button centered when the device orientation changes, you have to explicitly specify this by selecting the appropriate struts in the Size Inspector.


With Auto Layout, all you had to do was place the button in the bottom center of the view and it positioned itself automatically. This is much easier!
Now let’s take a closer look at the constraints that are automatically created for you by Interface Builder.


  1. Go back to Xcode and click the Stop button to stop the App from running in the Simulator.

  2. In the Document Outline pane, select the Vertical Space constraint. This highlights the button’s vertical constraint at the bottom of the view (the “I“ bar located below the button) as shown in Figure 8.21.
  3. Figure 8.21 The button’s vertical constraint is highlighted.

  4. Next, go to the Attributes Inspector (click the third button from the right in the Inspector toolbar). You will see the constraint attributes that are shown in Figure 8.22.

    Figure 8.22 Vertical-space constraint attributes

    • Relation – The three values for this attribute are:
      • Less Than or Equal
      • Equal
      • Greater Than or Equal
    • In the context of the vertical-space constraint, this indicates if the vertical space is less than or equal to, equal to, or greater than or equal to the specified amount of space.

    • Constant - This attribute allows you to specify the amount of vertical space in points.

    • Standard - When this option is selected, it indicates the “standard” amount of space is specified between the associated control and the parent view or another control. The standard space is indicated in the design surface by the appearance of guide lines as you position a control in the design surface.

    • Priority - Specifies the priority of this constraint in relation to other constraints. This value can be anywhere between 1 and 1000, where 1000 indicates the constraint is required. Anything less than 1000 indicates the constraint is optional. At run time, Auto Layout compares conflicting constraints. The constraint with the higher priority is applied, and the constraint with the lower priority is not applied.
  5. The constraint shown in Figure 8.22 could be read as “Require the vertical space between the bottom of the button and the bottom of the view to be the standard spacing for iOS Apps.”


When you change the size or position of a control at design time, Interface Builder deletes old constraints and adds new ones based on your changes. Let’s take a look at some other constraints that are created automatically.


  1. In the design surface, drag the button to the bottom-left corner of the view until the horizontal and vertical guide lines appear (Figure 8.23) and then release the mouse button.
  2. Figure 8.23 The button in the bottom left corner of the view

    This deletes the Center X Alignment constraint and creates a Horizontal Space constraint, which can be seen in the Document Outline pane (Figure 8.24).

    Figure 8.24 The Horizontal Space constraint

  3. The Size Inspector provides more meaningful information about a constraint, so go to the Size Inspector and you will see the constraints shown in Figure 8.25.
  4. Figure 8.25 The Leading Space constraint

    The constraint is more generically called Horizontal Space in the Document Outline pane, and more specifically called a Leading Space constraint in the Size Inspector. 

    In English, leading space indicates the space between the left side of the view and the left side of the UI control. In right-to-left languages such as Hebrew and Arabic, it indicates the space between the right side of the view and the right side of the control.

  5. To see the leading-space constraint’s attributes, click the down arrow on the right side of the constraint and select Select and Edit… from the popup menu (Figure 8.26).
  6. Figure 8.26 View the leading-space constraint attributes.

  7. This unselects the button, selects the constraint in the design surface, and displays the attributes shown in Figure 8.27 in the Attributes Inspector.

    Figure 8.27 The constraint’s Direction attribute

    Notice the Direction attribute, which lets you specify the direction of the horizontal space. The choices are:

    • Leading to Trailing - In English, leading is left and trailing is right. In right-to-left languages, leading is right and trailing is left. 

    • Left to Right - If you don’t want the horizontal space to change when the language changes, you can specify you always want it to be left to right.

  8. Go back to the design surface, drag the button to the bottom-right corner of the view until the horizontal and vertical guide lines appear (Figure 8.28) and then release the mouse button.
  9. Figure 8.28 Position the button in the bottom-right corner.

  10. Go to the Size Inspector and you can see that positioning the button in this location deletes the leading space constraint and creates a Trailing Space constraint shown at the top of Figure 8.29.
  11. Figure 8.29 The Trailing Space constraint

    The trailing-space constraint has the same attributes as the leading-space constraint, so we don’t need to go to the Attributes Inspector to check them out.

  12. Let’s check out one more constraint. In the design surface, drag the button to the top center of the view until the horizontal and vertical guide lines appear (Figure 8.30), and then let go of your mouse button.
  13. Figure 8.30 Position the button at the top center

  14. Go to the Size Inspector and you can see that positioning the button in this location deletes the trailing space constraint and adds a new Top Space constraint shown at the top of Figure 8.31.
  15. Figure 8.31 The new Top Space constraint

    The top-space constraint has the same attributes as the bottom-space constraint, so we don’t need to go to the Attributes Inspector.

  16. We’re finished looking at the different button constraints, so in the design surface, drag the button back down to the bottom center of the view until the horizontal and vertical guide lines appear, and then let go of your mouse button. This should restore the Bottom Space and Align Center X constraints shown in Figure 8.16.

Creating User Constraints


The constraints that are created automatically for you by Interface Builder are only a best guess, so sometimes you need to add your own constraints or modify an existing constraint to get the exact resizing and repositioning you need for the controls in your App. 


When you manually add a new constraint or modify an existing constraint, it creates something called a user constraint. Let’s give it a try.


  1. If it’s not already open, in Xcode, open the AutolayoutDemo project.

  2. Select the button at the bottom of the scene, and then go to the Attributes Inspector. Change the Title attribute to Done and press Enter. This adds a new Width constraint, which you can see in the design surface below the button (Figure 8.32).
  3. Figure 8.32 The Width constraint

    You can also see the constraint in the Document Outline pane listed under the button (Figure 8.33). Constraints that are specific to a single control are listed directly under the control.

    Figure 8.33 Width constraint in the Document Outline pane

  4. Select the Width constraint in the Document Outline and then go to the Attributes Inspector. You will see the attributes shown in Figure 8.34.
  5. Figure 8.34 The width constraint attributes

    Notice there is no Standard option as there was with the spacing constraints, just a Constant setting that specifies the width of the object in points. This indicates the button will always be 73 points wide.

    That might be OK if the text of the button always stays the same, but if the text of the button changes at run time (as it does if your App supports multiple languages), this hard-coded setting won’t work well.

  6. To see what I mean, let’s connect the button to an outlet that’s already been created for you. To do this, first display the Assistant Editor (in Xcode’s toolbar, click the center button in the Editor button set). This should display the ViewController.h file in the Assistant Editor (if it doesn’t, select the file in the Assistant Editor’s jump bar).

  7. Next, hold the Control key down, click on the button and drag down to the btnTest outlet in the ViewController.h file. When the Connect Outlet popup appears (Figure 8.35), let go of the mouse to connect the button to the outlet.
  8. Figure 8.35 Connect the button to the btnTest outlet.

  9. Now go to the Project Navigator and select the ViewController.m file to see the following code in the viewDidLoad method that stores a longer text string to the Done button’s Title:
  10.  [self.btnTest setTitle:@"Completed!" 
    	forState:UIControlStateNormal];
  11. Click Xcode’s Run button, and when the App appears in the Simulator, you can see the title of the button is clipped as shown in Figure 8.36.
  12. Figure 8.36 The button title is clipped.

    That’s because the constraint told the button that it could only be 73 points wide—not wide enough to display the complete title.

There are a few ways you can fix this problem. To start, let’s modify the constraint to allow the button to increase in width as its content changes.


  1. Go back to Xcode and click the Stop button. 

  2. Turn off the Assistant Editor by going to the Xcode toolbar and clicking the left button in the Editor button group.

  3. In the Project Navigator, select the MainStoryboard file, then go to the Document Outline pane and select the Width constraint as shown in Figure 8.33.

  4. Go to the Attributes Inspector and change the Relation attribute to Greater Than or Equal as shown in Figure 8.34.
  5. Figure 8.37 Set the Relation to Greater Than or Equal.

    Changing this default constraint created by Interface Builder changes the automatic constraint to a user constraint. Notice it’s color has changed from purple to blue in the Document Outline pane (Figure 8.38). In addition, the “I” constraint bar in the view is bolded. These are great visual cues so you can easily identify which user constraints you have changed or manually added.

    Figure 8.38 Width is now a user constraint.

  6. Let’s see how this works at run time. Click Xcode’s Run button, and when the App appears in the Simulator, you should see the full title in the button as shown in Figure 8.39. That’s because you let the button decide how big it should be. The button has an intrinsic size that is determined by its content. Usually, it’s best to let a button decide its size rather than hard-coding a constraint that sets its size to a hard-coded value. 
  7. Figure 8.39 The button title is no longer clipped!

    Usually, it’s best to let a button decide its size rather than hard-coding a constraint that sets its size to a hard-coded value. 

    In this set of steps, I wanted to show you how to change the Relation attribute of a constraint to allow it to expand to its intrinsic size. Ultimately, there is even a better way to do this.

  8. Go back to Xcode and click the Stop button. 

  9. Select the Done button in the design surface, go to the Xcode menu, and then select Editor > Size to Fit Content. Notice when you do this that the Width constraint completely disappears. As it turns out, it was a constraint that was automatically added to the view by Interface Builder that was completely unnecessary. You will learn how to avoid this in the next section!

  10. Click the Run button again and when the App appears in the Simulator, notice the content is not clipped!

Avoiding Unnecessary Constraints


In the previous section, you saw that, at times, Interface Builder adds unnecessary constraints to your view. Since you want to keep your views as simple as possible, you should only have the constraints you absolutely need. In the next set of steps you will add a label to the view and learn how to avoid some of these unnecessary constraints.


  1. If it’s not already open, in Xcode, open the AutolayoutDemo project.

  2. If it’s not already selected, go to the Project Navigator and select the Mainstoryboard file.

  3. Drag a Label from the Object Library and position in at the top center of the scene. When you see the guide lines appear (Figure 8.40), release the mouse button to drop the label in place.

  4.  

    Figure 8.40 Drop a label in the top center of the scene.

    Just as you would expect, this adds Center X Alignment and Vertical Space constraints for the label (Figure 8.41). If you look closely, you can see that the label is center aligned to the button rather than the view (the text Label – Label – Button – Done indicates the Label named Label is aligned to the Button named Done. This is standard behavior when you are centering multiple controls in the same view.


    Figure 8.41 The new label constraints

  5. With the label still selected, go to the Attributes Inspector (third button from the right in the Inspector toolbar), and set the label’s Text to Oliver! 
  6. Notice this adds a width constraint as shown in the design surface (Figure 8.42) and clips the label text.

    Figure 8.42 The label’s width constraint

    As you learned in the previous section, width constraints created in this way are usually not desirable. I’ll show you an easy way to get rid of them.

  7. Double-click the label in the design surface and press Enter. This removes the width constraint! 

The lesson to take away from this is that you should set a label’s text by double-clicking in the design surface rather than setting it in the Attributes Inspector. Note that this trick works with other controls that have a Text attribute such as buttons and segmented controls.


In Part 3 of this tutorial, we’ll take the lessons on Auto Layout even further!


Kevin McNeish
Author: iOS App Development for Non-Programmers book series 
Twitter: @kjmcneish
 

Tutorial: iOS 6 Auto Layout versus Springs and Struts – Part 1 of 3

This tutorial is an excerpt from our book series iOS App Development for Non-Programmers, available in the iBookStore for the iPad, and on Amazon.


Part 1 discusses the two layout options, then dives in on springs and struts. Part 2 discusses the new Auto Layout option available in iOS 6 and later. Part 3 takes tutorial on Auto Layout to the next level!


User Interface Layout Options


Starting in iOS 6, there are two main ways you can specify the size and position of UI controls in your App so that all objects reposition and resize properly when the user rotates the iOS device, or when designing a UI that will run on both the iPhone and iPad:


  1. Springs and Struts
  2. Auto Layout

This tutorial details how and when to use older “springs and struts” technology versus the new Auto Layout.


A storyboard’s Use Autolayout setting (found in the File Inspector as shown in Figure 8.1) determines which user-interface layout option you are using. 



Figure 8.1 The Autolayout setting


If this check box is unselected (it is for storyboards created using Xcode 4.4 or older), the storyboard uses springs and struts for laying out the user interface. If it’s selected (it is for storyboards created using Xcode 4.5 or newer), the storyboard uses the new Auto Layout approach.


Springs and Struts



The classic springs and struts approach to laying out your user interface works well for simple user interfaces. However, for more complex user interfaces, you need to write code to size and position controls when using springs and struts.


So, why use springs and struts at all? The primary reason is to allow your App to be installed on devices that are running an older version of iOS (prior to iOS 6). 



Auto Layout



In contrast with the hard-coded nature of springs and struts, the new Auto Layout feature is a descriptive layout system. It supports far more complex user-interface layouts, so you rarely need to write code to ensure your user-interface positions and resizes properly as the size of the parent view changes. 


Unfortunately, Auto Layout can only be used in iOS 6 or later. So, if you want to make you App available to users who are using an older version of iOS, you need to stick with the springs and struts layout.


This is the reason we will still cover the older springs and struts technology. Let’s dive in!


Working with Springs and Struts



When the Autolayout setting is unselected in a storyboard, the Size Inspector looks like Figure 8.2.



Figure 8.2 The Size Inspector with Autolayout turned off



The upper portion of the Size Inspector allows you to specify the size and location of the UI object that is currently selected in the design surface. The X coordinate specifies the number of points the selected object is positioned from the left side of the view. The Y coordinate specifies the number of points from the top of the view. The Width and Height properties specify the size of the UI object.


Often, you want the size and position of a user-interface object to change when its parent view’s size (height and width) changes. The view size can change when:


  1. The user rotates the iOS device.

  2. The App runs on different iOS devices such as the iPhone 3.5-inch, iPhone 4-inch, iPad, the iPad mini, and whatever new device size Apple produces in the future.

The Autosizing section at the bottom left corner of the Size Inspector allows you to specify if and how you want a user-interface objects’ size and position to change. The outer square in the Autosizing section represents the four sides of the view: top, bottom, left, and right. The inner square represents the currently selected UI object.


You use the red “I” bars, known as “struts,” between the two squares to indicate how you want the selected object to be positioned as the size of the view changes. The struts represent anchors. 


If a strut is dark red, it indicates the control is anchored to the corresponding side of the view. So, in Figure 8.2, the selected object is anchored to the top and left sides of the view. This means that, regardless of how the width or height of the view changes, the object is anchored to the top and left side of the view at the position specified in the X and Y settings. 


There are two red arrows, known as “springs,” inside the inner square that indicate how you want the selected object to resize. The horizontal spring indicates width and the vertical spring represents height. 


If you want the selected object’s width to stretch with the width of the view, select the horizontal spring. If you want the selected object’s height to stretch with the height of the view, select the vertical spring. In Figure 8.3, both springs are selected, so the object’s height and width stretches with the height and width of the view. 



Figure 8.3 Both springs are selected.


On the right side of the Autosizing section is an Example image (Figure 8.4) that shows the relative location and size of the selected object, shown in red, positioned on a white rectangle that represents the view. 




Figure 8.4 Autosizing Example on the right


If you hover your mouse pointer anywhere in the Autosizing section, the Example image animates, first expanding the width and height of the view and then contracting. This allows you to see how the position and size of the selected object changes as the size of the view changes. 


At the very bottom of the Size Inspector is an Arrange list box, which allows you to position or size the selected object easily by using these options:


  • Center Horizontally in Container – Centers the object horizontally in the view.

  • Center Vertically in Container – Centers the object vertically in the view.

  • Fill Container Horizontally – Changes the width of the object to the full width of the view.

  • Fill Container Vertically – Changes the height of the object to the full height of the view.

The other options in this list box are only available if you have multiple objects selected in the design surface. They allow you to align the objects horizontally or vertically within the containing view.


Springs and Struts in Action


Follow the steps in this section so you can see springs and struts in action. To get the sample code for this tutorial, click on the following link:


Tutorial Samples


  1. I In Xcode, open the SpringStrutsDemo project in this book’s sample code.

  2. In the Project Navigator, select the MainStoryboard file. You should see the scene displayed in Figure 8.5.
  3. Figure 8.5 The SpringStrutsDemo scene

    At the top of the scene is a label displaying the name of the play, Oliver! Beneath the label is an image view that displays a picture from the play, and on the right of the image view is a text view describing the play. At the bottom of the screen is a Done button, which doesn’t do anything at this point but will be used to demonstrate how autosizing and positioning works with springs and struts.

  4. Select the Oliver! label at the top of view. Next, make sure that the Size Inspector is selected (click the second button from the right in the Inspectors toolbar). Notice that the label is anchored to the top and left sides of the view and no resizing is selected for the label as shown in Figure 8.4. In fact, all of the user-interface objects on this view have the exact same setting.

  5. To see how this view looks at run time, press Xcode’s Run button and when the App appears in the Simulator, it will look like Figure 8.6This is as expected. The view at run time looks just like the view at design time.
  6. Figure 8.6 The SpringStrutsDemo App at run time

  7. To see how the view looks when the view size changes, let’s rotate the iOS Simulator. To do this, go to the iOS Simulator menu and select Hardware > Rotate Right. The view now looks like Figure 8.7.
  8. Figure 8.7 The SpringStrutsDemo App rotated right

    As advertised, all of the UI controls maintain their X and Y position coordinates because the autosize settings anchor them to their position relative to the top and left sides of the view. 

    Not only does this look bad, but the Done button at the bottom of the view is not visible because its Y coordinate places it off the screen. This is obviously not what you want. Let’s change the position settings to more appropriate values.

  9. To do this, go back to Xcode and press the Stop button. Next, select the label at the top of the view. In the Size Inspector’s Autosizing setting, click the left strut to remove the anchor on the left side of the view (Figure 8.8). 
  10. Figure 8.8 Label anchored to the top of the view

    As you can see by the Example animation on the right, since there are no left or right anchors, the label is centered and anchored to the top of the view as the view is resized.

  11. Now let’s take a look at the image view. You don’t want the image view to be repositioned when the view size changes. It is currently anchored to the top and left of the view, and that’s a good setting, so we’ll leave it as is.

  12. Select the text view on the right side of the scene. It’s best to pin this control to the top and right side of the view. To do this, click the left strut to remove the anchor on the left side of the view and then click the right strut to anchor the control to the right side of the view, as shown in Figure 8.9.
  13. Figure 8.9 The text view is anchored to the top and right sides of the view.

  14. Next, select the Done button at the bottom of the scene. You want the button to be anchored to the bottom of the view, but centered between the left and right sides. To do this, click the top struts to remove these anchors, and then click the bottom strut to anchor the button to the bottom of the view as shown in Figure 8.10.
  15. Figure 8.10 The button is anchored to the bottom of the view.

  16. To see how these settings work at run time, click Xcode’s Run button to run the App in the iOS Simulator. Then, in the iOS Simulator menu, select Hardware > Rotate Right. The App now has the UI controls positioned properly for both portrait and landscape modes (Figure 8.11).
  17. Figure 8.11 The UI controls are placed in much better positions.


As it stands right now, the controls are positioned properly, but we can make improvements by changing the size of the controls. 


Currently, all of the controls maintain the same size when moving from portrait to landscape mode. This doesn’t take full advantage of the additional space available. Notice the large white space between the image view and the text view. Also, the width of the label at the top of the scene doesn’t change when moving to landscape, so it isn’t taking full advantage of the extra space that may be needed for longer play titles.


Follow these steps to automatically resize the controls when moving between portrait and landscape modes:


  1. If the App is still running in the Simulator, go back to Xcode, and press the Stop button.

  2. Select the label at the top of the scene. Go to the Size Inspector and select the horizontal spring in the middle of the Autosizing control as shown in Figure 8.12. This indicates the width of the label automatically resizes with the width of the view.
  3. Figure 8.12 The label is set to autosize its width.

  4. Next, select the image view in the design surface and then, in the Size Inspector, click the horizontal spring to indicate that its width should also automatically resize with the width of the view.

  5. Select the text view on the right side of the scene and click the horizontal spring to indicate that its width should change with the width of the view.

  6. You don’t want the button to be resized, so don’t choose any resizing options for it.

  7. To see how the view looks at run time, press the Run button to run the App in the Simulator. Notice in Figure 8.13 that the image view and text view stretch proportionally to take advantage of the extra width of the screen in landscape mode.
  8. Figure 8.13 The image view and text view adjust widths in landscape mode.


This should give you a basic idea of how to use springs and struts to lay out your user interface. As already mentioned, the very nature of expressing layout by means of springs and struts makes this technology very limited in providing all the power you need to lay out your user interface. Ultimately, you often need to write code to position and resize your controls to get your controls to be sized and positioned correctly. In fact, every real App I have ever written required me to write code to fill in the blanks where springs and struts couldn’t do the job.


In Part 2 of this tutorial, you will learn how to use the new iOS 6 Auto Layout to manage the resizing and repositioning of your App’s user interface.


Kevin McNeish
Author: iOS App Development for Non-Programmers book series 
Twitter: @kjmcneish

Amazon Opens Kindle KDP Market in Canada and Brazil

This morning I was happy to see Amazon has opened up the KDP Market and is now allowing us to sell the Kindle version of my books in Canada and Brazil. KDP stands for Kindle Direct Publishing, and it allows authors to self-publish books for sale in the Kindle store.


The Amazon Brazil Store–A New Model
The Amazon Brazil store is big news. This new store is digital only–a new model for Amazon! The store sells Kindles and eBooks, and there is no physical presence in the country. It will be interesting to see if this works well, and in how many other countries Amazon will roll out this new model.


Number of English Speakers
If you are thinking about promoting an English book in these new stores, you should know that Portuguese is the official language of Brazil and is spoken by 99% of the population. Even though only 5% of Brazilians speak English, since Brazil is the fifth most populous country in the world, that still makes 9.5  million English speakers in Brazil. In contrast, approximately 85% of the Canadian population speak English (no surprise there), making approximately 33 million English speakers in Canada.


Comparing Amazon to Apple
In October, Apple opened its iBookstore to South American countries including: Bolivia, Brazil, Chile, Columbia, Ecuador, Paraguay, and Peru. Hopefully Amazon will open stores in these countries too.


It’s always gratifying to see your book in a new bookstore. Here’s a screen shot of the first book in our iOS Apps For Non-Programmers series in the Amazon Canada store:



Kevin McNeish
Author: iOS Apps For Non-Programmers book series
Twitter: @kjmcneish