📜 ⬆️ ⬇️

"Programming a mouse" in Swift part 2 - navigation

And hello again, Khabarovsk!

Developing the theme of the previous article , I write about programming navigation between the windows of your application using a minimum of code. Immediately I want to mention, the article is written for beginners, if you are an experienced developer in this business - it is unlikely she will give you something.


')
So let's get started.

Training


Before you start writing the application itself, create a project

Since most of it can do it, let's do a simple video here:


Filling the starting content


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

For convenience of display, let’s shave our ViewController in size and color it in red.



“Wrap up” the main page in the Tab Bar Controller

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


Ok, now we already have a “menu”, but so far there is no point in it - because there is only one page. Well, let's add a couple more:

Add 2 more ViewControllers

As in the previous article, we drag them onto our Storyboard, simultaneously “squeezing” them in size for better perception. And we will set 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 in 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]. Somehow not friendly, so we change the names of our VCs in the menu - to do this, select the [Item] button at the bottom of the VC that represents 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 menu, without writing a single line of code at all . To check, we will launch our application on the fifth iPhone simulator, first throwing Labels with the description of the current page for greater clarity:


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


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

Nested menus



Since The Navigation Controller and Table View are almost always used together; let's consider this option. In this version, in any case, you will have to write code if there is even 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 “Read more”, using the Container View. It will look a little worse (clutter of built-in controllers), but you can write less code)

Embedding the controller in another controller

To begin with, we need to create a controller that will handle our table. This is the usual TableVC, so there will only be a video:

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


So, now that we have embedded our tablet, we must somehow load the data into it. First, create a class for our TableVC.
Create the 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 we don’t need to associate anything with anything - just redefine the functions we need (number of sections, number of lines in a section, object of the nth row of a section, etc.), in contrast from tableview. And here autocompletion will help us - in words it is not clear, but in the video you can show it well:

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


Now fill in our file 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 should have a 10-line label, check it out:
Everything is working


So, we have a sign. By itself, it does not require the Navigation Controller. To do this, we need a VC, which will display in detail the information on some of the elements of the table.
Create a class for the controller in the file Detail.swift
 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 link it to Detail.swift and MyTableVC. Communication MyTable -> Detail let's call it “ToDetail”


Now edit the file MyTable:
  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
 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 } } 



Demo of the detailed view

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

As you can see, our detail loads 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, 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 header 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 in the detailed view, the bottom menu is shown, although it is not needed there to remove it, put the “Hide Bottom Bar on Push” checkbox in Detail .


The problem is that there is only a “Back” button out of the box, which is used only for simply going back, and if we want to do something before leaving, it should be tied to other buttons.
Imagine that the user can "buy" something. Add two options: through the top navigation bar and through the click of a button.

The purchase itself will be called from the doIt function.
  func doIt() { //  -   id println(id) //  navigationController?.popViewControllerAnimated(true) } 

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

For a button, simply call this function in the TouchUpInside event handler for the button, but for the button in the navigation you 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
 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 the moment. A sample project can be downloaded from here .
Many moments had to be ignored in order not to overload, but if you think that something needs to be added without fail, write to the PM.
I hope that this article will be useful for someone.

Source: https://habr.com/ru/post/244655/


All Articles