I am trying to pass data from my table which is a xib file to a view controller but for some reason, the code is not working and throwing an error. I have also browsed the internet, yet not got the solution.
viewDidLoad Function
override func viewDidLoad() {
super.viewDidLoad()
dataPass()
self.navigationItem.title = "Landlord List"
propertyTabel.register(UINib(nibName: "PropertyCell", bundle: nil), forCellReuseIdentifier: "Cell")
propertyTabel.dataSource = self
propertyTabel.delegate = self
}
didSelectRowAt Function
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(withIdentifier: "PropertyDetailsVC") as? PropertyDetailsVC
var dict = arrRes[indexPath.row]
prop.userId = nullToNil(value: dict["user_id"]) as? String
prop.id = nullToNil(value: dict["property_id"]) as? String
prop.code = nullToNil(value: dict["property_code"]) as? String
present(vc!, animated: true, completion: nil)
}
Error Screenshot
Add this code in your didSelect method
let yourStoryboardObject = UIStoryboard(name: "yourStoryboardName", bundle: nil)
let vc = yourStoryboardObject?.instantiateViewController(withIdentifier: "PropertyDetailsVC") as? PropertyDetailsVC
Related
I am trying to pass data when select or press cell in TableView, it's working and I can print the result while select that cell.
The problem when the second view shows the data is nil.
her is the code when when select cell:
var feedItems: NSArray = NSArray()
var selectedPlayer : UsersModel = UsersModel()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of feed items
return feedItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Retrieve cell
let cellIdentifier: String = "BasicCell"
let myCell: WinnerTableCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as! WinnerTableCell
// Get the location to be shown
let item: UsersModel = feedItems[indexPath.row] as! UsersModel
// Get references to labels of cell
myCell.lbTextName!.text = item.name
myCell.lbScore!.text = item.score
let imageURL = URL(string: "https://mywebsite.com/image-upload/img/\(item.userImage ?? "nil")")
myCell.userImage.sd_setImage(with: imageURL, placeholderImage: UIImage(named: "nullImageQuestion.png"))
return myCell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedPlayer = feedItems[indexPath.row] as! UsersModel
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "PlayersDetails") as! PlayersDetails
nextViewController.name = selectedPlayer.name ?? "nil"
print("Print When select cell \(selectedPlayer.name ?? "nil")")
}
And her is the code in the second view:
#IBOutlet weak var lbName: UILabel!
#IBOutlet weak var userImage: UIImageView!
var name = ""
var selectedUser : UsersModel?
override func viewDidLoad() {
super.viewDidLoad()
lbName.text = selectedUser?.name
print("Print when second view open via viewdidload \(selectedUser?.name ?? "nil")")
// Do any additional setup after loading the view.
}
I tried to print from the second view but it shows nothing. What mistake I did?
This is what i have out when print:
There is something I can not understand when I print, it start with the secondview?
Note: I am not using Navigation Controller.
Thanks
There are two serious issues:
The main issue is that you assign the name to the name property but you are assigning selectedUser?.name to the label in the destination controller which is of course nil
The second issue has already been fixed by Taimoor: You have to present or push the controller.
So replace didSelect with
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedPlayer = feedItems[indexPath.row] as! UsersModel
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "PlayersDetails") as! PlayersDetails
nextViewController.selectedUser = selectedPlayer
self.present(nextViewController, animated: true)
}
And please never use NSArray in Swift. Declare the data source array
var feedItems = [UsersModel]() // why not just `User`?
and delete all unnecessary type casts.
You have to use pushViewController to take the data to next screen. Just add one more line in didSelectRowAt function as below:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedPlayer = feedItems[indexPath.row] as! UsersModel
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "PlayersDetails") as! PlayersDetails
nextViewController.name = selectedPlayer.name ?? "nil"
print("Selcted \(selectedPlayer.name ?? "nil")")
self.navigationController?.pushViewController(nextViewController, animated: true)
}
This will take your data to next screen.
You can also try to execute "nextViewController.loadViewIfNeeded()"
Because you just created this ViewController, but did not present it, according to your hierarchical relationship, choose to use prensent or push to present it, it will execute "ViewDidload()"
you've given feedItems's array type NSArray() instead to
var feedItems: UsersModel = [ ]
or you assign name to selectedUser?.name of next page so that assign nil value
or that probably wrong
you have to implement at didSelectRowAt
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "PlayersDetails") as! PlayersDetails
nextViewController.name = feedItems[indexPath.row].name as! UsersModel
print("Selcted \(selectedPlayer.name ?? "nil")")
self.navigationController?.pushViewController(nextViewController, animated: true)
}
How i can send data from my table view cell, to another VC when in tap on cell.
My code for fetch data from DB:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! cellRequestsTVC
let good = data[indexPath.row]
name = good["username"] as! String
cell.userNameLable.text = "#\(name)"
area = good["place"] as! String
cell.areaLable.text = "\(area)"
cell.descriptionLable.text = good["description"] as? String
cell.priorityLable.text = "Priority " + ((good["priority"] as? Int)?.description)!
let imageProblems = good["image"] as? PFFile
imageProblems?.getDataInBackground{ (imageData, error)in
if imageData != nil {
let image = UIImage(data: imageData!)
cell.problemImage.image = image
}
}
return cell
}
It's working perfect. But now my target open data from cell in another VC.
Example:
Example
You have to implement UITableViewDelegate
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let good = data[indexPath.row]
// Here you can either perform a segue or push view controller to UINavigationController
//Push View controller
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc : DetailViewController = storyBoard.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
vc.sharedData = data // here you pass data
self.navigationController?.pushViewController(vc, animated: true)
}
In DetailViewController create object of data with required type
var sharedData : [String : Any]! // Take optional variable if object can be nil & here assuming data contains object of type [String : Any]
You can look the documentation here
UITableViewDelegate
Here is my current setup:
MainMenuViewController -> SubMenuViewController -> UserInputViewController -> ResultViewController
All of my view controllers contains a tableView.
When a user taps on a cell in MainMenuViewControlle, it will segue to SubMenuViewController. All fine and dandy.
Here is where it gets tricky, in SubMenuViewController, there are cells that needs to instantiate another SubMenuViewController because the sub menu options can be several levels deep.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let selectedNode = node?.childenNode[indexPath.row] else {
return
}
if selectedNode.isLeaveNode() {
performSegue(withIdentifier: "userInput", sender: self)
} else {
let subMenuViewController = SubMenuViewController(node: selectedNode)
self.navigationController?.pushViewController(subMenuViewController, animated: true)
}
When there are no child nodes, then it will segue to UserInputViewController, but when there are more options, it needs to instantiate another SubMenuViewController and the tableView will populate itself based on cell the user had tapped previously until selectedNode.isLeaveNode() is true (which means there won't be any child nodes).
This problem is when this code runs :
let subMenuViewController = SubMenuViewController(node: selectedNode)
self.navigationController?.pushViewController(subMenuViewController, animated: true)
it give me the following error:
fatal error: unexpectedly found nil while unwrapping an Optional value
From where I registered my cells which is here:
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "SubMenuTableViewCell", bundle: bundle)
tableView.register(nib, forCellReuseIdentifier: "SubMenuCell")
All my tableView cells are instantiated using a xib file, and I have registered my cells in viewDidLoad()
Can anybody see the problem?
UPDATE
Here is the rest of my code:
UIViewController
class SubMenuViewController: UIViewController {
var node: Node?
init(node: Node) {
self.node = node
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.isNavigationBarHidden = false
self.navigationItem.title = node?.value.rawValue
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "SubMenuTableViewCell", bundle: bundle)
tableView.register(nib, forCellReuseIdentifier: "SubMenuCell")
}
}
UITableViewDataSource
extension SubMenuViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return node!.childCount
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("called")
let cell = tableView.dequeueReusableCell(withIdentifier: "SubMenuCell", for: indexPath) as! SubMenuTableViewCell
let desciptionModule = node?.childenNode[indexPath.row].value
let description = Modules.description(module: desciptionModule!)
cell.title.text = description.main
cell.subtitle.text = description.sub
return cell
}
}
UITableViewDelegate
extension SubMenuViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 68
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let selectedNode = node?.childenNode[indexPath.row] else {
return
}
if selectedNode.isLeaveNode() {
performSegue(withIdentifier: "userInput", sender: self)
} else {
let subMenuViewController = SubMenuViewController(node: selectedNode)
self.navigationController?.pushViewController(subMenuViewController, animated: true)
}
}
}
In your viewDidLoad() method, make sure to do the following:
Based on the code you've posted, you might be having issues with this line:
let bundle = Bundle(for: type(of: self))
I would recommend replacing it with the following line:
let bundle = Bundle(forClass: self)
or
let bundle = Bundle.main
If you're still having trouble with this, then try modifying the following lines of code:
let nib = UINib(nibName: "SubMenuTableViewCell", bundle: bundle)
to
let nib = UINib(nibName: "SubMenuTableViewCell", bundle: nil)
In your tableView:cellForRowAtIndexPath UITableViewControllerDelegate method, include the following lines:
var cell = tableView.dequeueReusableCellWithIdentifier("SubMenuCell") as? UITableViewCell
if cell == nil {
tableView.registerNib(UINib(nibName: "SubMenuTableViewCell", bundle: nil), forCellReuseIdentifier: "SubMenuCell")
cell = tableView.dequeueReusableCellWithIdentifier("SubMenuCell") as SubMenuTableViewCell!
}
cell.configure(data: data[indexPath.row])
tableView.reloadData()
return cell
Note
Make sure that the UITableViewCell's reuseIdentifier is "SubMenuCell"
Make sure that the SubMenuTableViewCell.xib file's owner is SubMenuTableViewCell
Make sure the Module does not say "None" (i.e. the Module should be the name of your Project's Target).
Make sure to call tableView.reloadData() in your viewDidLoad() function.
Problem solved
The problem lies in the fact that I instantiated my view controller wrong. The line:
let subMenuViewController = SubMenuViewController(node: selectedNode)
only return a raw object with no outlets, that is why I was getting the optional nil error because there weren't a tableView to start with.
The correct approach would be to instantiate it using your storyboard. Each view controller has a storyboard property as an optional, since my view controller exists in a storyboard, that property will not be nil.
Instead of doing this:
let subMenuViewController = SubMenuViewController(node: selectedNode)
self.navigationController?.pushViewController(subMenuViewController, animated: true)
I actually needed to do this:
let subMenuViewController = storyboard!.instantiateViewController("SubMenuViewController")
That way I know for sure that my subMenuViewController will contain my tableView property which is an IBOutlet from storyboard.
I have a few viewcontrollers in storyboard with certain identifiers and when I try to access them like this:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let mainViewController = sideMenuController!
let vcName = identities[indexPath.row]
let viewController: UIViewController! = storyboard?.instantiateViewController(withIdentifier: vcName)
let navigationController = mainViewController.rootViewController as! NavigationController
navigationController.pushViewController(viewController, animated: true)
mainViewController.hideLeftView(animated: true, completionHandler: nil)
}
The app crashes. This line is detecting that the value is nil. Can anyone explain how this is nil while unwrapping?
navigationController.pushViewController(viewController, animated: true)
After several attempts I managed to get rid off the fatal error. However the view controller still does not appear upon tapping on the table view item.
Original Project -
link
Edited Project -
link
Well, the project is a little "messed up" :-)
First: You need to somehow get "the" UINavigationController (or derived class).
Since your LeftViewController is not on the navigation stack, it does not have a navigationController set. So, you need to get it from the mainViewController:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
print(storyboard)
let mainViewController = sideMenuController!
let vcName = identities[indexPath.row]
let viewController: UIViewController! = storyboard.instantiateViewController(withIdentifier: vcName)
print(viewController)
if let theNavigationController = mainViewController.rootViewController as? UINavigationController {
print("Gotcha")
theNavigationController.pushViewController(viewController!, animated: true)
mainViewController.hideLeftView(animated: true, completionHandler: nil)
}
}
Second, there is still a problem:
The view controller with identifier 'A' is of type EngineTunningParamteres, derived from UINavigationController. You cannot push a navigation controller onto an existing navigation controller. Therefore, we have to change the class, too:
class EngineTunningParamteres: UIViewController {
override func viewDidLoad() {
print("hi") }
}
Now it works. You're welcome.
let viewController: UIViewController!
is an Optional, with this ! you are just telling the compiler "I know this may be nil but don't worry, at run time it will never be nil".
If you want to get rid of this Optional you have to unwrap the storyboard :
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let mainViewController = sideMenuController!
let vcName = identities[indexPath.row]
let viewController = storyboard!.instantiateViewController(withIdentifier: vcName)
let navigationController = mainViewController.rootViewController as! NavigationController
navigationController.pushViewController(viewController, animated: true)
mainViewController.hideLeftView(animated: true, completionHandler: nil)
}
I have been trying everything I could but nothing seems to work. I am trying to pass firebase user information from one view controller to another, but it never appears.
It starts from a table view
The table view is populated with users that are being pulled from my firebase database. When the user selects a cell the following code gets executed:
I reference function "showProfileControllerForUser" as a way to transport the data of the cell that was selected
var mainProfile: userProfileNavigator?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let user1 = self.users[indexPath.row]
self.mainProfile?.showProfileControllerForUser(user3: user1)
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let vc: UINavigationController = storyboard.instantiateViewController(withIdentifier: "mainNavProfile") as! UINavigationController
self.present(vc, animated: true, completion: nil)
}
In "userProfileNavigator" is where the function that was used before is. The function passes the information that was collected and passes that to the final destination.
func showProfileControllerForUser(user3: User){
mainProfile?.user2 = user3
}
Finally, "mainProfile" is where I want the data to be passed to.
var user2: User?{
didSet{
navigationItem.title = user2?.name
}
I could imagine this being very confusing, I can provide anymore information or clarity if needed
attempted to setup table as such:
var mainProfile: mainProfile?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let user1 = self.users[indexPath.row]
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let vc: UIViewController = storyboard.instantiateViewController(withIdentifier: "mainProfile")
self.mainProfile?.user2 = user1
self.present(vc, animated: true, completion: nil)
}
And the ViewController in which the information is being passed as such:
import UIKit
import Firebase
class mainProfile: UIViewController{
#IBOutlet weak var testLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
print("lets see if this is the new user", user2.name as Any)
}
var user2 = User()
}
but it still comes up as "nil"
If I understood your question right, then you have two ViewControllers. One which contain UITableView, and another is ShowProfileControllerForUser.
In ViewController which contain UITableView, code this:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let user1 = self.users[indexPath.row]
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let vc: UINavigationController = storyboard.instantiateViewController(withIdentifier: "mainNavProfile") as! UINavigationController
vc.user3 = user1
self.present(vc, animated: true, completion: nil)
}
Also, I am not sure if you're presenting the right ViewController.
In your ShowProfileControllerForUser.swift,
var user3 = User // declare a variable with proper type.
Now in your viewDidLoad():
override func viewDidLoad() {
super.viewDidLoad()
showProfileControllerForUser(user3)
}
Edit:
var mainProfile: mainProfile?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let user1 = self.users[indexPath.row]
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let vc: UIViewController = storyboard.instantiateViewController(withIdentifier: "mainProfile")
//self.mainProfile?.user2 = user1 // Instead of this line
vc.user2 = user1 // use this line
self.present(vc, animated: true, completion: nil)
}
If you want to open controller with that passed value, you can set that value after instantiating it
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let vc: UINavigationController = storyboard.instantiateViewController(withIdentifier: "mainNavProfile") as! UINavigationController
// this is what your showProfileControllerForUser doing
vc.user2 = user1
self.present(vc, animated: true, completion: nil)