Tab Bar Controllers in Storyboards

[Updated to Swift 2.0/iOS9 9/21/15 SJL]
While Navigation controllers often have the limelight when it comes to Xcode controllers, Tab Bar controllers are great for independent tasks in the same app or for different ways of working with the same model. In this lesson, we’ll take a look at tab bar controllers and how to add them in the storyboard.  For more on implementing them completely in code see Swift Swift: Using Tab Bar Controllers in Swift. For more on data sharing once set up in the storyboard, see Passing Data in Tab Bar controllers

Creating from the Template

The easiest way to make a tab bar controller is through Xcode’s template. In Xcode, press Command-Shift-N or File>New>Project. Select the tabbed application.
This will give you a window with two tabs installed with two labels each and a tab item on the bottom of each controller. The template tends to make things more complicated instead of less, so it is rarely used.

Creating From A Single View

The most common way to make a tab bar controller is to start from a single view and embed it in a controller. We’ll try an example of this one in Xcode. Press Command-Shift-N and select Single View. Make a file named SwiftTabBarEmbed, using Swift as the language and with a Universal device. Once created, go over to the Storyboard. Select the view controller by clicking the view controller icon or title, and select Editor>Embed in>Tab Bar Controller.

Screenshot 2015-01-27 05.38.51

This will turn the single view controller into the first view controller of the tab bar.

Screenshot 2015-01-27 05.40.04

Add another view controller by dragging out a view controller then control-dragging from the tab bar controller to the new controller. In the popup, select under Relationship Segue the view controllers option.

Screenshot 2015-01-27 06.05.53

A segue appears on the story board. You’ll see a second tab in the tab bar controller.

Screenshot 2015-01-27 06.08.46

Adding More Tabs with System Icons

To configure the tab bar icon, go to the Item icon in the View Controller (not on the tab view controller) we just made and click it. This will bring up in the properties inspector the Tab Bar Item and Bar Item properties. In the Tab Bar Item properties is the System Item drop down. This is where we configure the tab bar button.

Screenshot 2015-01-27 07.08.34

In the System Item menu, Click the drop-down for System Item. you will see a  list of system icons.

Screenshot 2015-01-27 06.24.23

Select Favorites. The Icon changes both on the view controller and the tab bar controller.

Screenshot 2015-01-27 06.29.10
Click the other view controller’s tab bar, and change the system Icon to Recents.
Drag out six more view controllers, so we have a total of eight controllers. Control-drag from the tab bar controller to each new view controller and assign Contacts ,Bookmarks ,Search, Downloads ,and Most Viewed to the controllers, leaving one as a custom item. The tab bar controller should look like this:

Screenshot 2015-01-27 06.41.22

Using More

Set the simulator to an iPhone 6. Build and run. While your icons may be in a  different order, you will get something like this for the tab bar at the bottom of the phone:

Screenshot 2015-01-27 06.43.18

We get an extra tab we did not set up titled More. Tap on the More icon.

Screenshot 2015-01-27 06.47.05

Compact widths cannot handle more than five tabs. If you add more than five tabs, it places a More tab as the fifth tab and makes this table view for you. If you tap the tab in the table’s cell, the tab’s controller will appear.

Without any more code, tab bar controllers let users customize their tab bars. Tab the Edit button, and you get a configuration view:

Screenshot 2015-01-27 06.53.13

Drag one of the icons down to the tab bar. It replaces one tab. You can also drag tabs in the tab bar to change the order. Try getting the tab bar to look like in the above screenshot, then tap Done. Your tab bar now looks like this:

Screenshot 2015-01-27 07.01.21

One of the system icons you can use is a More icon. Be careful when using the More icon in a tab bar. It can cause user confusion between the More tab and your own use for More.

Close the simulator. In the storyboard, delete three view controllers so you get a tab bar like this:

Screenshot 2015-01-27 07.05.10

Since we now have five icons, this will keep off the More icon.

Custom Tab Bar Items

Click on the tab bar for the one custom controller.We can see all the tab bar properties.

Screenshot 2015-01-27 07.08.34

The lower half has bar item properties, which directly control the title and icon for the tab. We can easily change the text. Change the Bar Item Title to Square.

Screenshot 2015-01-27 12.36.47

The title changes on the tab bar:

Screenshot 2015-01-27 12.37.13

If we change a System items’s title, it becomes a custom item, and the icon disappears. Click on the Contacts Icon. Change the title to Pizza. Do the same to change Downloads to Pie. All become squares.

Icon Images

Under Title in the properties inspector,  we have a drop down for Image. For a custom bar item we can supply the icon. Image icons are not like other images. They are monochrome images,  unlike anything else you have ever seen. They are set not by color but by the alpha value , or transparency of the image. You can make any image here any color, but it will show up as a solid tint color if you select an alpha value of 100% and background color if you select 0% alpha. For example I created the following icons in both high and low resolutions:

gradient bar Pizza and Pie Bat items pizza bar icon pizza bar icon@2x pie bar icon pie bar icon@2

Icons are 30 points by 30 points, which means for retina images make the icon a 60 pixel by 60 pixel image and for low resolution images 30 pixel by 30 pixel. You can download them individually by right clicking the images above, and saving them to a folder.  Use these file names:

Screenshot 2015-01-27 07.37.51

You can also download a.zip file Of these here:Tab Bar Assets

For those not familiar with Apple image naming conventions, low resolution images have a file name. Retina Images have @2x appended to that file name. This tells Xcode to know what resolution it is. We have both 60×60 pixel retina and 30×30 pixel standard images. By loading both into Xcode, the system will use the correct one automatically for the device.
In Xcode’s navigator panel, open up images.xassets. Select both gradient bar icon files. Drag them into the center panel in Xcode, which will highlight, and the number 2 will appear next to your cursor. Release the mouse button, and you will see a new collection in the image assets.

Screenshot 2015-01-27 07.47.43

Do the same for the pie bar and pizza bar files Select the pizza bar.png and pizza bar@2x.png files and drag them to the assets manager. Do the same for the pie bar.png and pie bar@2x.png buttons.
Return to the storyboard. On the tab we titled Square,click anywhere on the tab bar.  Select the image drop-down in the tab bar properties and select the gradient bar icon.

Screenshot 2015-01-27 07.56.11

Find the Pizza and Pie tab views on the storyboard, and change them the same way. The Tab bar controller now has three custom icons.

Screenshot 2015-01-27 08.00.29

Build and run with the iPhone 6 simulator, and you will see our three icons, with one highlighted as selected.

Using View Controllers in Tab Bar Controllers

Tab Bar controllers make a set of single view controllers running parallel to each other. The tab bar switches the view, but the view we leave does not get dismissed, just hidden. there are two uses for such a controller. The first is a series of small applications related to each other. Apple’s clock app is an exmaple. The timer, alarma clock, world clock, and stopwatch all are related by a theme of time. All run independently of each other. The second use of Tab bar controllers is to use a shrared data set in different ways. Social media apps like Twitter or Instagram for example use tab bars to have different functions of displaying posts, searches, or displaying notifications that all use the same database. For this second type of use, see see Passing Data in Tab Bar controllers. We’ll look at the first type here.

Basic View Controllers

You already know how to hook up a view controller for a tab bar. First make a new view controller class by pressing Command-N. Make a new Cocoa Touch Class view controller named SearchViewController. Change the class code to this.

class SearchViewController: UIViewController {

    var count = 0
    
    @IBOutlet weak var myLabel: UILabel!
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        myLabel.text = String(format:"Count %i",count++)
    }
}

I did not use viewDidLoad here. In tab bar controllers, ViewDidLoad only fires once, when we first load the view controller. The method viewWillAppear fires every time the view appears, and so we use that to do any updating. This code assumes that this view controller will remain active even when it is not visible.

Go to the storyboard and select the Search view controller on your storyboard. In the identity inspector set the class to SearchViewController. On the view in the storyboard drag a label to the upper let and size it up a bit.

2015-09-21_08-07-00

Open up the assistant editor. Drag from the circle next to the outlet to the label to connect it. Build and run. Select the search tab, then another tab a few times. You will see the count increas every time you return to the search tab.

2015-09-21_08-19-57

Embedding Navigation Controllers in Tab Bar Controllers

You can also embed navigation controllers into tab controllers. Stop the simulator and go back to the storyboard. Close the Assistant editor if it is open. Select the Pizza View Controller’s icon on the storyboard. In the drop down menu, select Editor>Embed in>View Controller

2015-09-21_08-23-16

The view controller changes into a Navigation controller with the tab bar icon.

2015-09-21_08-25-05

Attached to the navigation controller is a view controller. Double click the center of the navigation controller bar. Change the title to Pizza!!. Drag a bar button item to the upper left of the controller on the storyboard. Title it More Pizza.

2015-09-21_08-25-33

Add another view controller, below the one we just configured. Control-Drag from the bar button item to the new view controller. Set up a show segue. In the new view controller, Drag a Navigation item to the navigation bar. Change the item’s title to More Pizza!!!

2015-09-21_08-28-11

Build and run. Select the Pizza tab and we have our navigation controller.

Tap the More Pizza Button. The More Pizza!!! View Appears. Now go to another tab and then back to the Pizza tab. We remain on the More Pizza!!! view.

You can use navigation controllers inside a tab bar controller to our heart’s content. However, tab bar controllers can only be used once in a project, as the top level controller. So embedding more tab bar controllers does not work.

This is most of waht you need to know to work with basic tab bar controllers. For more information on sharing between the controllers and a more extensive coding example, see Passing Data in Tab Bar controllers.

Swift Swift Tutorials: Calculator Style buttons in an Auto Layout Xib

frame 2015-01-21 17.18.00Some people want calculator-type buttons. It would be nice to have such buttons for say, an order entry app. In this lesson we’ll put together a set of buttons to make a layout for an order entry system. Along the way I’ll show how to use a Xib as a modal view in Swift. This requires some knowledge of auto layout. If you haven’t used Auto layout before I’d suggest reviewing Beginning Auto Layout first.

Set Up the Starting View

Start by making a new project with Command-Shift-N in Xcode. Make a single view project and name it PieOrderEntry, make the device Universal. Choose Swift for the language. Set your storyboard up like this with one label and one button.

Screenshot 2015-01-22 14.32.50

Click the label. In the properties inspector center align the text. Then using auto layout, pin the label 50 up, 30 left and 30 right. Update the frame with new constraints.

Screenshot 2015-01-22 14.34.59

Then pin the Order Pie button 30 up, 30 left and 30 right, again updating the frames.

Screenshot 2015-01-22 14.35.45

Go into assistant editor, select Automatic, and click the view controller in the storyboard. Control-drag the label to the view controller. Name the outlet statusLabel . Control-drag the button and make it a target action named myModalButton . Remove the rest of the methods in the view controller class so your view controller looks like this:

class ViewController: UIViewController {
    @IBOutlet weak var statusLabel: UILabel!
//MARK: - Target actions
    @IBAction func myModalButton(sender: UIButton) {
    }
  //MARK: - Delegate methods
}

Make a View Controller With a Xib

There is only one check box between using storyboard and a xib. Press Command-N to get a new file, and under iOS click a new Cocoa Touch Class . Click Next and fill out the dialog box like this with a new subclass of UIViewController named MyModalVC:

Screenshot 2015-01-22 14.43.08

Be sure to check on the Also create XIB file selection. This will create and associate a xib file with this view controller. If you have never seen a xib, it looks a lot like a storyboard with only one scene. You use it exactly the same way — even auto layout. The difference is the Xib is independent of the storyboard. You can take the MyModalVC.swift and MyModalVC.xib file from here, and import them into another project ready for use.

Layout for the Xib

After making the view controller, the xib should appear.

Once loaded, switch to the Storyboard. Add one label and seven buttons. In the properties inspector, make the label text read What is my pie? and center align the text. Position it towards the top of the view.  On one button, label it Submit Result with a red background and white text. Drag the Submit Result button towards the bottom. Make the rest of the buttons a blue color with white lettering. Title the buttons Pizza,Chicken Pot,Shepherd’s,Apple,Chocolate,and Blueberry. Arrange the buttons roughly like this:

Screenshot 2015-01-21 05.12.07

We will use auto layout pins to do most of the work. We need to get the bottom and top constrained first to always be the nearest neighbor.
Select the label. In the properties inspector,change the background to white. Pin the label 50 from the top and 0 from left and right. Update the frame.
Change the view’s background to black. It will help us see everything moving around.

Screenshot 2015-01-21 05.38.31

For the button titled Submit Result, Pin it 20 from the bottom and 0 from the left and right. Update the frame.

We now can start working on the buttons. Like Chapter 3, we will start with one button, and pin everything around it together. Because everything will get messy we will not update frames until we are done with this process.

Close the left hand navigator frame and open the assistant editor to the preview mode. The default iPhone 4 inch will do fine for checking our work as we go. Your workspace should look like this:

Screenshot 2015-01-21 05.47.02

We will use a 20 point gutter between buttons. Select the Pizza button in the upper left. Pin the button 20 up, 0 left, 20 down, and 20 right.
Select the Apple button. Since we already have a relationship to the Pizza button, we don’t need a top pin. Pin the Apple button 0 left, 20 right, and 20 down.
Select the Chocolate button. Pin the button down 20 and right 20.
Select the Blueberry button. Pin the button down 20 and right 0.
Select the Shepherd’s button. Pin the button up 20, left 20, right 0, and down 20
Select the Chicken Pot button. Pin up 20 and down 20.
You now have all the buttons pinned in all four directions. Clicking on each button and make sure you see four constraints, which most likely will be misplaced like this.

Screenshot 2015-01-21 06.02.17

If you have a constraint missing, add it from the pin menu. The Preview has the buttons in the right place but sized incorrectly:

Screenshot 2015-01-21 06.06.58

We have not set any size information. Select the Pizza Button. Control-Drag from the Pizza Button to the Label. Select Equal Heights.
Control drag from the Pizza Button to the Submit Result button. Select Equal Heights.
Control-Drag from the Pizza button to the Apple Button. Shift Select Equal Width and Equal Heights and press return. Repeat for the other four blue buttons, always dragging from Pizza to the Chocolate, Blueberry,Meat and Chicken Pot.

In the resolver, for All Views in View, select Update Frames.

Screenshot 2015-01-21 06.16.39

The label and Submit Result button is a bit too big. Select the label. Go to the Size inspector and select Edit for the Equal Height to Pizza. Change the multiplier to 2:3.

Screenshot 2015-01-21 06.21.00

Select the Submit Result Button and change the multiplier to 1:2

Screenshot 2015-01-21 06.23.26

Again for all views in view, select Update Frames. We have a completed layout that looks pretty good.

Screenshot 2015-01-21 06.28.38

Resolving Six Warnings

Unfortunately, we get six warnings. Going to the error panel you will find them:

Screenshot 2015-01-21 06.41.24

Reading the error we note that each button is off by one. Annoyingly you can resolve all but the pizza. If your resolve the pizza misplacement, all the errors come back.
All of the errors are height errors. This is a rounding error, and its cause is the multiplier for the label which is 2:3. As a repeating decimal, it is causing rounding errors between all the views. One easiest answer is to change the multiplier from 2:3 to 1:2. The error goes away.
In the preview, both Landscape and Portrait work:

Screenshot 2015-01-21 06.52.39  Screenshot 2015-01-21 06.52.51

Connect the Labels and Buttons

Set your workspace up for wiring up the views. Open the assistant editor if not already open. Select Automatic for the assistant editor. Hide the Properties or Size Inspector to give yourself some room.

In the assistant editor, remove the methods already in the class, including the commented out prepareForsegue . Control drag the What is my Pie Label to make an @IBOutlet named pieLabel . Control drag the Submit Result button and make an @IBAction for a UIButton named submitResult . Control drag the Pizza button and make an action for a UIButton named pieSelectionButton . Your class should look like this:

class MyModalVC: UIViewController {
  @IBOutlet weak var pieLabel: UILabel!

  @IBAction func pieSelectionButton(sender: UIButton) {
  }

  @IBAction func submitResult(sender: UIButton) {
  }
}

From the circle to the left of the pieSelectionButton method, click then drag to the other five blue pie buttons and release to connect them to the action. When connected, they will highlight as you hover over the circle.
Add the following property above the @IBOutlet:

var pieString = "No Pie"

Fill in our two methods so the entire class looks like this.

import UIKit
class MyModalVC: UIViewController {
   var pieString = "No Pie"
   @IBOutlet weak var pieLabel: UILabel!

    @IBAction func pieSelectionButton(sender: UIButton) {
        pieString = sender.titleLabel!.text!						//7
        pieLabel.text = pieString + " Pie"							//8
    }

    @IBAction func submitResult(sender: UIButton) {
        dismissViewControllerAnimated(true, completion: nil)		//12
    }
}

Line 7 takes the tile and places it in a string property pieString. Note the titleLabel property is an optional UILabel and the text property of UILabel is also optional. We unwrap them before concatenating to the string” Pie” in line 8. Line 12 dismisses the modal view, though we will change this shortly.
Open up the navigator. and close the assistant editor. Go back to the viewController.swift file. Just under the class declaration, add the following:

let pieVC = MyModalVC(nibName: "MyModalVC", bundle: nil)

There is an initializer for UIVIewController called init(nibName:bundle:) . This line creates an instance of the view controller MyModalVC that uses the xib specified as a string for nibName: We can specify a bundle location if necessary. If we leave the bundle nil , the compiler will search for it in our code bundle.
We need to set a transition and present the modal view controller. Change the code for myModalButton() to the following

  @IBAction func myModalButton(sender: UIButton) {
    pieVC.modalTransitionStyle = UIModalTransitionStyle.PartialCurl
    presentViewController(pieVC, animated: true, completion: nil)
  }

In line 2, We set the transition style to .PartialCurl to simulate a waiter’s order pad. We then present the view controller in line 3. This is enough code to run the app. Build and Run.

      frame 2015-01-21 17.18.00      frame 2015-01-21 17.18.30

Add a Delegate to the Modal View

The modal view currently does not give the root view controller any data back. For that, we will need a delegate. Delegates in modal views are a bit simpler than in navigation controllers. We still start with a protocol. Go to the MyModalVC.swift code and add this above the class declaration:

protocol MyModalDelegate{
  func myModalDidFinish(controller:MyModalVC, pie:String)
}

In the MyModalVC class add a delegate property:

var delegate:MyModalDelegate! = nil
Change the method submitResult() to use the delegate:
@IBAction func submitResult(sender: UIButton) {
    //dismissViewControllerAnimated(true, completion: nil)
    delegate.myModalDidFinish(self, pie: pieString)
}

Now head over to ViewController.swift to adopt the protocol:

class ViewController: UIViewController,MyModalDelegate {

Right on cue, Xcode complains we have not implemented the delegate. Add the following code to the ViewController class:

//MARK: - Delegate methods
func myModalDidFinish(controller: MyModalVC, pie: String) {
    statusLabel.text = “Order ” + pie + " pie"
    controller.dismissViewControllerAnimated(true, completion: nil)
}

We put our result in the label, then dismiss the modal view from the delegate method. Now assign the delegate to this view controller by changing myModalButton :

@IBAction func myModalButton(sender: UIButton) {
    pieVC.delegate = self
    pieVC.modalTransitionStyle = UIModalTransitionStyle.PartialCurl
    presentViewController(pieVC, animated: true, completion: nil)
}

While storyboard-based views need to place the delegate declaration in a prepareForSegue method, here we directly send it to the view controller. We now should have a working delegate. Build and run

frame 2015-01-21 17.19.00      frame 2015-01-21 17.19.30

And it works!

The Whole Code

My ModalVC.swift

//
//  MyModalVC.swift
//
//
//  Created by Steven Lipton on 1/20/15.
//  Copyright (c) 2015 MakeAppPie.Com. All rights reserved.
//

import UIKit
protocol MyModalDelegate{
    func myModalDidFinish(controller:MyModalVC, pie:String)
}
class MyModalVC: UIViewController {
    var pieString = "No Pie"
    var delegate:MyModalDelegate! = nil
    @IBOutlet weak var pieLabel: UILabel!

    @IBAction func pieSelectionButton(sender: UIButton) {
        pieString = sender.titleLabel!.text!						//7
        pieLabel.text = pieString + " Pie"							//8
    }
    @IBAction func submitResult(sender: UIButton) {
        //dismissViewControllerAnimated(true, completion: nil)		//12
        delegate.myModalDidFinish(self, pie: pieString)
    }

}

ViewController.swift

//
//  ViewController.swift
//
//
//  Created by Steven Lipton on 1/20/15.
//  Copyright (c) 2015 MakeAppPie.Com. All rights reserved.
//

import UIKit

class ViewController: UIViewController,MyModalDelegate {
    let pieVC = MyModalVC(nibName: "MyModalVC", bundle: nil)
    @IBOutlet weak var statusLabel: UILabel!

    @IBAction func myModalButton(sender: UIButton) {
        pieVC.delegate = self
        pieVC.modalTransitionStyle = UIModalTransitionStyle.PartialCurl
        presentViewController(pieVC, animated: true, completion: nil)
    }

    //MARK - Delegate Methods
   func myModalDidFinish(controller: MyModalVC, pie: String) {
        statusLabel.text = "Order " + pie + " pie"
        controller.dismissViewControllerAnimated(true, completion: nil)
   }
}

Solving Conflicting Constraints in Auto Layout

Screenshot 2015-01-13 07.39.25

In the Auto Layout and Size Class video series, we came up with a generic layout. We made a different layout for iPhones in landscape and then another for iPads. However the preview in iPhone 6 plus in landscape is not working.

Screenshot 2015-01-12 10.22.48

In the last video we solved the problem quickly. In this post I’d like to go into more detail about conflicting constraints. Our goal will be to have a landscape view like an iPad and a portrait like an iPhone.

Screenshot 2015-01-13 07.36.51

This lesson assumes you have built a few class sizes first. You can  follow along with the videos. For those who wish to follow along and not do all that work, I have a Zip file you can download here.

The iPhone 6 plus is an odd device. It acts like a iPhone in portrait and an iPad in landscape. iPhone 6 has a compact width in portrait like a phone and a regular width in landscape like a tablet.
In the videos, we have built sets of constraints for wAny hAny, wAny hCompact and wRegular hAny. The iPhone 6 plus landscape is wRegular,hCompact. The phone inherits constraints from both wAny hCompact and wRegular hAny. These constraints between the two sets are not the same, creating conflicting constraints. This  makes most of our UI disappear,  since Xcode can’t figure out what to do
If you do not already have the project open, open the project. Change the size class of the storyboard to regular width, compact height to get the class size storyboard for the iPhone 6 plus landscape:

Screenshot 2015-01-13 05.52.56

The screen is full of errors:

Screenshot 2015-01-13 05.55.16

Select the text view button from the outline and the size inspector shows all the constraints:

Screenshot 2015-01-13 06.00.45

We have a lot of duplicate entries. Select the Pepperoni button from the outline and you will see many constraints, so many they don’t fit on the screen. Open the error pane by clicking the small red circle on the document outline and you will see all those problems.

Screenshot 2015-01-13 06.06.15

There are several strategies to deal with this mess. The most important one is to plan ahead. One simple change would have prevented this. I could have done the iPhone landscape in wCompact,hCompact. It would only apply to the smaller phones in landscape and ignore the iPhone 6 plus. The wRegular,hAny would have set the landscape for the iPhone 6 plus to the same as the iPad.
However, no one can plan ahead for everything. Someone in your organization might add a new feature to one layout that messes up everything. You might change your mind on a UI feature halfway through implementation. It happens. We need to know how to fix conflicting constraints.

There are two ways to solve constraint errors: Delete all the constraints of a view then start over or delete only the conflicting constraints.

Deleting Conflicting Constraints

For our example, we want the regular width constraints to remain and the compact width constraints to disappear, leaving us with the appearance we get on the iPad.
You can delete constraints in the error panel, but I find it does not give me enough information about what I am deleting. Go back to the document outline and select the Cheese button. In the size inspector, you will find these constraints among others:

Screenshot 2015-01-13 06.22.51

The Equal Widths and Equal Heights have duplicates. Duplicates are not conflicting, but they are messy.   Click on the left side of one of the Equal Width duplicates and press Delete on your keyboard. Do the same for the Equal Height duplicate. That’s the easy stuff. Above these you will find the serious conflicts.

Screenshot 2015-01-13 06.26.13

Refer to your previous layout to know what to delete. For example we have two leading space constraints: Leading Space to Superview and Leading Space to Pepperoni Equals 10. We want the regular width,which was 10 points from Pepperoni. Delete the Leading Space to Superview. Similarly we want to align to the top of Pepperoni. We have a choice between Align Top to Pepperoni and Top Space to Pepperoni Equals 10 points. Delete the unwanted Top Space to Pepperoni Equals 10 points. When done, it should look like this:

Screenshot 2015-01-13 06.32.26

Deleting Conflicting Constraints: The Pepperoni Button

This won’t change anything because the Cheese button is dependent for its size information on the Pepperoni button, which is so messed up it has no size information. Fix that next. Select Pepperoni in the document outline. You get that long list of constraints. Start with the easy duplicates. You will find this in the size inspector:

Screenshot 2015-01-13 06.36.49

Delete one of these duplicates. Going through the list another conflict is the width:

Screenshot 2015-01-13 06.39.44

On the regular width version of the layout, we have a proportionally sized button. Delete the Equal Width to Done. With that change, we start to see views in the preview,though not sized right:

Screenshot 2015-01-13 06.44.04

There is only one more conflict. Does the Pepperoni button’s trailing space go to the Cheese button or the text view?

Screenshot 2015-01-13 06.46.01

These are the in-line buttons, so it goes to Cheese. Delete Trailing Space to: Text View Equals 10.

The Done Button

Click on the black background of the superview. In the resolver, click the Update Frames in the All Views in View controller section. We get our views back, though not quite right yet. Click the done button and you can see it still has conflicts:

Screenshot 2015-01-13 07.03.18

The Done button is to blame for the buttons stretching down the view. In the iPhone landscape it was anchored to the bottom of the superview. Delete this constraint:
Update all frames and you get buttons again, though not quite what we wanted:

Clearing all Constraints of a View

Often, it is just easier to clear all constraints for a view and start over.
Shift-select the Done button and the text view. In the resolver select the Clear Constraints in the Selected Views part. Resize the text view like this:

Screenshot 2015-01-13 07.13.45
By resizing, the text view has a nearest neighbor on top of the buttons, and the left,right and bottom margins on the other three sides. We can pin the text view in one step. Pin the text view like this:

Screenshot 2015-01-13 07.14.13

Add some constraints to the Done button. Control-drag from Done to Pepperoni. Shift-click Top, Equal Widths and Equal Heights, then press Return.
Pin the Done button like this to put constraint to Cheese and the Right Margin.:

Screenshot 2015-01-13 07.20.44

With the Done button selected, edit the Equal Width to Pepperoni by changing the multiplier to 1:3. If the Done button gets bigger than the other buttons, make the multiplier 3.

Screenshot 2015-01-13 07.26.08

Everything is set up correctly. On the resolve menu, click Update Frames in the All Frames section.

Screenshot 2015-01-13 07.33.43

Going over to the preview pane, we see the iPhone 6 plus is now set up correctly.

Screenshot 2015-01-13 07.36.51

We’ve now completed the size class and auto layout of this app’s storyboard by getting rid of all the errors.

Some Tips for Class Sizes and Layout

It best to avoid errors. Some planning and knowledge helps a lot to prevent errors. Here’s a few tips to help you using auto layout with Size classes:

  • Plan Ahead with Mockups: Have on paper exactly what you want for all the devices you will lay out for.
  • Avoid Any Width, Compact Height: For most layout you will want what we did in this chapter. The iphone 6 plus will act like an iPhone in portrait and an ipad in landscape. TO avoid the errors we corrected in this section use wCompact hCompact for the iPhone landscape layout.
  • Think iPad for wAnyhAny, get specific for iPhone: For the generic case start with your layout for iPad. Like above, it avoids conflicts. Make a different wCompact hRegular for iPhone Portrait.

The Auto Layout With Size Classes Series

Basic Auto Layout: A Practical View for Beginners

Note: When I originally posted the Basic Auto Layout video on YouTube, I recorded it live and did not make a script I could post underneath it. This post makes the same UI as the video, but with a few differences in depth of material and process to illustrate how to handle autolayout errors and a few other auto layout subtleties. I will produce a video on this probably in early February. This will be part of the Swift UI book I am writing, but may also become part of my second book on UI design in iOS.

Why Auto Layout?

For most of Xcode’s existence, storyboards were separate for iPads and iPhones. Even among devices, there was a lot of extra work necessary to use the same app in landscape and portrait. With iOS 8 and new phone sizes, this has become a bit more of a problem. We could design a different storyboard for every size and orientation, but that would be an excessive amount of work. Every new version of the phone or tablet would need to be different. If you hear about device fragmentation this is what people are talking about: different devices need different layouts because their screens are different sizes. It’s not just mobile devices that have this problem. Any window for a web page might be a different size. The web page needs to change to handle those differences.
Over that last few years, there are many solutions to adaptive user interfaces, which are interfaces that adapt their size and shape automatically to the device or window they happen to be in. Apple’s solution to this problem is auto layout, which lets the system do the hard work. Using relations between views we describe how to layout the user interface.
In iOS 8, Apple introduced class sizes, a way to describe any device in any orientation. While this removes device fragmentation, class sizes rely heavily on auto layout. Up to iOS 8 you could escape auto layout. Apple changed several methods for user interfaces and view controllers, most notably presentViewController, in a way that you need auto layout to use them effectively. In this chapter, we’ll discuss basic auto layout and size classes so it will make sense when we need to use them.

 

Pins and Aligns

If you take a look at Apple’s auto layout documentation, they go on and on about linear equations. This is very confusing and while explaining how it works, it does not get to the functional meat of how to use it. Let’s look at this differently.

We call the relations between views constraints. Basically there are two types of constraints for a view. The first is the size and space between views, usually neighbor views. These we call pins. The second is between two specific views by an attribute of the view. These we call alignment or aligns. Besides aligns and pins, you will need to update and resolve changes to the constraints with resolvers. At the bottom of the storyboard, you will find a few buttons that make your auto layout changes.

buttons for autolayout

Pins

For a pin, you select one object and describe how far way it is from its nearest neighbors. All pin operations are found on the pin button. If you were to select a view and open it, you would see this:

Screenshot 2015-01-05 05.35.10

The top section sets spacing to the nearest neighbor. If there is no neighbor view, auto layout assumes the superview. On the superview while dragging around a subview you will find blue dotted lines, which are layout guides for the margins. Auto layout will usually base its decisions on these layout guides if your nearest neighbor is the superview.
Below the spacing section in the menu, the sections below that let you set a size. You can set a size specifically for the view using the Width and Height selections. For a size based on the sizes of a neighbor, you can use Equal Widths, Equal Heights and Aspect ratio.
At the very bottom is a drop down  selection allowing you to update the view’s frames to match the new constraints. There are time you want to do this and times you do not.

Aligns

Similar to the pin selections is the alignment selections, which you can reach with the align button:

Screenshot 2015-01-05 05.34.40

To use these, you need to select two views, and then set a relationship between them. Note here we align to a common edge or center. The last two selections Horizontal Center in Container and Vertical Center in Container need only one selection, since they will center to the superview. Here too we have a selection to update the frames.

The Resolver

If we do not update the frames immediately, we can update them in the resolver.

Screenshot 2015-01-05 05.36.32

While the selections are the same for each half of the resolver, what they affect is very different. The top applies only to selected views, the bottom to all views. Try to avoid using the bottom half, unless you are sure you have all your constraints in place. Views tend to disappear if you use this and you don’t have all your views set. The only one in the bottom half used often is Clear Constraints, which is the best way to handle out-of-control problems with constraints.

Once you get a constraint set up correctly, a selected view will have blue i-beams describing the constraints.

Screenshot 2015-01-05 06.56.39

Constraint Errors

Once you start working with constraints, you will get a lot of warnings. All errors for constraints start as a warning, though they may become fatal errors at run time. You will need to resolve all these errors. There are three types of errors: misplaced views, missing constraints and conflicting constraints.

Misplaced Views

Misplaced views have the correct constraints but are not in the place they will display at run time. If you select the misplaced view, it will show how far off from the actual location the view is. The storyboard will also show a dotted rectangle giving the correct position. For example this label is a misplaced view.

Screenshot 2015-01-05 06.46.29

To resolve a misplaced view you select the misplaced view, and in the selected view section click Update Frames in the resolver. You can avoid this step by using the Update Frames selection in the pin and align popovers. You may not want to do this immediately since there is another kind of error to get rid of first.

Missing Constraints

A second kind of error is missing constraints. All views using constraints need enough information to determine location and size of a view. In most cases, auto layout can guess at a size if you give enough position information. For example this button is missing constraints:

Screenshot 2015-01-05 06.43.54

This button has a constraint to the label so it has a horizontal position, though misplaced. It needs a vertical position too. To fix these errors, add some more constraints. For the button, you might align to the top of the label, or pin to the top margin.

Conflicting Constraints

The third type of error is the most difficult: too many constraints. For a button, suppose I pin to the top twice: once 50 points from the top margin and once 53 points from the top margin. The view can’t be in two places at the same time. This causes a conflicting constraint error, which shows as red i-beam in the storyboard.

Screenshot 2015-01-05 07.11.20

To correct a conflicting constraint, delete the conflicts until there is only one constraint. If we clicked on the red constraint above that says 50, and then press the Delete key, the constraints will be correct.

Working with Auto Layout

With the above explanation, let’s try to put together a user interface using auto layout. We’ll use three buttons, a label and a text view and make them work properly on any device in both landscape and portrait.

Set Up the Project

Let’s start with a new project. In Xcode hit Command-Shift-N and make a new single view project named MySizePizzaDemo. Use Universal for the device and set Swift for the Language.

Once loaded, Go to the storyboard. On the bottom tool bar you will see the size classes like this:

Screenshot 2015-01-08 06.02.53

Make sure this reads wAny hAny. This is the most generic case, and thus works with everything. Drag onto the storyboard a label with a green background and black text centered. Make the label read Hello Pizza. Then add a button with a red background with white text titled Done. Add two more buttons with a blue background and white lettering. Title one Cheese and the other Pepperoni. Add a text view with the default black text on white background. Set the superview’s background to black to see everything better.

Arrange the buttons so they look like this:

Screenshot 2015-01-05 20.05.06

Center Align the Text Field

To start using constraints, we will align the text field to the center of the superview. Select the text view and then click the alignment button. Check Horizontal Center in Container  and Vertical Center in Container, and select items of new Constraints like this:

Screenshot 2015-01-05 20.13.48

Your results are not very exciting. The text field almost disappears. This is why you might want to wait until you have set all your constraints to update a frame. In this case we have no size, so auto layout assumes a size of 0 height and 0 width. Click the pin button and set the Width to 175 and the height to 175. Again update the frames for Items of New Constraints. Our text field shows up again.

Pinning the Label

For the label, let’s pin everything. Select the label a pin to the top, left and right margin with a value of 0. Set the height to 36pt like this:

Screenshot 2015-01-05 20.29.20

To turn on the i-beam in the menu, click on one of the dotted i-beams. When entering numbers into the fields be sure to press tab after each entry or the value will revert to its previous value. This time everything is set on this one page. Update frames with Items of New Constraints. The storyboard should look like this:

Screenshot 2015-01-06 05.35.19

You might have noticed we didn’t specify a width. That was intentional. We pinned the leading and training edges of the label to the left and right margins, and let auto layout figure out the width for the device it will display on. The width will act as a rubber band – stretching and compressing to the width of the display.

Constraints on the Done Button with Control-Drag

We could set up constraints the same way for the Done button as we did with the label. There is another way to make a constraint which can be helpful. Using control-drag while on the storyboard brings up a modal menu of all possible constraints in this case. Select the Done button. Control-drag straight down from the button until your cursor is over the black of the superview. You get this menu:

Screenshot 2015-01-06 05.28.58

You can click to select one constraint, or shift-click to select more than one constraint. Click Bottom Space to Bottom Layout Guide.

There is a drawback to this way of making constraints. It does not set the values correctly — control-drag just keeps your current values. You need to go to the size inspector to set the values for the constraint. In the right panel on Xcode click the ruler button, which is next to the property inspector.

Screenshot 2015-01-06 05.45.42

About halfway down, you will find the constraint:

Screenshot 2015-01-06 05.41.00

This tells us the constraint has a value of 29, or it is 29 points away from the bottom layout guide. We want it to be 10 points away. Click the Edit button in the constraint, and change the Constant to 10.

Screenshot 2015-01-06 05.41.34

Press Return after you make the change. The Done button will respond with a misplacement error. Since we have not set enough constraints yet, we will wait to update the frame.
Select the Done button if not already selected. Control-drag directly left into the superview. You will get another menu, different than the first. These menus are context sensitive, and give only what applies to the situation.

Screenshot 2015-01-06 05.52.27

Select Leading Space to Container Margin. Now edit the constant to be -16. Usually we set this to the margin which is 16 points from the edge of the screen. In our case we want to start at the edge of the screen, so we use a constraint of -16.

Screenshot 2015-01-06 05.54.21

From the selected Done button, Control-drag up and to the right. You get this menu.

Screenshot 2015-01-06 06.05.24

Diagonal drags will take both directions dragged as their context.By dragging up and to the right we got trailing and top space constraints. We only need a horizontal trailing space this time. Click  Trailing Space to Container Margin. Change the constraint constant to 0 so we are flush with the margin.

Screenshot 2015-01-06 06.08.28

We have all our constraints, but we can see they are misplaced:

Screenshot 2015-01-06 06.10.18

With the Done button still selected, click the resolver menu and in the top part for Selected views, click Update Frames.

Screenshot 2015-01-07 06.45.09

Our Done button now looks right, and we have blue i-beams.

Screenshot 2015-01-06 06.12.42

Pepperoni’s Nearest Neighbor

Select the Pepperoni button. Using the pin menu set the top to 10 the right to 10, and the left to 0. Set the height to 36. Update the frames as well like this:

Screenshot 2015-01-07 06.52.49

We get something we did not expect:

Screenshot 2015-01-06 06.28.59

The pin menu always relates to the nearest neighbor. The Pepperoni button started overlapping the label. Once one view overlaps another, it is no longer a neighbor. The nearest neighbor heading upward is the top margin, so it placed itself 10 points from the top margin, overlapping the label even more. Since we placed one constraint between Cheese and Pepperoni, auto layout guesses at the vertical placement of Cheese.

There are several ways to fix this. One is prevention. If we dragged the Pepperoni button down slightly before pinning,  it would not overlap the button.  Our pin would have worked. Another option is change the constant for the Top Edge to Container Margin to 46 points so we have our button 10 points below the label. I’m not a fan of this. If our label height changes for some reason, we have the same overlap problem again.

The third solution is to make a new constraint between the Pepperoni button and the label. This is where control-drag is the most useful. When you need to make a constraint between two views that are not neighbors use control-drag.
To prevent a conflict, delete the current constraint by going to the size inspect and clicking on the left side of the constraint:

Screenshot 2015-01-06 06.21.18

Press Delete on the keyboard. It disappears. Now select the Pepperoni button. Control-drag from the Pepperoni button into the label. You will see the label highlight. Release the mouse button. The following menu appears.

Screenshot 2015-01-06 06.21.59

Select Vertical Spacing which is the same as pinning to the top. In the size inspector, you’ll see the constraint Top Space to: Hello Pizza!! has a constant of -26. Edit the constraint to have a constant of 10.

Screenshot 2015-01-06 06.22.21

Update the frame if necessary. You may find this change automatically updates.

Aligning The Cheese Button

We have one more button to go. We already have a horizontal alignment, we just need a vertical. We will align the top of the Cheese button to the top of the Pepperoni button. Select the cheese button and then shift select the Pepperoni button. Click the align menu and select Top Edges. Keep the value at 0. Update frames to new constraints like this:

Screenshot 2015-01-06 06.53.25

With that change, we now have satisfied all constraints and have no errors. It still doesn’t look great. We need to make a few more changes.

 Screenshot 2015-01-06 06.57.55

Filling Space with the Text View

The first change is to fill the middle space with the text view. Select the text view. In the resolver, Clear Constraints for selected views. This is the easiest way to avoid conflicting constraints — get rid of all of them. Click the pin menu. Set the bottom to 10, and the left and right to 0. Don’t update the frames.

Screenshot 2015-01-06 07.05.56

We have a slight problem here using pin for all four directions. Directly above the text view is the label. if we pinned the top with 10. It would pin to the label with 10, overlapping the buttons below. We want the text view to pin to the buttons, so we skip that pin and will use a control-drag to set it. But we have no top constraint or a height. Auto layout will guess the height is 0, and the view disappears if we update frames now. This is why we didn’t update views this time.

Control-drag from the text view to the Pepperoni button. Select Vertical Spacing. You will get a blue vertical i-beam. Place your cursor in the beam and it will highlight. Click once on the i-beam. This is another way to edit the constant. You will see in the size inspector only information about this constraint. change the constant to 10 like this:

Screenshot 2015-01-06 07.19.43

Now the constraint shows a misplaced error. Select the text view again and for the selected views update the frames.

Screenshot 2015-01-06 07.23.11

Equal Widths and Equal Heights

The buttons on the top are bit too small. They should be bigger and more consistent in size. Two constraints help with this: Equal Widths and Equal Heights. These two will take the size constraints of one view and use it for other views. To make them work correctly we need one more constraint on the Cheese button. Pin the right side of  Cheese to 0 and update the frames.

Screenshot 2015-01-06 07.28.23

The Cheese button stretches across the superview. Control-drag from the Pepperoni button to the Cheese button. Holding down shift, select both Equal Widths and Equal Heights.

Screenshot 2015-01-06 07.28.45

The two buttons now are the same size.

Screenshot 2015-01-06 07.31.00

I’d like all my buttons to be the same height. I’d also like them a little taller than they are now for easy entry. Let’s add one last constraint between the Pepperoni and the Done button. Since the text view is in the way, control-drag from Pepperoni to the Done button. Select Equal Heights on the menu that shows up. Select the Pepperoni button again. In the size inspector, find the height constraint and change it to 64:

Screenshot 2015-01-06 07.31.41

If necessary, update frames. Your storyboard should look like this:

Screenshot 2015-01-06 07.32.03

Preview Mode

The square story board does not look like any device I’m familiar with. What this will look like on a device we can get by using the simulator, though that requires building and running the simulator.  A faster way during the layout stage is to use the preview mode in the assistant editor.
Click on the assistant editor. At the bottom of the drop down menu that usually shows you will find a Preview selection.

Screenshot 2015-01-07 05.38.17

Select the Main.Storyboard and the current view appears for a iPhone 4 inch.

Screenshot 2015-01-07 05.44.03

At the bottom of each preview is the name of the device. If you float your cursor over the device name, a small icon for rotation appears.

Screenshot 2015-01-07 05.45.53

Click it and the phone rotates. It may be too big for the panel. On a touch pad, pinch to change the scale until you can see the preview

Screenshot 2015-01-07 05.50.41

On the lower left side of the preview, there is a + button to add more devices to your preview. Click the + button

Screenshot 2015-01-06 07.33.21

A list of possible previews appears. You can select any of these you wish. If you want to see both landscape and portrait at the same time, you can select the same device twice and rotate one.

Screenshot 2015-01-07 06.02.23

Usually you’ll want an iPhone and iPad preview, since these may be different. As we’ll learn more in the next sections on class sizes, you can customize for each size class and orientation. It becomes handy to see every change you make.

Screenshot 2015-01-06 07.33.58

If you make a mistake, the preview pane gets too busy, or need to get rid of a preview device for any other reason, click on the name of the device to select it, then press the Delete key on your keyboard to remove the device.

For those using localization, on the bottom right you will find a button you can use to preview in the different localization languages. Click the default language, and a list of languages appear. One interesting selection for testing is the Double Length Pseudo-language, which doubles all your text to give you an idea how an interface will look with a language that uses a lot of characters.

Screenshot 2015-01-07 05.45.24

With these basics, you can lay out most user interfaces. There are more features and ways to use auto layout. We’ll cover some of those in other lessons.

Auto Layout and Size Classes: The Regular Size and Lining up Buttons Evenly

In the conclusion of our series on auto layout, we’ll discuss the regular size for iPads and one side of the iPhone 6 plus. We’ll make a line of evenly spaced buttons using auto layout, first for the iPad, then again for the iPhone 6 plus in landscape with its odd regular width, compact height size.

Transcript

Hello, welcome to our final installment in the beginning auto layout videos. I’m Steve from MakeAppPie.com

With size classes we’ve discussed there are three classes: Compact, Any and Regular.

We’ve already discussed most iPhones have compact sizes, and in the previous lessons set up a special arrangement in compact height for landscape with buttons on the side.

This lesson will be on the regular class. In its default settings, an iPad and an iPhone 6 plus in landscape have regular sized sides.

iPads have regular sizes on both sides. The iPhone 6 plus has one regular side and one compact side.

In our demo, we’ll continue working with the layout from the previous videos. We’d like to have side by side buttons just below the label in regular views. We’ll also make the Done button proportionally smaller than the rest of the buttons.

We would like to keep our buttons proportionally sized on different devices. However the actual size of the view changes. There are a few techniques in auto layout we will use to accomplish equally sized buttons.

The key to equally sized buttons is keeping the beginning and end buttons in a specific place. We then add constraints to keep aligned vertically and the same space away from every other button. Next, we make all the buttons the same size and height as the beginning button and specify the width and height as necessary for the beginning button. Our final step will be to make one button proportionally sized smaller than the rest.

Let’s do this for all regular width devices. Change the size class to regular width any height.

If you cannot see it, move the preview over to show the iPad preview.

Our vertical constraints for the text view are currently set to be 10 points from the Cheese button and 10 points from the Done button. Those buttons will be moving and will mess up our text view when moved, so we’ll constrain the top and bottom constraints to the container view’s margins instead.

Control drag the text view to the bottom space and click Bottom space to Bottom Layout Guide to make a new constraint for the bottom view.

We’ll also do the same to the top margin of the text view. Control drag from the text view to the top of the container. Select Top Space to Top Layout Guide.

Often it’s best to clear constrains instead of changing them when making big changes. Clear the constraints on the Done button

Clear the constraints on the cheese button as well.

We will need some space to put the done button. Resize the Cheese button by dragging the right handle over until there is some space for a done button. Don’t worry how much, we will be setting sizes later.

Move the Done button to its new position and resize it to generally fit. Again, we don’t have to be anywhere close to perfect, as the constraints will do that for us. You can use the recommended choices from the guides as they show up.

Control drag to the right and select Trailing Space to Container to anchor this to the right margin.

Once done, control drag from the Done button to the Pepperoni button. Shift select Top, Equal Heights and Equal Widths.

Since the Pepperoni button is already anchored and sized we don’t do anything with it. We now need to set the middle Cheese button. Control drag the Cheese button to the Pepperoni button. Shift select Horizontal Spacing, Top, Equal Heights, Equal Widths.

Now control drag from the cheese to the done button. Shift select Horizontal Spacing, and Top.

Update the frames. We now have three buttons of equal widths aligned.

We want the Done button to be smaller than the other buttons. Select the Done button and then click Edit for Equal Width to Pepperoni. Change the multiplier to 1:3.

And we’re done. It looks good on the iPad.

However the preview in iPhone 6 plus in landscape is a big mess. The regular width, compact height has too many constraints and can’t figure out what to do. Change the size class of the storyboard to regular width, compact height.

Select Done and you will see far too many constraints. This is the point that is best just to get rid of it all.

Select the text view, which also has too many constraints and clear its constraints. Move and resize the text view using the guides.

Let’s do the same for the Done button. Select the Done Button and clear its constraints. Move the Done button over to the right margin aligned with the other buttons.

Now let’s get the Cheese button cleaned up. Select and clear the constraints. Move the cheese button to the center.

To finish our cleanup, clear the constraints on the Pepperoni button. It is in the right position so we don’t have to move it.

Select the text view, and then the pin menu. add i-Beams on the bottom, left and right. Pin the left margin to 0 points, the right margin to 0 points, and the bottom to 20 points. Set the height to 240 points.

Select the Pepperoni button. Pin 10 points to the top,10 to the bottom, 0 to the left margin, and update constraints. Remember this is to the nearest views, so we are 10 from the label and 10 from the text view.

Select the Done button. Pin 10 points from the top 10 points to the bottom, 0 points to the right margin, and again update the constraints.

Select the Cheese button. Pin left 10 points and right 10 points but don’t update its constraints. There are placement errors at the moment but we will deal with them after we set the vertical alignment and size.

Control drag from Cheese to Pepperoni and select Top.

Control drag from Cheese to Pepperoni again. We already have horizontal spacing and top defined. Shift click equal widths equal heights.

Set the size of the done button. Control drag from Done to Pepperoni. Shift click equal widths and equal heights.

Now everything is set up correctly. On the resolve menu, click update all frames.

We still need to make the done button smaller. Like before, select the Done button. In the inspector click the Edit button on Equal Width to Pepperoni. Change the multiplier there to 1:3.

Going over to the preview pane, we see the iPhone 6 plus is now set up correctly.

We’ve now completed the size class and auto layout of this app’s storyboard. a general lesson to remember is to start with width any, height: any and work your way to more detailed size classes if you need to make changes.

Thank you for watching this short series on beginning auto layout and size classes. For more cool stuff on iOS app development head over to makeAppPie.com.

Swift Swift: Using UIScrollView with Autolayout

Last week we talked about cameras, one thing I’d like in my camera app is the ability to take a picture then zoom and scroll around the photo. For that, we need to use the UIScrollview. UIScrollView is a very flexible and important control in UIKit. It is the basis for many the controls we all know and love including UItableView. IT’s what does all the pinch zoom and pan on many applications like maps. I love UItableView and UIPicker, but more than once I’d like that does the same thing moving sideways. besides zooming I’ll cover that too. Most importantly, in iOS8 we can’t get around auto layout, which messes up just about everything that traditional approaches to UIScrollView does. I’ll show you a good way of handling things.

Set up the views

we are going to use two UIScrollViews for this in auto layout. Make a new project named SwiftPizzaScrollview with a single view in Swift and universal. Drag two UIScrollViews on the screen and anchor one to the bottom and one to the top. Make 10 point vertical space between them, and equal heights. Update the frames then change the equal height to a 1:4 proportion. IF that made no sense to you, follow the video below:

Once you have the layout completed, open the aassistant editor and set to automatic so you see the viewController.Swift class. Control drag from the top UIScrollView to the class and make an outlet named buttonScroll. then control drag from the bottom UIScrollView and make a outlet named imageScroll.

    @IBOutlet weak var imageScroll: UIScrollView!
    @IBOutlet weak var buttonScroll: UIScrollView!
  

Setting up the Button Scroll

The first scroll we will look at is the button scroll. Scroll views work by having view added to them and the contentSize property being bigger than the size of the scroll view. We’ll need a big view for this. For this one, I’ll make a programmatic row of buttons that select a color. Under the outlets, add this code to make a function to create the buttons:

    func colorButtonsView(buttonSize:CGSize, buttonCount:Int) -> UIView {
    //creates color buttons in a UIView
        let buttonView = UIView()

        return buttonView()
}

This makes a view, but doesn’t do much, let’s start by defining a few properties for the view. We’ll make the background black, and set the origin to 0,0.
Add this after the buttonView declaration:

        buttonView.backgroundColor = UIColor.blackColor()
        buttonView.frame.origin = CGPointMake(0,0)

Next we need to size the view. However to size the view we have to calculate its size based on the size of the buttons and the padding added to it.

let padding = CGSizeMake(10, 10)
buttonView.frame.size.width = (buttonSize.width + padding.width) * CGFloat(buttonCount)
buttonView.frame.size.height = (buttonSize.height +  2.0 * padding.height)

Line 1 I made a constant for the padding I can use in my calculations. The width of the view in line 2 would be the size of the button and one side of padding multiplied by the number of buttons. The height in line 3 is the height of the button with a top and bottom padding.

Our strategy to add the buttons is to loop through ans add buttons each loop. Along the way we will change the x origin of the new button, and change the color using a HSB hue in UIColor(hue:saturation:brightness:alpha) We’ll need some initial values before we start the loop:

//add buttons to the view
        var buttonPosition = CGPointMake(padding.width * 0.5, padding.height)
        let buttonIncrement = buttonSize.width + padding.width
        let hueIncrement = 1.0 / CGFloat(buttonCount)
        var newHue = hueIncrement

Line two starts us at a place with some padding. Line 3 is the distance between buttons on the x axis. line 4 is the distance between colors we will show. Since colors are represented between 1.0 and 0 this will give the number of colors. We initialize a value for the hue we will use.

Next we start the loop. Add the loop like this:

for i in 0...(buttonCount - 1)  {
            var button = UIButton.buttonWithType(.Custom) as UIButton
            button.frame.size = buttonSize
            button.frame.origin = buttonPosition
            buttonPosition.x = buttonPosition.x + buttonIncrement
            button.backgroundColor = UIColor(hue: newHue, saturation: 1.0, brightness: 1.0, alpha: 1.0)
            newHue = newHue + hueIncrement
            button.addTarget(self, action: "colorButtonPressed:", forControlEvents: .TouchUpInside)
            buttonView.addSubview(button)
        }

We make a button, set it size and origin, then increment the origin for the next loop. Similarly, we set the background color of the hue to a color, then increment the hue to the next color. We add an action to the button and the add the button to the view. when the loop finished we have a complete view we can return and do what we need with it.

Only thing missing is the target. Add this under the last method:

func colorButtonPressed(sender:UIButton){
        buttonScroll.backgroundColor = sender.backgroundColor
 }

Which just take the background color and makes the buttonScroll that color.

Making the sliding button scroll view

To make the scroll view run, we need the following code in the viewDidLoad:

      //scrolling pageview
        let scrollingView = colorButtonsView(CGSizeMake(100.0,50.0), buttonCount: 10)
        buttonScroll.contentSize = scrollingView.frame.size
        buttonScroll.addSubview(scrollingView)
        buttonScroll.showsHorizontalScrollIndicator = true
        buttonScroll.indicatorStyle = .Default

Line 1 gets a view. once we have the view we have it’s size which we make the contentSize of the scrollingView. If you skip this step the scroll view will not work. We add the view to the buttonScroll and we are set tot go. The last two lines add a scroll indicator for cosmetic purposes.
Build and run. You get a color picker that side scrolls.

Screenshot 2014-12-10 16.14.13

Using a Xib

We used for this example a programmatically generated view. It’s not the only way to go. While I didn’t use this, you could use a xib if you wanted various controls in the code. Set up a view controller with a xib. Set an absolute height and width for the xib’s view to make it easy to get a contentView size. Then declare the view controller (again this isn’t part of our app)

let scrollingViewController = UIViewController(nibName: "buttonScrollViewController", bundle: nil)

Instead of line 1 above in the viewDidLoad use this:

let scrollingView = scrollingViewController.view

This will allow you to put any control in the scroll view, and use the xib’s view controller to control it.

Scrolling a Picture

We’ll need a photo for this. We’ll use this one of a pizza:

pizza

Download the image and click on Images.Xassets in Xcode. Drag the image into the assets from your finder.

Just like the scrolling buttons we just have to make a view, add it to the scroll view and set the content size. Next make an UIImageView in the declaration just below the outlets:

 var imageView = UIImageView(image: UIImage(named: "pizza"))

And then we add this to the viewDidLoad

imageScroll.contentSize = imageView.frame.size
imageScroll.addSubview(imageView)

Build and run. The photo is 640×640 so depending on the simulator you pick you will get different scrolling behaviors. Use a small device like the iPhone 4s and you will get more scrolling.

Zooming in a scroll view with Auto Layout

If you look across the web, you will find a lot of articles on zooming with UIScrollView. If you use Auto Layout, you will quickly and frustratingly learn auto layout has problem with the way most prope write for UIScrollView. Some turn off auto layout to get around this. The problem is a moving target. A Zoom needs several things:

  1. Add the UIScrollViewDelegate
  2. Set the delegate
  3. Implement the delegate method viewForZoomingInScrollView
  4. Set the maximumZoomScale property
  5. Set the minimumZoomScale property
  6. Set the current zoom

The maximumZoomScale and minimumZoomScale properties of the UIScrollView cannot be equal on order for zoom to work. It’s these scales which messes everything up. Most people set their zoom by making a minimum zoom as a ratio of the bounds of the scroll view and the content view. This usually get coded in viewDidLoad, except auto layout does not set the bounds of the scroll view that early. It’s one of the last things to happen. So all of that code is looking for something either that is the wrong size, or doesn’t exist yet.

The way around this is overriding didLayoutSubViews. This happens after any changes to the bounds, and all the bounds are actually set. This takes care of both auto layout not knowing anything on the initial load, and any time we change orientation. Both cases are caught. There is one caution to this. The content size will change in the process of a rotation. We will need to reset it every time we go change the scale. To make this easy, I made a reference property for the class

 var imageSize = CGSizeMake(0,0)

You could keep calling the imageView’s frame if you wanted, but it seems verbose. Instead in viewDidLoad I assign it and the delegate, and set the content size in didLayoutSubviews. Change what we had for the imageScroll code in viewDidLoad to this:

//zooming photo
        imageSize = imageView.frame.size
        imageScroll.delegate = self
        imageScroll.addSubview(imageView)
        imageScroll.showsHorizontalScrollIndicator = false
        imageScroll.showsVerticalScrollIndicator = false

Much of this code we discussed already. I added the scroll indicator properties explicitly instead of its defaults.
now add the following just below viewDidLoad:

  override func viewDidLayoutSubviews() {
            imageScroll.maximumZoomScale = 5.0
            imageScroll.contentSize = imageSize
            let widthScale = imageScroll.bounds.size.width / imageSize.width
            let heightScale = imageScroll.bounds.size.height / imageSize.height
            imageScroll.minimumZoomScale = min(widthScale, heightScale)
            imageScroll.setZoomScale(max(widthScale, heightScale), animated: true )
    }

Line 2 sets the maximum zoom scale. I used an arbitrary value of five times the size of the image. I then set the content size. As we’ve already said, the scroll view needs this, but we are keeping the size under control by using a constant, since the content view changes when we rotate the view, and can mess up our calculations. Next we figure the scale by taking the bounds of the scroll View, which by now are correct since auto layout is done with them. We see if the widthScale or heightScale is smaller, and use that scale as the one for our minimum zoom scale. We then set the scale for the scroll view as the minimum zoom scale. You can do a lot with this code and modify it a lot of different ways for different scale effects. This is the basics.

Our last step is to set up the delegate, which is a quick one. It just wants the view we are zooming to return to the delegate

//MARK: Delegates
    func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
        return imageView
    }

Build and run. You get a working scroll view.

Screenshot 2014-12-10 16.34.05
You can do a lot from this point. The scrolling anchor point for example is the origin at (0,0) some people like zooming from a center point. The big thing you need to do is get your sizing information after auto layout does what it does.

Kinderswift 9: Strings

Transcript

Hello, welcome to Kinderswift Lesson 9. I’m Steve from makeapppie.com

So far we’ve worked with numbers and colors, but what if you wanted to work with text?

The basic text type in Swift is String.

Like a literal Double of 3.5, we can have a literal String. Enclose String literals in quotes like this

"This is a string"

We declare strings the same way we declare other types we’ve discussed. Implicit typing works on strings. You can declare a string constant by typing this:

let string1 = "stringthing1"

You can declare a string variable by typing this:

var string3 = "blank"

You can of course be explicit about your type as well:

let string2:String = "my Double result is"

You can also make an empty string by declaring this.

var myString:String = ""

If you have two strings and you want to join them together we concatenate them. The concatenation operator may look a little familiar: it is the plus symbol.

A simple example would be this:

string3 = string2 + string3

Notice what happens. Swift joins the two strings but there is no space between them. Swift will take what you give it and put them together. Often we will concatenate a string literal with a space between two strings in a concatenation like this:

string3 = string1 + " " + string2 //concatenation

If you are adding to the end of a string the += is an operator for that.

string3 += " " + string1 //concatenation

It is important to remember this is not addition even though it uses a plus.

There are a lot of string functions. We will cover many of them as we go through the lessons. There is one initializer function you should get used to early.

String(format:"hello pizza number %i",12)

This is the formatting string. It takes numbers and other strings, usually in variables, and adds them correctly formatted to the string. We use this very often when displaying formatted numbers.
The key is the part behind the % sign. It adds a code to say “add this type” to the string. %i is for Int. There is a code for Double too %f. We add a set of numbers before it to give the length in characters and number of decimal points. %6.2f adds six size spaces before with two decimal points. %10.3f is a ten space number with three decimal points

Try this:

let a = 2.567891
let pi = 3.1415926
let myInt = 12
String(format:"the integer i equals %i",myInt)
String(format:"a = %6.2f",a)
String(format:"the value of pi is %6.3f",pi)

Let’s do an example of a variable and a calculation:

String(format:("The area of radius %1.3f is %2.3",a, pi * a * a)

Now that we have done this last example, Let’s go back to our pizzaArea and deep dish volume functions

func printPizzaArea(diameter:Double){
	let radius:Double = diameter / 2.0
	let area = pizzaArea(radius)
	println (radius,area)
}

Right now they print pretty ugly. Let’s pretty it up a bit using strings.

func printPizzaArea(diameter:Double){
	let radius:Double = diameter / 2.0
	let area = pizzaArea(radius)
	let displayString = String(format:"A Pizza with radius %2.2 is %2.2",value:)
	println (displayString)
}

The print gives us six digits of space for the format, and two decimal places. We print the string instead of the values.

Next time, we’ll collect variables and functions into a single object called a class.

For a challenge, try to do the following:

Make a function conctenateWithSpace(a,b) that returns a string that concatenates a with b, but with a space between therm. so concantenateWithSpace("Hello","Pizza") returns a string “Hello Pizza”

Format the string in deep dish so it prints this.
For a pizza of diameter 12
The area is x.xx and the volume is x.xx

hint: you can use more then one printLn in a function.