Data not appearing in view controller - ios

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)

Related

Pass String Variable Data from one Class/ViewController to Class/ViewController

I have two viewControllers:
1. CheckoutVC
2. DeliveryTimeVC
In DeliveryTimeVC I have the following variable:
class DeliveryTimeVC: UIViewController {
var tableViewDay:String = ""
}
I use the following push to go from CheckoutVC to DeliveryTimeVC:
let storyboard = UIStoryboard(name: Storyboard.DeliveryTimeStoryboard, bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: StoryboardId.DeliveryTimeVC)
navigationController?.pushViewController(controller, animated: true)
In DeliveryTimeVC I have a tableView where on didSelect I go back to CheckoutVC and in didSelect func I have the following code to append my variable before leaving the controller:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableViewDay.removeAll()
tableViewDay.append(cell.weekDayLbl.text!)
navigationController?.popViewController(animated: true)
}
How can I transfer the String variable data from DeliveryTimeVC to another variable in CheckoutVC? Lets say I create a variable in CheckoutVC as:
class CheckoutVC: UIViewController, CartProductCellDelegate {
var tableViewDayTransferedData:String = ""
}
How can I transfer the data from tableViewDay:String to tableViewDayTransferedData:String
well you can use this lines
let storyboard = UIStoryboard(name: "NAMEOFYOURSTORYBOARD", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "IDOFYOURVIEW") as! CheckoutVC
vc.tableViewDayTransferedData = self. tableViewDay
self.navigationController!.pushViewController(vc, animated: true)
Based on #Hassan Shahbazi comment above I was able to change his code slightly and get the final answer that works for me:
protocol DeliveryTimeDelegate: class {
func didGetData(tableViewDayTransferedData: String)
}
class DeliveryTimeVC: UIViewController {
weak var delegate: DeliveryTimeDelegate?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableViewDay.removeAll()
tableViewDay.append(cell.weekDayLbl.text!)
delegate?.didGetData(tableViewDayTransferedData: tableViewDay)
navigationController?.popViewController(animated: true)
}
}
class CheckoutVC: UIViewController, DeliveryTimeDelegate {
func ...() {
let storyboard = UIStoryboard(name: Storyboard.DeliveryTimeStoryboard, bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: StoryboardId.DeliveryTimeVC) as! DeliveryTimeVC
controller.delegate = self
navigationController?.pushViewController(controller, animated: true)
}
func didGetData(tableViewDayTransferedData: String) {
tableViewDayTransferedData = tableViewDayTransferedData
}
}
in the last func you see two same variables; they just have the same name: tableViewDayTransferedData = tableViewDayTransferedData first one is from CheckoutVC. the second one is from the protocol.
There are generally 2 main patterns for data/variable passing in iOS.
Delegate Pattern
The popular pattern for Objective-c days, but still useful and easy to do. You'll need to define a delegate protocol and use it for passing variables.
protocol DeliveryTimeDelegate: class {
func didGetData(tableViewDay: String)
}
class DeliveryTimeVC: UIViewController {
weak var delegate: DeliveryTimeDelegate?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableViewDay.removeAll()
tableViewDay.append(cell.weekDayLbl.text!)
delegate?.didGetData(tableViewDay: "YOUR_VALUE")
navigationController?.popViewController(animated: true)
}
}
class CheckoutVC: UIViewController, CartProductCellDelegate {
func ...() {
let storyboard = UIStoryboard(name: Storyboard.DeliveryTimeStoryboard, bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: StoryboardId.DeliveryTimeVC)
controller.delegate = self
navigationController?.pushViewController(controller, animated: true)
}
}
extension CheckoutVC: DeliveryTimeDelegate {
func didGetData(tableViewDay: String) {
tableViewDayTransferedData = tableViewDay
}
}
Closure Pattern
The pattern could be seen as the swift friendly implementation of delegate. It's important to pay enough attention to memory management and retain cycle issues when you're using closure.
class DeliveryTimeVC: UIViewController {
var onDataTransfered: ((String) -> Void)?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableViewDay.removeAll()
tableViewDay.append(cell.weekDayLbl.text!)
self.onDataTransfered?("YOUR_VALUE")
navigationController?.popViewController(animated: true)
}
}
class CheckoutVC: UIViewController, DeliveryTimeDelegate {
func ...() {
let storyboard = UIStoryboard(name: Storyboard.DeliveryTimeStoryboard, bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: StoryboardId.DeliveryTimeVC)
controller.onDataTransfered = { [weak self] tableViewDayTransferedData in
self?.tableViewDay = tableViewDayTransferedData
}
navigationController?.pushViewController(controller, animated: true)
}
}

Push new view controller programmatically from within didSelectRow method

I'm building a meme generator that employs a tableView and a collectionView for viewing saved memes. When the user taps a cell within the tableView the image should be presented by sliding the view from the previous controller not by presenting modally as a new view, which is the case. Here is my current code
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let meme = memes[indexPath.row]
let detailController = UIStoryboard(name: "DetailsStoryboard", bundle: nil).instantiateViewController(withIdentifier: "DetailsViewController") as! DetailsViewController
present(detailController, animated: true, completion: nil)
detailController.detailsImageView.image = meme.memedImage
}
Everything I am finding is depending on using a segue, which I am not. Can I use a segue within the didSelectRow method? Here is a link to the repo.
You have to use UINavigationController to push the controller.
And second thing you can't set image to DetailsViewController from TableView didSelect method.
Instead you can pass this image to DetailsViewController and set this image in particular controller.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let meme = memes[indexPath.row]
let detailController = UIStoryboard(name: "DetailsStoryboard", bundle: nil).instantiateViewController(withIdentifier: "DetailsViewController") as! DetailsViewController
detailController.memeImage = meme.memedImage //Pass image like this
self.navigationController?.pushViewController(detailController, animated: true)
}
In DetailsViewController do like this
class DetailsViewController: UIViewController {
//Add one memeImage variable
var memeImage: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
detailsImageView.image = memeImage
}
}

fatal error while unwrapping a view controller value

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)
}

Pass Data using a pushViewController?

Using this code I am able to 'segue' to the same instance of my view controller
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "DetailVC")
self.navigationController?.pushViewController(vc, animated: true)
}
However, how do I pass data over? I only know how to pass data using the segue option. When I run the code with this, I get nil errors because the new instantiated view controller cannot read the data.
for example I add here, for detail description you can get the tutorial from here
class SecondViewController: UIViewController {
var myStringValue:String?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
// We will simply print out the value here
print("The value of myStringValue is: \(myStringValue!)")
}
and send the string as
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "DetailVC") as! SecondViewController
vc.myStringValue = "yourvalue"
self.navigationController?.pushViewController(vc, animated: true)
}
First off. This isn't a segue. This is just pushing another view to the stack. And (like Ashley Mills says) this is not the same instance you are currently in. This is a NEW instance of a view controller.
But all you need to do is populate the data. You already have the controller here...
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
// you need to cast this next line to the type of VC.
let vc = storyboard.instantiateViewController(withIdentifier: "DetailVC") as! DetailVC // or whatever it is
// vc is the controller. Just put the properties in it.
vc.thePropertyYouWantToSet = theValue
self.navigationController?.pushViewController(vc, animated: true)
}
Then in your second view controller catch the value like this
class DetailVC: UIViewController {
var thePropertyYouWantToSet = String()
override func viewDidLoad() {
print(thePropertyYouWantToSet)
}
}
What you're using isn't a segue. This is just pushing a NEW instance (not the same one) of view controller onto the nav stack.
To segue, in your storyboard, you can just drag a link from the collection view cell to the view controller, then assign data in the prepareForSegue method…
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let viewController = segue.destinationViewController as? DetailVC {
viewController.someProperty = self.someProperty
}
}
In DetailVC, Create a variable and assign value while you create an object. Check example below:
class DetailVC {
var dataString: String?
}
Pass data like below:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "DetailVC") as! DetailVC
vc.dataString = "PASS THE DATA LIKE THIS"
self.navigationController?.pushViewController(vc, animated: true)
}
If you're following the no storyboard pattern you can do it like this
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let viewController = NextViewController()
viewController.dataInsideNextViewController = "Data to be passed"
navigationController?.pushViewController(viewController, animated: true)
}
In your ViewController1 launch your ViewController2 using these code
Class ViewController1: UIViewController {
var dataFromVC2 = ""
func loadVC2() {
let vc2 = ViewController2()
vc2.dataFromVC1 = "Hi VC2"
vc2delegate = self
navigationController?.pushViewController(vc2, animated: true)
}
}
In your ViewController2, add this code. You can use the delegate property to pass back data from ViewContoller2.
Class ViewController2: UIViewController {
var dataFromVC1: String = ""
weak var delegate: ViewController1!
func passData_toVC1() {
delegate.dataFromVC2 = "How's it going VC1?"
}
}

Push different UIView for each row of UITableView

I'm trying to push a new view when I select a row.
let identifiers:[String] = ["vc1", "vc2", "vc3", "vc4", "vc5"]
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let identifier = identifiers[indexPath.row]
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc: UINavigationController = storyboard.instantiateViewControllerWithIdentifier("\(identifier)") as! UINavigationController
self.presentViewController(vc, animated: true, completion: nil)
}
All the different views called with this tableView have their own class and Storyboard ID.
At the moment I get the error:
Could not cast value of type 'testApp.CalculatorViewController' to 'UINavigationController'
I think you are trying to cast UIViewController to UINavigationController.
Please try this. This may help you.
let identifiers:[String] = ["vc1", "vc2", "vc3", "vc4", "vc5"]
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let identifier = identifiers[indexPath.row]
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc: UIViewController = storyboard.instantiateViewControllerWithIdentifier("\(identifier)") as! UIViewController
self.navigationController?.pushViewController(vc, animated: true, completion: nil)
}

Resources