“Mouse Programming” in Swift Part 2 - Navigation

  • Tutorial
Hello again, Khabarovsk!

Developing the theme of the previous article , I am writing about programming navigation between the windows of your application using a minimum of code. I want to mention right away, the article is written for beginners, if you are an experienced developer in this matter, it is unlikely that it will give you anything.



So let's get started.

Training


Before you start writing the application itself, create a project

Because most of them are already able to do this, let's do a simple video here:


Starter Fill


Great, the project is ready.
We want to create several VCs with a choice through the tab bar, so we set the color of the main window, wrap it in a TabViewController and then add a couple more VC (= ViewControllers) there.

For ease of display, squeeze our ViewController in size and “paint” it red



“Wrap” the main page in the Tab Bar Controller

To do this, we select our main page, select Editor -> Embed In -> Tab Bar Controller from the menu


Ok, now we already have a “menu”, but so far it makes no sense - there is only one page. Well, add a couple more:

Add 2 more ViewControllers

As in the previous article, we drag them to our Storyboard, simultaneously “squeezing” them in size for a better perception. And set the colors - for example, yellow and green.


Now add them to our menu

To do this, hold down the right mouse button on the Tab Bar Controller, "drag" the mouse to what you want to add to the menu, and in the list that appears, select Relationship Segue -> view controllers


Last step

Look now at your TabView Controller - there will be something like [Item] [Item] [Item]. It’s somehow not friendly, so we’ll change the names of our VCs in the menu - for this, select the [Item] button at the bottom of the VC representing it in the menu and set the values ​​we need there. For example, I only change the name, but here you can experiment.


Navigation testing

So, we have already created a full-fledged menu without writing a single line of code at all . To check, we will launch our application on the fifth iPhone simulator, having previously thrown Labels with a description of the current page for greater clarity:


As you can see, building a simple menu requires almost no effort. There is only one important point left: the order of the elements in our menu. If you want to swap the menu items, this is also done with a minimum of effort, just drag and drop with the left mouse button pressed in the menu bar in the Tab Bar Controller:


In principle, for many applications this option will be enough, but what if we want to “wrap” one of the elements in the Navigation Controller?

Nested Menus



Because Navigation Controller and Table View are almost always used together, consider just such an option. In this case, in any case, you will have to write code if there is at least the slightest chance that these labels are not constant.

But since laziness is the engine of progress, we use the TableVC already created for us inside our VC "Details", using the Container View. It will look a little worse (a heap of built-in controllers), but you can write less code)

Embedding a controller in another controller

To begin with, we need to create a controller that will process our plate. This is a regular TableVC, so there will be only video:

Next, we will embed our table controller in the “yellow” controller using the Container View. When you create a Container View in your VC, it automatically creates a "child" VC that you want to remove.
Then drag and hold the right mouse button, the line from the Container View to our TableVC, select "embed" from the drop-down list. Now your "child" VC will be drawn in the "parent", trying to fit into its size.
Now the moment that you just need to “make”: stretching the Container View over the entire View and leaving a little space on top for other content (title, for example), select your “yellow” VC and select Editor -> Resolve Auto Layout Issues -> All from the menu Views -> Reset to Suggested Constraints .
A crutch is necessary, because Auto Layout will not be covered in this article.


So, now that we have embedded our plate, we need to somehow load the data into it. First, create a class for our TableVC.
Create a Swift file “MyTable.swift” and write such code in it
Hidden text
import UIKit
class MyTable: UITableViewController {
    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.
    }
}


Then in Stroryboard we select our table controller and change its class to MyTable


Loading data into MyTable

The advantage of our TableViewController is that you don’t need to associate anything with anything - just redefine the functions we need (number of sections, number of rows in a section, object of the nth row of a section, etc.), unlike from TableView. And here auto-completion will help us - it’s not clear in words, but in the video you can show well:

For the tablet, we need a sample row of the table, it can be created from the storyboard like this:


Now fill our file with the code:
MyTable.swift
import UIKit
class MyTable: UITableViewController {
    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 tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        //кол-во элементов для каждой из секций
        return 10
    }
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        //надо вернуть объект для отображения n-ной ячейки таблицы
        let cell = tableView.dequeueReusableCellWithIdentifier("MyCell") as UITableViewCell
        cell.textLabel.text = "Строка #\(indexPath.item)"
        return cell
    }
    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        //а это вызывается если юзер кликнул по какой-то ячейке
    }
    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        //а здесь нао вернуть кол-во секций(для простой таблицы это 1)
        return 1
    }
}


Well, now we have to display a 10-line label, check this:
Everything works


So, we have a tablet. On its own, it does not require a Navigation Controller. To do this, we need a VC that will display information in detail on some of the elements of the plate.
Create a class for the controller in the Detail.swift file
import UIKit
class Detail: UIViewController {
    required init(coder aDecoder: NSCoder) {
        id = 0
        super.init(coder: aDecoder)
    }
    @IBOutlet weak var idDetail: UILabel!//эта перменная связана с лэйблом с текстом "id..."
// получается, как и все, перетаскиванием 
    var id: Int //для передачи id в объект нашего view при переходе
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        idDetail.text = "\(id)"
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}



Create a VC, and bind it to Detail.swift and MyTableVC. Connection MyTable -> Detail will be called “ToDetail”


Now edit the MyTable file:
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {//вызывается перед кадым показом Detail
        let vc = segue.destinationViewController as Detail
        let id = sender as Int//сохраним отправителя в поле такого же типа в Detail
        vc.id = id
    }
    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        //а это вызывается если юзер кликнул по какой-то ячейке
        performSegueWithIdentifier("ToDetail", sender: indexPath.item)//отправителем мы задали номер ячейки, а можно любой объект
    }

As a result, the code will be like this

import UIKit
class MyTable: UITableViewController {
    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 tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        //кол-во элементов для каждой из секций
        return 10
    }
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        //надо вернуть объект для отображения n-ной ячейки таблицы
        let cell = tableView.dequeueReusableCellWithIdentifier("MyCell") as UITableViewCell
        cell.textLabel.text = "Строка #\(indexPath.item)"
        return cell
    }
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {//вызывается перед кадым показом Detail
        let vc = segue.destinationViewController as Detail
        let id = sender as Int//сохраним отправителя в поле такого же типа в Detail
        vc.id = id
    }
    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        //а это вызывается если юзер кликнул по какой-то ячейке
        performSegueWithIdentifier("ToDetail", sender: indexPath.item)//отправителем мы задали номер ячейки? а можно любой объект
    }
    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        //а здесь нао вернуть кол-во секций(для простой таблицы это 1)
        return 1
    }
}



Detailed demo

We have already done everything to simulate data loading when opening Detail. Check in the simulator:

As you can see, our detail is loaded and gets what it should show, but we can’t go back to the list of options. And here you can use the Navigation Controller, and not your bike.

Navigation Controller in Tab Bar


First, the simplest thing is to wrap our “yellow” VC in the NavigationConctroller:

Along the way, we set the navigation title for the “yellow” VC and the text of the back button.

Already not bad, out of the box we can go back without writing code. But the bottom menu is shown in a detailed view, although it is not needed there, to remove it, put the “Hide Bottom Bar on Push” checkbox on Detail


The problem is that out of the box there is only the “Back” button, which is used only for a simple return, and if we want to do something before leaving, we need to attach it to other buttons.
Imagine that the user can "buy" something. We add two options: through the top navigation bar and through a button click.

The purchase itself will be called from the doIt function
    func doIt() {
        //делаем тут что-то с нашим id
        println(id)
        //возвращаемся назад
        navigationController?.popViewControllerAnimated(true)
    }

Pay attention to navigationController? .PopViewControllerAnimated (true) , thanks to this line we go back as if the back button in the navigation bar was pressed.

For a button, the call to this function is simply placed in the TouchUpInside event handler of the button, but for the button in the navigation, you will have to add
        var rightButton = UIBarButtonItem(title: "Купить", style: .Done, target: self, action: "doIt")
        self.navigationItem.rightBarButtonItem = rightButton

in viewDidLoad.
As a result, the code will be like this
import UIKit
class Detail: UIViewController {
    required init(coder aDecoder: NSCoder) {
        id = 0
        super.init(coder: aDecoder)
    }
    @IBOutlet weak var idDetail: UILabel!//чтобы показать, что мы получили id
    var id: Int//для того, чтобы передать этому vc id
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        idDetail.text = "\(id)"
        var rightButton = UIBarButtonItem(title: "Купить", style: .Done, target: self, action: "doIt")
        self.navigationItem.rightBarButtonItem = rightButton
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    @IBAction func buyPressed(sender: AnyObject) {
        doIt()
    }
    func doIt() {
        //делаем тут что-то с нашим id
        println(id)
        //возвращаемся назад
        navigationController?.popViewControllerAnimated(true)
    }
}


Let's look at the final result in the demo.


That's all for now. A sample project can be downloaded from here .
Many points had to be ignored so as not to overload, but if you think that something needs to be added - write to the PM.
Hope this article is helpful to someone.

Only registered users can participate in the survey. Please come in.

Continue to write "beginner's guides"?

  • 84.7% Yes 189
  • 3.5% No, I'm new to 8
  • 6.2% No, I’m an experienced developer, and I think your articles are unnecessary 14
  • 5.3% Change format 12

Also popular now: