Xcode Cannot go back to TableViewController in navigation stack - swift

I have controller1 -> TableViewController2 -> TableViewController3 in my storyboard. When I press a button in controller1, I want to jump to TableViewController3 and from there when I select a row, I want to go back to TableViewController2 and get some data and then go back to controller1.
In controller1 instantiate TableViewController3:
if segue.identifier == "MySegue" {
let viewController:TableViewController3 = segue.destination as! TableViewController3
viewController.addLocationToMap = true
}
In TableViewController3 instantiate TableViewController2 like this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if addLocationToMap == true {
let navc:UINavigationController = self.navigationController!
let tvc:UITableViewController = TableViewController2()
let rootView = navc.viewControllers[0]
navc.setViewControllers([rootView, tvc], animated: true)
return
}
In TableViewController2 viewDidLoad, it fails in ViewDidLoad when I try to set a text field value because the textfield is nil. It cannot be because Textfield is already in the view. Looks like the TableViewController2 view never got loaded.
in TableViewController2
override func viewDidLoad() {
super.viewDidLoad()
locationPurposeTextField.text = "sometext"
}
Fails when setting text value because locationPurposeTextField is nil.
How to fix this?
EDITS:
On pressing a button In controller1:
let navc:UINavigationController = self.navigationController!
let alTvc = self.storyboard!.instantiateViewController(withIdentifier: "AddLocationID") as! UITableViewController
navc.pushViewController(alTvc, animated: false)
let cListTvc = self.storyboard!.instantiateViewController(withIdentifier: "ContactListID") as! UITableViewController
navc.pushViewController(cListTvc, animated: true)
The code takes me to TableViewController3 with storyboard ID: ContactListID as desired.
Next, in TableViewController3
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.myDelegate?.userSelectedContact(contactIdentifier: self.contactId, contactFullName: fullName, contactSelected: true)
let navc:UINavigationController = self.navigationController!
let tvc:UITableViewController = self.storyboard!.instantiateViewController(withIdentifier: "AddLocationID") as! UITableViewController
let rootView = navc.viewControllers[0]
navc.setViewControllers([rootView, tvc], animated: true)
}
Takes me to TableViewController2 with storyboard ID: AddLocationID
Based on the delegate sent TableViewController3, in ViewDidappear method of TableViewController2, I set some text in the view and call tableview.realoadData(). It does not load the view.
However if I select a button in TableViewController2 and load TableViewController3 and then comeback to TableViewController2 upon execution of the very same method didSelectRow it reloads the view in TableViewController2
How to fix it, please let me know?

There are a couple of things wrong with your approach.
If you want to go from controller1 to TableViewController3 but have your navigation stack contain controller1 -> TableViewController2 -> TableViewController3, you will have to have the button in controller1 first push TableViewController2 without animation, and then push TableViewController3 with animation. (You won't be able to have your button trigger a segue.)
Second problem: You can't create a copy of your TableViewController2 by just invoking it's initializer (TableViewController2()). When you do that it doesn't load its views from the storyboard. Instead, you should assign a storyboard identifier to it and use the UIStoryboard method instantiateViewController(withIdentifier:) to create a new instance of it.

Related

Open a ViewController if a cell of a TableViewController is clicked

i'm trying to create a view controller programmatically that will be opened if a cell on a table is clicked. i'm using a table view and i'm filling it with a xib.So, i'm stuck at the point of referencing the new view controller from the first one on click on a cell created in the table view via xib.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(withIdentifier: "DViewController") as? DViewController
self.navigationController?.present(vc!, animated: true, completion: nil) //present della view successiva
vc?.name = data[indexPath.row].nome
}
this code allows me to click on the row but when clicked it shows an error "Fatal error: Unexpectedly found nil while unwrapping an Optional value" generated apparently from the self.navigationController?.present(vc!...the vc value result to be nil and i can't figure out why.
THis is the ViewController that i want to open on click and the only thing that it has to do is open and change the tile to the name ow the cell that i've clicked onto in the other ViewController
class DViewController: UITableViewController {
var name = ""
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.topItem?.title = "\(name)"
}
}
Can someone help?i'm new to swift...ty
Based on what you mentioned. You can initialize your viewController without stroyboard, and there is no crash anymore.
let vc = DViewController()
If you present your view controller like what you did, it won’t display the title you wish.
To display the title, there are 2 ways you can do:
Display your new view controller with Pushing style:
let vc = DViewController()
navigationController?.pushViewController(vc, animated: true)
Keep using presenting style, but you need to wrap your view controller in other navigation controller:
let vc = DViewController()
let nav = UINavigationController(rootViewController: vc)
navigationController?.present(nav, animated: true)

De-initialzing a ViewController after dismissal?

I have two viewControllers in my App, the code for the first viewController is as illustrated below:
import UIKit
class firstViewController: UIViewController {
// The below two variables will be passed from the firstViewController to the secondViewController then back again from the secondViewController to the firstViewController:
var selectedRowValue: Int = 0
var selectedSectionValue: Int = 0
let main = UIStoryboard(name: "Main", bundle: nil)
lazy var secondViewController = main.instantiateViewController(withIdentifier: "secondViewController")
override func viewDidLoad() {
super.viewDidLoad()
}
// The below function will be triggered when the user tap on a specific tableView cell detailClosure icon. This is when the needed data get sent from this viewController to the secondViewController:
func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
let secondViewControllerProperties = secondViewController as! secondViewController
secondViewControllerProperties.receivedSelectedSectionValueFromFirstVc = indexPath.section
secondViewControllerProperties.receivedSelectedRowValueFromFirstVc = indexPath.row
// The below is the relevant content of a UILabel inside the tapped tableView cell by the user that get send to the secondViewController for it to be displayed as its NavigationBar title:
secondViewControllerProperties.selectedUniversalBeamSectionDesignation = arrayWithAllDataRelatedToUbsSections.filter({ $0.sectionSerialNumber == "\(arrayWithAllSectionsSerialNumbers[indexPath.section])" }).map({ $0.fullSectionDesignation })[indexPath.row]
self.present(secondViewControllerProperties, animated: true, completion: nil)
}
}
// The below extension inside the firstViewController is used to pass data back from the secondViewController to the firstViewController:
extension firstViewController: ProtocolToPassDataBackwardsFromSecondVcToFirstVc {
func dataToBePassedUsingProtocol(passedSelectedTableSectionNumberFromPreviousVc: Int, passedSelectedTableRowNumberFromPreviousVc: Int) {
self.selectedRowValue = passedSelectedTableRowNumberFromPreviousVc
self. selectedSectionValue = passedSelectedTableSectionNumberFromPreviousVc
}
}
Below is the code inside the second view controller:
import UIKit
class secondViewController: UIViewController {
weak var delegate: ProtocolToPassDataBackwardsFromSecondVcToFirstVc?
// The below variables get their values when the data get passed from the firstViewController to the secondViewController:
var receivedSelectedRowValueFromFirstVc: Int = 0
var receivedSelectedSectionValueFromFirstVc: Int = 0
var selectedUniversalBeamSectionDesignation: String = ""
// Inside the below navigationBar declaration, its labelTitleText will depend on the tapped tableViewCell by the user inside the firstViewController:
lazy var navigationBar = CustomUINavigationBar(navBarLeftButtonTarget: self, navBarLeftButtonSelector: #selector(navigationBarLeftButtonPressed(sender:)), labelTitleText: "UB \(selectedUniversalBeamSectionDesignation)", navBarDelegate: self)
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(navigationBar)
}
// The below gets triggered when the user hit the back button inside the navigationBar of the secondViewController. This is where using the Protocol data get passed back to the firstViewController:
extension secondViewController: UINavigationBarDelegate {
#objc func navigationBarLeftButtonPressed(sender : UIButton) {
if delegate != nil {
delegate?.dataToBePassedUsingProtocol(passedSelectedTableSectionNumberFromPreviousVc: self.selectedTableSectionNumberFromPreviousViewController, passedSelectedTableRowNumberFromPreviousVc: self.selectedTableRowNumberFromPreviousViewController)
}
dismiss(animated: true) {}
}
}
However, what I am noticing is whenever the secondViewController gets dismissed when the user hit on the back button inside the navigationBar of the secondViewController. The secondViewController does not get de-initialized, and therefore, whenever I press on a different cell inside the tableView inside the firstViewController, the navigationBar title that gets displayed inside the secondViewController is still the same as the one displayed when I pressed the first time. Since the secondViewController did not get de-initialzied and thus, I am seeing the same values as the first time it got initialized.
My question is how to de-initialize the secondViewController when it gets dismissed, so that every time I tap on a different cell inside the tableView inside the firstViewController a new secondViewController gets initialized?
Your code generates secondViewController once and reuses it (it's a property).
lazy var secondViewController = main.instantiateViewController(withIdentifier: "secondViewController")
It means it will live until the first view controller is destroyed, and of course - will be reused.
Instead, you should create it as needed.
func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
// Create the second view controller
let secondViewController = main.instantiateViewController(withIdentifier: "secondViewController")
let secondViewControllerProperties = secondViewController as! secondViewController
secondViewControllerProperties.receivedSelectedSectionValueFromFirstVc = indexPath.section
secondViewControllerProperties.receivedSelectedRowValueFromFirstVc = indexPath.row
// The below is the relevant content of a UILabel inside the tapped tableView cell by the user that get send to the secondViewController for it to be displayed as its NavigationBar title:
secondViewControllerProperties.selectedUniversalBeamSectionDesignation = arrayWithAllDataRelatedToUbsSections.filter({ $0.sectionSerialNumber == "\(arrayWithAllSectionsSerialNumbers[indexPath.section])" }).map({ $0.fullSectionDesignation })[indexPath.row]
self.present(secondViewControllerProperties, animated: true, completion: nil)
}
}
Remove the lazy var of course, it is no longer needed.
Also, you could just do:
let secondViewController = main.instantiateViewController(withIdentifier: "secondViewController") as! SecondViewController instead of casting it later, it's a bit cleaner.

pass data to previous view without segue,#IBaction swift

I have a View1 when i click on textbox i am going to view2(table view) to pick a value. I want to send the picked value to view1 for that textbox.
The controls are created programmatically so i am not using segue,IBActions.
I am trying to use the protocol methods still no success. Here is what i have tried.
class DynamicSuperView: UIViewController,UITableViewDelegate,UITableViewDataSource,dropdownDelegate,UITextFieldDelegate
{
func setValue(value:AnyObject)
{
print("dynamic view delegate method executed")
self.labelText = value as! String
//return selectedValue;
}
override func viewDidLoad() {
}
}
The second class is here with delegate method..
protocol dropdownDelegate {
func setValue(value: AnyObject);
}
class testClass: UIViewController,UITableViewDataSource,UITableViewDelegate,UISearchBarDelegate {
var delegate:dropdownDelegate! = nil
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vcName = names[indexPath.row]
print ("Table view cell clicked and value passed: \(vcName)")
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .checkmark
}
self.navigationController?.popViewController(animated: true)
delegate.setValue(value: "Testing delegate")
}
}
Once i select the value in the tableview i am calling the delegate method and trying to pass that value but no success.
I don't want to create the new instance of the previous view controller because i will lose the data already entered by the user, so i am popping the current view controller and going to the previous view controller.
Please suggest if my approach i correct or not?
ERROR:
fatal error: unexpectedly found nil while unwrapping an Optional value
Thank you in advance
Try this:
view func ButtonPressed() {
print("Button Pressed!!") // This will create the new instance of the view controller.
let vc = UIStoryboard(name:"Main", bundle:nil).instantiateViewController(withIdentifier: "Storage") as! testClass
vc.delegate = self
self.navigationController?.pushViewController(vc, animated:true)
}
You are not setting the delegate to first view controller.

I want to send data when I tap a button in tableView Cell

I am implementing a commentView for my app. I have a main view which is tableview contains picture and a button to go comment view.
I want that when user tap comment button in table view, view shows comment view and pass PFObject by prepareforSegue method.
now comment button works, but I have an error from prepareforsegue
here is my code.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "mainToComment") {
let destViewController : CommentVC = segue.destinationViewController as! CommentVC
destViewController.parentObjectID = parentObjectID
let selectedRowIndex = self.tableView.indexPathForSelectedRow
destViewController.object = (postsArray[(selectedRowIndex?.row)!] as? PFObject)
and here is my how my button works.
#IBAction func commentButtonTapped(sender: AnyObject) {
let button = sender as! UIButton
let view = button.superview!
let cell = view.superview as! MainTVCE
let indexPath = tableView.indexPathForCell(cell)
parentObjectID = postsArray[(indexPath?.row)!].objectId!!
when I debug, selectedRowIndex has no value(nil)
I think it cause of I tap button instead of cell.
How can I set indexPath for this?
or
How can I make it work?
I don't know name of your main TableViewCell view controller. Assume that, I name this view controller is MainTableViewCell.
I create a closure in MainTableViewCell:
var didRequestToShowComment:((cell:UITableViewCell) -> ())?
When button comment is tapped:
#IBAction func commentButtonTapped(sender: AnyObject) {
self.didRequestToShowComment?(self) // self is this UITableViewCell
}
In table cellForRowAtIndex... of your main view controller.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
...
mainTableViewCell.didRequestToShowComment = { (cell) in
let indexPath = tableView.indexPathForCell(cell)
let objectToSend = postsArray[indexPath.row] as? PFObject
// Show your Comment view controller here, and set object to send here
}
...
return cell
}

Master-Detail: UINavigatorController vs Storyboard Segue

Scenario: Master(TableView) --> Detail.
Modus Operandi: Select Row --> display DetailVC
As you can see below, I have a MasterVC embedded in a UINavigationController:
I currently display the DetailVC via pushing it into the UINavigationController's VC stack:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("tableView didSelectRowAtIndexPath")
let storyboard = UIStoryboard(name: "Bliss", bundle: nil);
let controller = storyboard.instantiateViewControllerWithIdentifier("DiaryPlayerVC") as DiaryPlayerViewController
self.navigationController?.pushViewController(controller, animated: true)
}
This works fine.
However, the 'prepareForSeque' doesn't fire:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDiaryPlayer" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
let object = objects[indexPath.row] as NSDate
// (segue.destinationViewController as DiaryPlayerViewController).detailItem = object
}
}
}
I understand that I probably have two (2) conflicting paradigms here:
1) Using the UINavigationController vs
2) Using the Storyboard Relationship.
So...
Option 1: it appears that I can remove the Segue link to have a storyboard stand-alone DetailVC.
Option 2: via Segue, I'm assuming I can remove the UINavigatorController from the link.
I'm currently using Option #1, launching the DetailVC via the UINavigationController.
Question: If I choose Option #2, how do I access (launch) the DetailVC ("Diary Player") from the Master's Row and hence, fire the Segue's 'prepareForSegue()'?
Answer: create a segue from the table view cell to the detail view controller.
Your screenshot shows that you already created a segue in your storyboard. Give that segue an identifier in its property inspectory. Then you can simply perform the segue in the didSelectRowAtIndexPath method:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("tableView didSelectRowAtIndexPath")
performSegueWithIdentifier("mySegueIdentifier", sender: nil)
}
Note: Ctrl-drag the segue from the TableViewController icon, not from the TableViewCell.