1

see this image

see this gif

when I choose the city Med , it passed to the TableVC not to the FirstVC (MainVC)

can I do that ? segue to the mainVC with the data passed through the container (TableVC) ?

here what I did so far

MainVC

Empty

TableVC

import UIKit

class passedViewController: UITableViewController {

@IBOutlet weak var passcelltow: UITableViewCell!
@IBOutlet weak var passcell: UITableViewCell!

var passedCity1 = "اختر المدينة الاولى"
var passedCity2 = "اختر المدينة الثانية"
 override func viewDidLoad() {
    super .viewDidLoad()

    passcell.textLabel?.text = passedCity1
    passcelltow.textLabel?.text = passedCity2

}

}

Table 1 with data to pass to the TableVC

import UIKit

class city2ViewController: UIViewController , UITableViewDelegate , UITableViewDataSource{
@IBOutlet weak var tableView: UITableView!

var city2 = ["RUH" , "Med" , "Jed"]

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.delegate = self
    tableView.dataSource = self

}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return city2.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = UITableViewCell()
    print(indexPath.row)

    cell.textLabel?.text = city2[indexPath.row]

    return cell
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    performSegue(withIdentifier: "show", sender: city2[indexPath.row])
}


override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    let passing = segue.destination as! passedViewController

    passing.passedCity2 = sender as! String

}




}

Table 2 is the same ..

commend error

0 1 2 Could not cast value of type 'UIViewController' (0x107a10288) to 'table_view_test_pass.passedViewController' (0x105dbfdf8). (lldb)

Ahmed
  • 61
  • 9

3 Answers3

1

You can pass data via segues or protocols. Since you are using segues i will show you a complete example and how to do it the right way in Swift 3. Using only two ViewControllers.

  1. Create two UITextFields in the main "ViewController".
  2. Create a new view controller of type UIViewController call it "MainTabelViewController" and add a tableView in it. Select content Dynamic prototypes Style Grouped and create 1 prototype cell and add a UILabel to it for the city name. "Don't forget the put the cell identifier name". I called it "cell".
  3. Add the delegates and data sources to the class and add its functions like in code.
  4. Create a segue from the main view controller to the main table view controller. And create another segue the opposite direction. "Don't forget the put the segue identifier names" I called them "toCity" & "toMain"
  5. Create a "CityTableViewCell" controller of type UITableViewCell and create an IBOutlet of UILabel type where you will save the city name in as a text.

Edit this part in the AppDelegate.swift To delete the city names saved using in the UserDefaults every time the app is launched. So i wont populate the UITextFields randomly every time.

import UIKit


@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    var userDefaults: UserDefaults!

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        userDefaults = UserDefaults.standard
        userDefaults.removeObject(forKey: "City One")
        userDefaults.removeObject(forKey: "City Two")

        return true
    }

This is the ordinary main ViewController.swift where you have your UITextFields in. I distinguish which UITextField did the user click on using the tags. You need to add also the UITextFieldDelegate protocol to be able to use the the textFieldDidBeginEditing function. And i also save the selected city names using UserDefaults class to call them when user chooses the other city.

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var cityOneLabel: UITextField!
    @IBOutlet var cityTwoLabel: UITextField!

    @IBOutlet var continueButton: UIButton!

    var selectedCityOne = ""
    var selectedCityTwo = ""

    var userDefaults: UserDefaults!

    override func viewDidLoad() {
        super.viewDidLoad()

        cityOneLabel.delegate = self
        cityTwoLabel.delegate = self

        cityOneLabel.tag = 1
        cityTwoLabel.tag = 2


        continueButton.isEnabled = false


    }

    override func viewDidAppear(_ animated: Bool) {

        userDefaults = UserDefaults.standard

        cityOneLabel.text = selectedCityOne
        cityTwoLabel.text = selectedCityTwo

        if selectedCityOne != "" {
            userDefaults.set(selectedCityOne, forKey: "City One")
        } else {
            cityOneLabel.text = userDefaults.string(forKey: "City One")
        }
        if selectedCityTwo != "" {
            userDefaults.set(selectedCityTwo, forKey: "City Two")
        } else {
            cityTwoLabel.text = userDefaults.string(forKey: "City Two")
        }


    if cityOneLabel.text != "" && cityTwoLabel.text != "" {

            continueButton.isEnabled = true
        } else {
            continueButton.isEnabled = false
        }

    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func continueButtonAction(_ sender: UIButton) {

        //Later on continue after selecting the cities
    }



    func textFieldDidBeginEditing(_ textField: UITextField) {

        performSegue(withIdentifier: "toCity", sender: textField.tag)
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "toCity" {

            guard let cityVC = segue.destination as? MainTableViewController else {
                return
            }

            cityVC.selectedTextField = sender as! Int
        }
    }

}

In the CityTabelViewCell.swift add the IBOutlet UILabel for the city name.

import UIKit

class CityTableViewCell: UITableViewCell {

    @IBOutlet var cityNameLabel: UILabel!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

For the MainTabelViewController.swift write this: Here is where i create an array of strings to populate my table view UILabels with.

import UIKit

class MainTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet var cityTabelView: UITableView!

    var cityNamesArray = ["Cairo", "Alexandria", "Suez"]

    var selectedTextField = Int()

    var selectedCityName = ""

    override func viewDidLoad() {
        super.viewDidLoad()

        cityTabelView.delegate = self
        cityTabelView.dataSource = self

        // Do any additional setup after loading the view.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

     func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CityTableViewCell

        cell.cityNameLabel.text = cityNamesArray[indexPath.row]

        return cell
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return cityNamesArray.count
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        selectedCityName = cityNamesArray[indexPath.row]

        performSegue(withIdentifier: "toMain", sender: self)
    }

    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {

        var title = ""
        if selectedTextField == 1 {
            title = "City One"
        } else if selectedTextField == 2 {
            title = "City Two"
        }

        return title
    }



    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        if segue.identifier == "toMain" {
            guard let mainVC = segue.destination as? ViewController else {
                return
            }
            if selectedTextField == 1 {
                mainVC.selectedCityOne = selectedCityName
            } else if selectedTextField == 2 {
                mainVC.selectedCityTwo = selectedCityName
            }
        }

    }


}

This is how my layout looks like. Try it. I just added a continue button too if the user will have to go to another UIViewController after selecting the two cities. enter image description here

enter image description here

Kegham K.
  • 1,589
  • 22
  • 40
  • in what VC should I add this – Ahmed May 12 '17 at 16:13
  • In the table view VC to go to the MainVC. Just add a global variable and change the code in the didSelectRowAt as i did in the example and then edit the prepare function as i did in the example. – Kegham K. May 12 '17 at 16:14
  • Make sure you have a storyboad segue drawn from the tableVC to the mainVC on the main storyboard with a valid identifier name. – Kegham K. May 12 '17 at 16:24
  • it worked but not going to the first VC (see the gif in the quotation) .. I want to go to the first VC wich called (MainVc) – Ahmed May 12 '17 at 16:25
  • What is the name of the mainVC class name? ViewController.swift? or something else? Did you draw a segue from the table vc you want to segue to and added a unique identifier to it? – Kegham K. May 12 '17 at 16:29
  • thank you for the replay .. no the name is MainVC.swift (I did nothing here) .. the container tableview (static) named city2ViewController.swift (receive the passed data) .. segue and array named passedViewController.swift .. I draw a segue from passedViewController.swift to city2ViewController.swift to pass the data and It worked !! but I want to go to the MainVC – Ahmed May 12 '17 at 16:38
  • I changed the perform segue code and created an instance from the MainVC not the ViewController. Try again. – Kegham K. May 12 '17 at 16:54
  • I notice that and I change it before .. still crashing :( – Ahmed May 12 '17 at 17:00
  • Ok please go to the MainVC.swift and check its class name on the top. Maybe the file name is called MainVC.swift but the class name is different. And also make sure that you have a segue identifier from the tableVC to the MainVC. – Kegham K. May 12 '17 at 17:03
  • no its the same .. class MainVC: UIViewController { I really don't know where is the problem – Ahmed May 12 '17 at 17:06
  • sorry , I can't do this let mainController = segue.destination as! MainVC , because the passedCity2 variable is part of passedViewController.swift – Ahmed May 12 '17 at 17:13
  • Then create the passed city variable in the MainVC. And pass the selected city to it. Or the selected city save it using the UserDefaults class then retrive it when you load or get the MainVC to the foreground. – Kegham K. May 12 '17 at 17:31
  • But I can't do this passcell.textLabel?.text = passedCity1 in the MainVC , because there is no cell in the MainVC , the cell is in the passedViewController.swift , – Ahmed May 12 '17 at 17:41
  • I don't know , is the problem complicated or I am the one who made it complicated < I thing the solution is very simple but what is it – Ahmed May 12 '17 at 17:42
  • @Ahmed the idea is that you don't need the TableVC. You just need 1 Table with the whole Cities and make the user select the city he needs then go back to the MainVC. Then the user clicks choose second city and opens the same cities list without the chosen city 1 included and then he clicks the available city then segues back to the MainVC. Now in the MainVC you have the two chosen cities. "You just need the MainVC & 1 Table with cities array." – Kegham K. May 12 '17 at 20:08
  • Great idea @Kegham , But the problem is I can't add static tableview to a VC , it should be TableViewController so because of that I use the container , so about your idea is there a way to let the user click on TextField and go to the table of cities and when select go back to MainVC ? – Ahmed May 13 '17 at 01:07
  • @Ahmed why do you need a static one? Make a dynamic one and populate the table view with an array of city name strings. I will update the code show you how to do it. – Kegham K. May 15 '17 at 23:21
  • you are a legend! Thank you so much , it worked for me perfectly what I want ,I have been stuck here for three weeks !! thank you again ,, I finished my app in android (Java) but iOS is getting me trouble because I am new to swift – Ahmed May 16 '17 at 22:00
0

If you want to segue to MainVC, you should instantiate a view controller from that class in prepare for segue.

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let passing = segue.destination as! ViewController
    passing.passedCity2 = sender as! String
}

Change ViewController to whatever the name of your class is for MainVC.

Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
  • so the segue will not be to the TableVC ? because I want to pass the data there , and show it through the container in MainVC – Ahmed May 12 '17 at 15:12
  • In your question you said you want to segue into MainVC. What do you mean you want to show data passed to TableVC through MainVC? – Dávid Pásztor May 12 '17 at 15:16
  • I deleted my answer because it was the same as @David's – Kegham K. May 12 '17 at 15:27
  • sorry for misunderstanding , if I create a prepare for segue in (Table 1) to the TableVC , in this case when I select the row It will go to the TableVC with data pass(Worked for me ) .but what i want when I select the row I go to the MainVC and the data that I passed should shown in the container . – Ahmed May 12 '17 at 15:33
  • This is exactly what the code in my answer does, it sets MainVC as the destination of the segue and sends the data to it. – Dávid Pásztor May 12 '17 at 15:37
  • it crashed with this commend error Could not cast value of type 'table_view_test_pass.MainVC' (0x107845ef8) to 'NSString' (0x107c87c60). (lldb) – Ahmed May 12 '17 at 15:39
  • @Dávid Pásztor I add a gif in my question could you please look at it – Ahmed May 12 '17 at 16:04
0

If you want to go back to the Parent View, you should be using an unwind-segue.

For that you must create the unwind segue method in the Parent View like this

@IBAction func unwindSegueFromChild(segue: UIStoryboardSegue){
    // This code executes when returning to view
}

And in your child view you must create the unwind segue ctrl+dragging

enter image description here There a dropdown appears and you select unwindSegueFromChild

Once you've done that, you must assign the unwind segue an identifier and programmatically perform it like a normal segue.

Jose Tomas
  • 107
  • 2
  • 8
  • I add the code to the MainVC and did all things as you say but it crash when I select a row – Ahmed May 12 '17 at 17:05
  • func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { passedCity = city2[indexPath.row] performSegue(withIdentifier: "show", sender: self) } – Ahmed May 12 '17 at 17:26
  • "show" is the identifier for the unwindSegueFromChild – Ahmed May 12 '17 at 17:27
  • when I delete the prepare for segue method unwind segue worked .. but how can I pass data now ? – Ahmed May 12 '17 at 17:47
  • override func prepare(for segue: UIStoryboardSegue, sender: Any?){ let mainController = segue.destination as! passedViewController mainController.passedCity2 = sender as! String } – Ahmed May 12 '17 at 17:47
  • In here http://stackoverflow.com/questions/35313747/passing-data-with-unwind-segue it's detailed how to pass data through an unwind segue. – Jose Tomas May 12 '17 at 17:59