Segue to view details of a previous input

Dear all,

In my continuous struggle to learn how to code I am starting to see some light as, without help, I can complete the lessons and the apps work!


Just now I would like to complete the challenge at the end of Unit 4.9 of 'App Development with Swift'.

The app by itself (a room booking app) works as follows:

- TableView(1) > click '+' > get into another TableView(2) > insert a bunch of data (names, dates, ... ) > click 'Done' button

- TableView(1) now has a row carefully populated with the data I have inserted in TableView(2).


Now, the challenge asks that I create a segue from TableView(1)'s cell that would allow the user to get back to TableView(2) and see the datas (and I assume possibly edit them).

I had no problem creating a functioning segue but I get to an empty TableView(2), instead of to the one I just used.


I wonder if this has something to do with data not being saved but I doubt it because every new entry gets appended into an array.

I can paste hundreds of lines of code but I would prefer to get a hint to the logic needed to get back to my 'detailTableView'.


Is it the reloadView() method?

Thank you for your help and feel free to ask me any other detail I could add to help you help me!

I will keep crunching my brain as I feel something improving!


Thanks!

Accepted Reply

The key problem was that the prepare did not set the properties of the destination controller.


You should also simplify the design: you have set a tableView to display a reservation, with 5 sections and several rows in each section, each with a different cell type which contain a single instance !

That's is not a the best use of table views.


You should better replace this tableView by a simple ViewController, where you would put all the itmes.


I'll send an example of this separately.


Good luck.

Replies

Have you written the prepare(segue:) function ?


in this func, you set the var that will be used by the second controller to load the tableview.


This looks like this:


override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 
       let destVC = segue.destination as! DestViewController // the class of the destination controller
       destVC.var1 =  // set value for each property of destination controller needed for table view
       } 
    }


in the destination controller, you use this var1.... in viewDidLoad

Please give a look at the discussion labelled How to segue back to filled-in version of TableViewController (Swift)? on StackOverflow.

I have already implemented their suggestion and, still the TVC reloads as an unfilled form, while I need it to reload the data it has just passed.


Also, each property of destination controller should be equal to the part of that just saved in the array the tapped cell belongs to.

Well, I will not review all this code.


Please post:

- Your prepare function

- the viewDidload in destinationVC

Forgive me, I didn't ask you to review all that code because it works until I get to the challenge in the book which says as follows:


Update the RegistrationTableViewController with a segue that allows the user to select and view the details of a registration in the AddRegistrationTableViewController.


Your proposed solution makes the app crash because there is no identifier to the segue.

So: my prepare function is just this by now:


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


All the proposed solution do not give the desired result, at least by now.


The viewDidLoad in the AddRegistrationTableViewController:

override func viewDidLoad() {
        super.viewDidLoad()
       
        let midnightToday = Calendar.current.startOfDay(for: Date())
        checkInDatePicker.minimumDate = midnightToday
        checkInDatePicker.date = midnightToday
       
        updateDateViews()
        updateNumberOfGuests()
        updateRoomType()
    }


Thank you for your help.

So you update the dates with a date from calendar ; I guess this is correctly displayed ?


Probably, the other elements of the table

        updateDateViews() 
        updateNumberOfGuests() 
        updateRoomType()


Could you post the code of those functions ? They probably use the var that you need to set in prepare segue.

Please find the code below.

In the meantime I am wondering ... when I first create a new Registration object, where does it get saved?

And if it does get saved, why can't I just call it back and load it into the summoned back TVC?


func updateDateViews() {
        checkOutDatePicker.minimumDate = checkInDatePicker.date.addingTimeInterval(86400)
       
        let dateFormatter = DateFormatter()
        dateFormatter.dateStyle = .medium
       
        checkInDateLabel.text = dateFormatter.string(from: checkInDatePicker.date)
        checkOutDateLabel.text = dateFormatter.string(from: checkOutDatePicker.date)
    }


func updateNumberOfGuests() {
        numberOfAdultsLabel.text = "\(Int(numberOfAdultsStepper.value))"
        numberOfChildrenLabel.text = "\(Int(numberOfChildrenStepper.value))"
    }


func updateRoomType() {
        if let roomType = roomType {
            roomTypeLabel.text = roomType.name
        } else {
            roomTypeLabel.text = "Not Set"
        }
    }


Thank you

I think I see your problem.


In the destination controller, you should define properties to hold the values:

var numberOfAdults : Int = 0          // This will be updated later
var numberOfChildren : Int = 0
var checkInDate = Date()
var checkOutDate = Date()
var roomType : RoomType     // in fact, the type you have defined

Then, in viewDidload of destination VC, you set the outlets with the correct values, such as:


numberOfAdultsStepper.value = numberOfAdults


In fact, you should also use the var elsewhere, such as:


func updateNumberOfGuests() {
        numberOfAdultsLabel.text = String(numberOfAdults)
        numberOfChildrenLabel.text = String(numberOfChildren)
    }


And, in prepare segue, you set the values for all those var, as read from the selectedCell.

Ok, I am going to try this. While I write the rest, could you please also write how the prepare segue should look.

I need to know how to read the properties from the selectedCell.


By now I have only this:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        performSegue(withIdentifier: "ViewRegistrationDetails", sender: indexPath)
    }
   
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "ViewRegistrationDetails" {
            let destinationViewController = segue.destination as? AddRegistrationTableViewController
        }
    }


Thank you

Your segue is starting from the cell when you click, right ?

Sender should be the cell, not the indexPath


Then


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

        if (segue.identifier == "ViewRegistrationDetails") {     // Your segue identifier
            if let cell = sender as? UITableViewCell {
                if let destVC = segue.destination as? DestViewController {     // Your type
                    destVC.numberOfAdults = cell.numberOfAdults    // How have you defined numberOfAdults in cell ?
                    // The same for other var
                }
            }
        }
    }

This is how the cellForRowAt looks like:


override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "RegistrationCell", for: indexPath)
       
        let registration = registrations[indexPath.row]
        let dateFormatter = DateFormatter()
        dateFormatter.dateStyle = .short

        cell.textLabel?.text = registration.firstName + " " + registration.lastName
        cell.detailTextLabel?.text = dateFormatter.string(from: registration.checkInDate) + " - " + registration.roomType.name

        return cell
    }

I guess I should take the data from there if I want to go back.

Where do you store or compute numberOfAdults, numberOfChildren ?


prepare should look like:


    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "ViewRegistrationDetails" {
            if let destinationViewController = segue.destination as? AddRegistrationTableViewController {          // Additional if

             if let selectedRow = tableView.indexPathForSelectedRow.row     {       
               // I suppose you have defined an IBOutlet for the table view ;
               // otherwise, when you select cell, you could save the indexPath.row of the selectedCell
                       let registration = registrations[selectedRow]
                      // set properties here
                }

          }
        }
    }

I'm sorry, I am getting more and more confused (I'm a beginner and trying my best to learn but after just 3 months of learning my head is exploding at this...).


Could you please give a look at the code in the StackOverflow post (that is really all the code except for a TableView that doesn't get used in this case) and tell me what has to be changed or, at least, in which direction I have to look.

All the changes proposed produce more and more errors, though I am sure part of it is because I am not comfortable enough to understand your instructions ...


I can also send you the project if you may give me your e-mail address.

I understand if you do not have the time for this.

After three days of mind-crunching and failure I have almost decided to move on to the next project and come back when I will be able to look at this and just do it.


Thank you

OK, give your mail address and I will reply so that you can send code.

The key problem was that the prepare did not set the properties of the destination controller.


You should also simplify the design: you have set a tableView to display a reservation, with 5 sections and several rows in each section, each with a different cell type which contain a single instance !

That's is not a the best use of table views.


You should better replace this tableView by a simple ViewController, where you would put all the itmes.


I'll send an example of this separately.


Good luck.