”;
In this chapter, we will cover some advance features like Creating Multiple Views on our Application, Adding Navigation Bars, Adding Table Views, Storing Data in the Application, Making Web Applications, etc.
Please go through every section carefully, as this chapter contains most of the things, which we need while developing applications.
Multiple View Controller
In our previous applications, we only gave a single view / view controller. However, we can have multiple views in our application and we can perform on any of those independently.
Therefore, we will start by creating a new project; the name of this project has multiple views. As every other project, this project also has a View Controller and a Swift File for that controller. (This you can see by selecting view & seeing its properties in the Identity Inspector.)
The following screenshot shows how our current view looks like −
On the right-hand side (Identity inspector), we can see the class that is related to our view controller. This arrow on the left is the entry point. This is the first view of our application that will show after the application starts running.
Adding Second Multiple View Controller
To add other view controllers to our application, we will search view controller in our object library. Once we find it, we will drag the view controller to our main.stroryboard, just outside any other views.
This is how your application should look like. Now, we have added a view controller, but now we will also need to create a view controller class for our newly added view.
Right click on your project → new File → cocoa Touch Class → Name it anything you want to, we will name it “SecondViewController”.
This is how you create a class file for your view controller. Now, go back to your “main.storyboard”, click on your second view controller and see its Identity Inspector.
The class field must be empty right now, so click on that field and start typing your class name that you added in last step. If it appears, click enter.
We have now created a multiple view controller and added the controller class file for that View. However, if you run the application, it will not show your second view still. Why?
Because we have not added a function, which will take us to that view. In short, we have not yet added Navigation to our Application. Do not worry; we will cover it in the following section.
Adding Navigation to the Application
The process of transition from one view to another is called Segueing, i.e. done by creating segue between the two views. To do this, add a button in the first view controller, and control drag from it to your second view. When you release the button, you will see some options as shown in the screenshot below.
Select the Show option from the Action Segue. Now run your application, you will see that on the click of a button, you second view appears (To see more clearly add something in your second view, so that you can identify).
However, now you cannot go back to your first view. For that, we have Navigation controllers.
Adding a Navigation Controller
Select your first view controller and in the top bar, click Editor → Embed in → Navigation controller.
Now, our application should look like the following screenshot.
We should see to it that, there is a little light grey row on top of the view. Now, when we Run the application, we can see that there is a navigation bar on top of the view. When we click on the button, we will go to second view, where we will see a back button in that navigation bar. Click on this and we will come back to the Initial View.
Adding Title & Back Button to the Navigation Bar
To add a title to your navigation bar, click on the navigation bar, and see its attribute inspector. There we will see −
-
Title − This will be the title of Navigation bar, which appears in center.
-
Prompt − This appears on top of title bar, in center.
-
Back Button − Here you can modify the Text that appears in back button.
Currently the button that is passing the view is located on our view, which might not suit if we want something else to appear on the screen. Therefore, we will add a Bar Button item in the navigation bar, which will take us to our second view. However, for this we should first delete the last button we added.
Adding a Bar Button Item
Search for bar button item in the object library and drag drop to the right hand side of the navigation bar. Name it as – “Next >”, control drag from it to the second view, select Show as we did with the last button we added.
Now run the application, it will look cleaner and better. This is all we will do with Navigation right now. In the subsequent chapters, we will modify the navigation bar using the Swift Code, when required.
Table Views
A table presents data as a single column list containing multiple rows, which can be further divided into sections. Tables should be used to present data in a clean and efficient way.
In this section, we will understand how to add table views, adding prototype cells, adding data source and delegates for a table view, changing properties of a table and setting dynamic data for table view cells.
Adding a Table View
To add a table view, we will first create a new project and name it as – “tableView”. Then, go to the object library and search for Table View, we will see the table view, the table view controller and many other options. However, we should select table view, drag it and add to the default view Controller.
Adding a Prototype Cell
Stretch the table view so that it covers the complete view, while your table view is highlighted. Check its attribute inspector, there is a field called Prototype cells, which is currently 0. We should change its value to 1, now your view should look as follows −
Changing Cell Identifier
Now, inside your view, click on your prototype cell (which is a little tricky). So, in your Document outline, click on View controller → View → Table View → Table View Cell, and now in its attribute inspector there is a column called Identifier, click on that and name it as “Cell”. See the following screenshots to understand the above steps.
Adding Delegate & Data Source
To make our table views dynamic, we need them to load dynamic data. Therefore, we need a delegate and a data source for that. To make delegate and data source of your table, control drag from table view to your view controller or the yellow button on top of the view controller as shown in the screenshot below.
When we release the cursor, we will see two options there, dataSource and delegate, select them one by one (when you select any one option, the pop up will hide, you will need to repeat the above step to add a second option). Now it should look like −
This is all we will do with our UI / Main.Storyboard, right now. Now switch to “ViewController.swift” file. Add UITableViewDelegate, UITableViewDataSource, to your viewController.swift as shown below −
However, now Xcode will show an error in this line.
This is because there are a couple of methods where we need to use UITableView
To see these methods, Command + click on the UITableViewDataSouce, and copy the first two methods, having “numberOfRowsInSection”, “cellForRowAtIndex” Arguments and paste them in the ViewController.swift, before our viewDidLoad().
Remove this line @available(iOS 2.0, *), from both the methods and add the opening and closing braces “{}”. Now, the view will look as follows −
Xcode must be showing error in both the functions. However, do not worry as this is because we have not added a return type of those functions.
numberOfRowsInSection − This function defines the number of rows our section will contain. So right now add this line to your method.
return 1 //This will return only one row.
cellForRowAt − This method returns the content for every cell, indexPath contains the index of every cell. We will create a cell and then we will assign some Value to that Cell and finally return the cell.
Now, your functions should look as follows −
internal func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1; } internal func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "Cell") cell.textLabel?.text = "First Row" return cell }
In the first line, we are creating a cell with default style, and reuseIdentifier is the name of the prototype cell we made.
Cell.textLable?.text − This defines the text that should appear as title of that cell.
Finally, we return a cell from there. Try running your Application now, it should look as follows −
Time Table Application
In this application, we will continue our last project, and we will make an application where we print the table of 2 (2… 10…. 20).
So, to make this application, just change the project’s view controller file.
Change the functions as shown below −
internal func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 10; } internal func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "Cell") }
Now, run your application. It should look as shown below.
Now, as we have completed the table views and made an application, so here is a quick challenge for us to solve.
Challenge
Make an application, where we print the counting table of whichever number the user inputs.
Hint − Take input, add a button, which when pressed will load the table with counting of that number. Here we will also need the following function, which will reload the table data.
tableView.reloadData()
This is a challenge for you as we have covered all topics about this application, so we will not provide solution for this.
Egg Timer Application
In this application, We will use the concept of Timer() and Class Constructor, which manages time. We will provide you the concept and the coding. You have to make the UI yourself as we have already discussed every UI Element many times in our previous chapters. (Though we will provide hints for everything that looks quite new).
Your final app layout should look like this −
What happens in this application?
-
The title label has a starting value 210.
-
On click of play button, value should decrease by one every second.
-
On click of pause, value should stop there only.
-
On click of -10, value should be reduced by 10, and decrement should continue.
-
On click of +10, value should be increased by 10, and decrement should continue.
-
On click of Reset, value should become 210.
-
Value should never go below 0.
Concept
-
We will use a variable of class Timer() → var timer = Timer().
-
We will set a value to this timer variable we just created.
-
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.processTimer), userInfo: nil, repeats: true)
-
timeInterval -> is the time interval which we want to use,
-
target -> is the view controller which should be effected,
-
selector -> is the function name which will use this timer,
-
userInfo -> null and repeats, yes we want to repeat so It’ll be true.
-
Invalidating Timer
To stop a timer through programming, we will add timer.invalidate() function.
Elements we have used −
Navigation bar − In the navigation bar, we have added three items.
- Bar Button Item, one on left and one on right.
- Title named as – “Our Egg Timer”.
Toolbar − A toolbar appears at the bottom of an app screen and contains buttons for performing actions relevant to the current view or content within it.
Toolbars are translucent and may have a background tint. They often hide when people are unlikely to need them.
We have added a toolbar at the bottom of our UI, which has 5 items.
- Three Bar button items, named -10, Reset and +10.
- Two flexible Space : Flexible space between bar button items −
How to add an icon to bar button item?
Select your bar button item. Click on your bar button item, go to attribute inspector, click on Select Item and choose the item from the Dropdown that appears.
Similarly, select items for all other buttons and create a UI as given above. Add a label to the center of the View and connect it as an outlet, name it as − timeLeftLabel.
Action for Start Timer
Following is the program for the start timer.
@IBAction func startTimerButton(_ sender: Any) { if !timerActive { timerActive = true eggTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.processTimer), userInfo: nil, repeats: true) } }
Create the following function −
func stopTimer() { timerActive = false eggTimer.invalidate() }
Action for Stop Function
Following is the program for the stop function.
@IBAction func pauseTimerButton(_ sender: Any) { stopTimer() }
Action for Subtracting Time
Following is the program for subtracting time.
@IBAction func subtractTime(_ sender: Any) { if timeLeft > 10 { timeLeft = timeLeft - 10 timeLeftLabel.text = String(timeLeft) } }
Action for Resetting Time
Following is the program for resetting the time.
@IBAction func resetTimer(_ sender: Any) { timeLeft = 210 timeLeftLabel.text = String(timeLeft) }
Action for addTime
Following is the program for adding time.
@IBAction func addTime(_ sender: Any) { timeLeft = timeLeft + 10 timeLeftLabel.text = String(timeLeft) }
Now, the viewController.swift should look like −
import UIKit class ViewController: UIViewController { @IBOutlet weak var timeLeftLabel: UILabel! var eggTimer = Timer() // Initialize the Timer class. var timerActive = false // Prevents multiple timers from firing. var timeLeft = 210 func stopTimer() { timerActive = false eggTimer.invalidate() } func processTimer() { if timeLeft <= 0 { stopTimer() return } timeLeft = timeLeft - 1; timeLeftLabel.text = String(timeLeft) } @IBAction func startTimerButton(_ sender: Any) { if !timerActive { timerActive = true eggTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.processTimer), userInfo: nil, repeats: true) } } @IBAction func pauseTimerButton(_ sender: Any) { stopTimer() } @IBAction func subtractTime(_ sender: Any) { if timeLeft > 10 { timeLeft = timeLeft - 10 timeLeftLabel.text = String(timeLeft) } } @IBAction func resetTimer(_ sender: Any) { timeLeft = 210 timeLeftLabel.text = String(timeLeft) } @IBAction func addTime(_ sender: Any) { timeLeft = timeLeft + 10 timeLeftLabel.text = String(timeLeft) } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated } }
This is all we will do in our application, try to run the application, it should run fine.
Storing Data on Local Storage
Storing data on local storage means to use the local device’s storage to store data related to application on the device. We have two ways to store the data on a local storage namely NSUserDefault and CoreData.
Let us understand them in detail.
NSUserDefaults
NSUserDefaults are meant to store small pieces of data such as preferences, settings or individual values. To use UserDefaults in our application, we just need to create a reference to the nsuserDefaults through our code as shown below.
let defaultValues = NSUserDefaults.standardUserDefaults()
To set values to data in UserDefaults we can use the following code −
defaultValues.setObject("Simplified iOS", forKey: "nameKey") func setDouble(value: Double, forKey defaultName: String) func setBool(value: Bool, forKey defaultName: String) func setObject(value: AnyObject?, forKey defaultName: String) func setURL(url: NSURL?, forKey defaultName: String) func setInteger(value: Int, forKey defaultName: String) func setFloat(value: Float, forKey defaultName: String)
To get values from NSUserDefaults, we can use the following code.
func boolForKey(defaultName: String) -> Bool func integerForKey(defaultName: String) -> Int func floatForKey(defaultName: String) -> Float func doubleForKey(defaultName: String) -> Double func objectForKey(defaultName: String) -> AnyObject? func URLForKey(defaultName: String) -> NSURL?
CoreData
CoreData is a persistent framework, which supports large data transactions. CoreData allows you to build relational entity–attribute model for storing user data. CoreData is a framework and can use SQLite, binary formats to store data.
To use CoreData in our application, we will start with a new project and make sure you check “Use Core Data”, while creating the project.
Login Using core Data − Create a new project, select use CoreData as shown in the following screenshot.
Continue until the project is open, now we see that the project has more files than our previous projects.
This file CoreData_demo.xcdatamodeld is our database in which we will be making our user table and storing data.
Concept − The thing about CoreData is, even if we close the app, and open it after months, it will still have the data we stored, which we will see in the next application we make.
Now we will see how to add core data and retrieve core data.
Adding Core Data − To add CoreData, click on the file CoreData_demo.xcdatamodeld and then we will see that the entities are empty. Click on add Entity button, it will add an entity, now double click on the entity name and rename it anything you like.
Now click on the entity and we can see that the attributes field is empty. Click on the plus symbol and rename the entity. Select the type of the entity from the next field.
We have added an Entity and one Attribute in it. Now, if we go to the AppDelegate.swift, we can see that two new functions have been added because we selected CoreData. The two functions added are −
Note − Import CoreData in your file before proceeding.
Saving data to Core Data − To save some data in CoreData, we need to make an object of AppDelegate Class.
let appDelegate = UIApplication.shared.delegate as! AppDelegate
And, a context object
let context = appDelegate.persistentContainer.viewContext
Then, we need to create an entity object, which will call our entity −
let newValue = NSEntityDescription.insertNewObject(forEntityName: "Users", into: context)
We will now set the value of that attribute we created.
newValue.setValue(textField.text, forKey: "name")
We will save the data using
context.save();
Fetching from core data − While fetching, the above two steps (creating appDelegate and context) will be the same. Then, we will create a fetch request.
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Users")
We will create an object to store the result.
let results = try context.fetch(request)
Then we will go through the results as per our requirement. We will see more of CoreData with the next application we create.
Challenge − Try to create an application, where the user enters the name, then clicks on login and closes the application. When the next time the user opens the application, he should be still logged in. Then add a button – logout, and if he clicks on that, the application will ask for the username again.
Login/Logout using CoreData
Create a single view project called ‘Login’, select use of CoreData. Click on CoreData_demo.xcdatamodeld and add an entity called ‘Users’. Inside that, add an attribute called ‘name’.
Go to the main.storyboard, Add a text field and a login button. Below that, add a label, double click on it and remove its content. Then, add a logout button, go to its attribute inspector and make ‘alpha’ equal to 0. Now, our view should look as follows −
Now, go to your view controller file, open assistant editor and create connections between the UI Elements and your controller file.
Note − We will also create outlets for both the buttons, because we need to modify the look of those buttons. For example − When a user is logged in, we will hide the login button, if the user is not logged in we will show login and hide the logout button.
As we have already discussed about adding and fetching data from CoreData, we will put the code here.
Try-Catch − You will notice that we have used try-catch block many times in the code. It is because if we do not use the try-catch blocks and there is some exception or error in our program, the execution will stop. Whereas, if we are using the try catch blocks and if some error occurs, then the catch block handles the error. Read more about it in our Swift Tutorial
Code for Login/Logout Application
Let us understand the different components and the code that is used for a login/logout application.
Login Button Action − The following code explains how to add a login button action.
var isLoggedIn = false @IBAction func logIn(_ sender: AnyObject) { let appDelegate = UIApplication.shared.delegate as! AppDelegate let context = appDelegate.persistentContainer.viewContext if isLoggedIn { let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Users") do { let results = try context.fetch(request) if results.count > 0 { for result in results as! [NSManagedObject] { result.setValue(textField.text, forKey: "name") do { try context.save() } catch { print("Update username failed") } } label.text = "Hi " + textField.text! + "!" } } catch { print("Update failed") } } else { let newValue = NSEntityDescription.insertNewObject(forEntityName: "Users", into: context) newValue.setValue(textField.text, forKey: "name") do { try context.save() logInButton.setTitle("Update username", for: []) label.alpha = 1 label.text = "Hi " + textField.text! + "!" isLoggedIn = true logOutButton.alpha = 1 } catch { print("Failed to save") } } }
Logout Button Action − The following code explains how to add a logout button action.
@IBAction func logOut(_ sender: AnyObject) { let appDelegate = UIApplication.shared.delegate as! AppDelegate let context = appDelegate.persistentContainer.viewContext let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Users") do { let results = try context.fetch(request) if results.count > 0 { for result in results as! [NSManagedObject] { context.delete(result) do { try context.save() } catch { print("Individual delete failed") } } label.alpha = 0 logOutButton.alpha = 0 logInButton.setTitle("Login", for: []) isLoggedIn = false textField.alpha = 1 } } catch { print("Delete failed") } }
ViewDidLoad() − The following code explains how to use the ViewDidLoad() function.
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. let appDelegate = UIApplication.shared.delegate as! AppDelegate let context = appDelegate.persistentContainer.viewContext let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Users") request.returnsObjectsAsFaults = false do { let results = try context.fetch(request) for result in results as! [NSManagedObject] { if let username = result.value(forKey: "name") as? String { logInButton.setTitle("Update username", for: []) logOutButton.alpha = 1 label.alpha = 1 label.text = "Hi there " + username + "!" } } } catch { print("Request failed") } }
Remember you had to create an outlet and an action, for both the buttons.
Now, save and run the application. Login, close the application and run it again. It should look as follows.
That is all we will do with CoreData. Using the same concepts, we can build many CoreData applications.
Controlling the Keyboard
In this section, we will learn to control the keyboard behavior. For example – When we click outside a text field after entering some text, the keyboard does not close. Here, we will understand how to control the keyboard.
Keyboard should disappear on clicking outside the input field
This is a simple task, to do this just paste the following code into your viewController file, before the closing the curly braces.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { self.view.endEditing(true) }
By doing this, it will make the keyboard disappear on clicking outside the input field.
Keyboard should disappear on return key tap
To make the keyboard disappear, we should add a new Type for our view Controller. We will also add a text field and create its outlet called textField. Lastly, we will add the UITextFieldDelegate.
We will also control + drag from our input field to the view controller and select delegate from the options that appear.
Then, we will add the following function.
func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() return true }
The final View Controller file should look as follows −
import UIKit class ViewController: UIViewController, UITextFieldDelegate { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { self.view.endEditing(true) } func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() return true } }
Downloading Web Content − Opening Facebook/Google
In this section, we will learn about making an application, which will open Facebook and Google, when each button is pressed respectively. We will also learn the concept of Web Views and App transport layer Security. After this, you will be able to make a browser of your own.
Note − We need an Internet Connection in this application.
Making a Web Application
We will make a new single view application, iOS project. In the Search bar of the object library, we will search for web view, drag it and add it to our View controller in the main.Storyboard.
After adding the web view, we will stretch it to all the corners. The Application UI should look like as follows −
We will open our main.storyboard and view controller by clicking on the assistant editor. We will create an outlet for our webView and action for both the buttons. On load, the application will load yahoo in webView. On clicking google, it should load Google, and on clicking the Facebook button, it should load the Facebook page.
The final view should look as follows −
The following screenshots show how the different screens of our application should look like. If you try to open a web service that is not https, it will show an error and we will have to add an App Transport Layer Security exception in your info.plist file.
”;