I'm learning swift from couple months and one thing I keep screwing up is passing data between ViewControllers. When I PO the path of the forward looking variable from within the prepareForSegue method, the value is intact. But when the new ViewController actually appears and I checked its value, it is nil at that point. if anyone could point me in the right direction I'd be very appreciative.
class LoginViewController: UIViewController {
var user_ID:String = ""
//this below is within another method activated by button
Auth.auth().signIn(withEmail: email, password: password) { (result, error) in
if error != nil {
print ("there was an error signing in")
print (error!.localizedDescription)
return
}
else {
//go to home screen
let userUID = result?.user.uid
print (userUID)
self.user_ID = userUID
self.performSegue(withIdentifier: "MainSegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//this is the destination VC
let viewController = segue.destination as! ViewController
viewController.user_ID = self.user_ID
//NOTE: if I break here and PO viewConroller.user_ID the value is intact
let homeViewController = (self.storyboard?.instantiateViewController(withIdentifier: "MainVC"))! as! ViewController
//let mainViewController = ViewController()
//mainViewController.user_ID = userUID
self.present(homeViewController, animated: true, completion: nil)
}
class ViewController: UIViewController {
var persons = [Person]()
let db = Firestore.firestore()
var user_ID:String = ""
//NOTE: WHEN `viewdidload` runs value of user_ID is nil
Phillip in the comments above provided the solution. The present ViewController was the problem. The segue already does that. Thank you!!!
Related
I have these codes when I use storyboard:
override func prepare (for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == ProfilePhotoViewController.identifier {
guard let username = usernameTextField.text, let email = emailTextField.text, let password = passwordTextField.text else { return }
let profilePhotoVC = segue.destination as! ProfilePhotoViewController
profilePhotoVC.email = email
profilePhotoVC.username = username
profilePhotoVC.password = password
}
}
How do I write these code if I don't use storyboard and write them programmatically in order to pass the info from current controller to ProfilePhotoViewController?
EDIT:
After doing research I understand that doing this programmatically is through delegate. However, I don't know how I can complete the code:
Need to pass username, email and password from SignUpController ---> profilePhotoViewController
in SignUpController:
protocol SignUpControllerDelegate {
func handleSignUp(//what should I write here?)
}
var delegate: SignUpControllerDelegate?
#objc func handleSignUp() {
//...other code..//
delegate?.handleSignUp(//??)
}
In ProfileViewController:
what should I write to receive the username, email and password info from SignUpController?
We can create a view controller with/without a NIB file.
With NIB, you use init(nibName: String?, bundle: Bundle?) method. In this case, view will be defined using Interface Builder.
// bundle = nil denote the main bundle
let viewController = MyViewController(nibName:"MyViewController", bundle: nil)
Also you can define a custom UIViewController subclass without a NIB file and implement loadView() method.
override func loadView() {
self.view = UIView(...)
}
After that, we need to add the view into the view hierarchy.
self.view.addSubview(viewController.view);
// or
self.present(viewController, animated: false, completion: nil)
// or if we have UINavigationController
self.navigationController?.pushViewController(viewController, animated: false)
Try like this
guard let username = usernameTextField.text, let email = emailTextField.text, let password = passwordTextField.text else { return }
let profileVC = ProfilePhotoViewController()
profileVC.email = email
I have a collection view with some cells representing a contact (their data has a phone number and name) and I am trying to add the contact to the iPhone contacts. I have created a segue from a button called "add contact" that is inside the CollectionViewCell to a navigation controller, and set its identifier as "ADD_CONTACT".
In the storyboard, my segue has a navigation controller with no root view controller.
in prepareToSegue of the view controller that delegates my UICollectionView I wrote this code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == ADD_CONTACT {
let dest = segue.destination as! UINavigationController
if let cell = sender as? SBInstructionCell {
if cell.isContact {
let newContact = CNMutableContact()
if let phone = cell.instructionBean?.contactAttachment?.phoneNumber{
newContact.phoneNumbers.append(CNLabeledValue(label: "home", value: CNPhoneNumber(stringValue: phone)))
}
if let name = cell.instructionBean?.contactAttachment?.contactName {
newContact.givenName.append(name)
}
let contactVC = CNContactViewController(forNewContact: newContact)
contactVC.contactStore = CNContactStore()
contactVC.delegate = self
dest.setViewControllers([contactVC], animated: false)
}
}
}
}
this results with a black screen.
How can this be fixed? I want to see the CNContactViewController
Eventually I solved this in a different approach using Closures.
In my UICollectionViewCell
I added this var:
var closureForContact: (()->())? = nil
Now on my button's action in the same cell I have this func:
#IBAction func addContactTapped(_ sender: UIButton) {
if closureForContact != nil{
closureForContact!()
}
}
Which calls the function.
In my CollectionView in cell for item at index path, I set the closure like this:
cell.closureForContact = {
if cell.isContact {
let newContact = CNMutableContact()
if let phone = cell.instructionBean?.contactAttachment?.phoneNumber{
newContact.phoneNumbers.append(CNLabeledValue(label: "home", value: CNPhoneNumber(stringValue: phone)))
}
if let name = cell.instructionBean?.contactAttachment?.contactName {
newContact.givenName.append(name)
}
let contactVC = CNContactViewController(forNewContact: newContact)
contactVC.contactStore = CNContactStore()
contactVC.delegate = self
contactVC.allowsEditing = true
contactVC.allowsActions = true
if let nav = self.navigationController {
nav.navigationBar.isTranslucent = false
nav.pushViewController(contactVC, animated: true)
}
}
}
This worked perfectly. I learned that for navigating from a cell, it is best to use closures.
I've tried passing data backward from my unwind segue in a number of ways. It seems like the data is not getting sent or its getting sent after viewDidLoad() so the label I'm trying to set isn't getting updated. The unwind segue is working, and below I use prepare for segue with some success to change the title of the previous view controller to 'new title', but the last line isn't setting nbaRotoHome.player to 'new player name'.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "BuyStatsTapPager" {
let nav = segue.destination as! UINavigationController
let buyStatsTapPager = nav.viewControllers[0] as! BuyStatsTabPager
buyStatsTapPager.selectedPlayerBuyStats = selectedPlayer
buyStatsTapPager.buyStatsRef = self
}
if segue.identifier == "unwindToViewController1" {
var viewControllers: [UIViewController] = mainNavigationController.viewControllers as [UIViewController];
if(viewControllers.count == 3){
viewControllers.remove(at: viewControllers.count-2)
mainNavigationController?.viewControllers = viewControllers
}
let enteredContestViewController = viewControllers[viewControllers.count-1]
enteredContestViewController.title = "new title"
self.presentingViewController?.dismiss(animated: true, completion: nil)
let nbaRotoHome = segue.destination as! NBARotoHome
nbaRotoHome.player = "new player name"
}
Back in my previous view controller I have
#IBAction func prepareForUnwind(segue: UIStoryboardSegue) {
}
And after looking at this question
Passing data with unwind segue
I've also tried getting the data this way in the previous view controller
#IBAction func prepareForUnwind(segue: UIStoryboardSegue) {
if let sourceViewController = segue as? BuyStats {
playerNameLabel.text = sourceViewController.playerName
}
}
If I need to add more detail to what I'm trying to do please ask and I will edit. I wanted to ask the question but I am having trouble formulating.
It seems like the data is not getting sent or its getting sent after viewDidLoad() so the label I'm trying to set isn't getting updated.
In an unwind segue you are returning to an already created viewController, so viewDidLoad happened ages ago before you segued to the other viewController.
If you're using segues, you should not be mucking with the array of viewControllers in the navigationController or calling dismiss. The unwind segue will do all of that. Just get the destination in prepare(for:sender:) and set the data:
if segue.identifier == "unwindToViewController1" {
let nbaRotoHome = segue.destination as! NBARotoHome
nbaRotoHome.player = "new player name"
}
or in your prepareForUnwind get the source and read the data:
In this line you are missing .source. Change:
if let sourceViewController = segue as? BuyStats
to:
if let sourceViewController = segue.source as? BuyStats
I want to pass Data from for example ViewController "A" to ViewController "B" and pass another Data from "A" to "C" when for example "OK" button tapped . How can I do that ?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
println("YES")
var mojodiTemp : B = segue.destinationViewController as B println("NO")
mojodiTemp.tempMojoodi = mablagh.text!
var HesabeHarFard : C = segue.destinationViewController as C
HesabeHarFard.person = person
HesabeHarFard.year = year
HesabeHarFard.month = month
HesabeHarFard.day = day
}
There is a detailed example in this post:
Best temporary storage measure
In summary:
use performSegueWithIdentifier and also use prepareForSegue to support it as shown below:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "segueTitle") {
// pass data to next view
let destinationVC = segue.destinationViewController as! YourNextViewController
destinationVC.transferObject = self.transferObject;
}
}
First create dataToPass class variable/property in viewControllerB and C with appropriate dataType and follow the steps below:
SOLUTION 1:
//IN ViewControllerA write following function before creating that you need segues from A to B and A to C with identifiers for each.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "identifierForB" {
let instanceOfViewControllerB = segue.destinationViewController as! ViewControllerB
instanceOfViewControllerB.dataToPass = dataOne
} else if segue.identifier == "identifierForC" {
let instanceOfViewControllerC = segue.destinationViewController as! ViewControllerC
instanceOfViewControllerB.dataToPass = nextData
}
}
SOLUTION 2:
If you go to ViewControllerB or C by code: do like this: (keep like this code in action of OK button pressed)
//First you need to give storyboard id to view controller associated with ViewControllerB and C then in action function of OK button
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
if (showViewControllerB == true) {
let instanceOfViewControllerB = storyBoard.instantiateViewControllerWithIdentifier("ViewControllerBIdentifier") as! ViewControllerB
instanceOfViewControllerB.dataToPass = dataOne
self.presentViewController(instanceOfViewControllerB, animated: true, completion: nil)
} else if showViewControllerC == true {
let instanceOfViewControllerC = storyBoard.instantiateViewControllerWithIdentifier("ViewControllerCIdentifier") as! ViewControllerC
instanceOfViewControllerC.dataToPass = dataOne
self.presentViewController(instanceOfViewControllerC, animated: true, completion: nil)
}
SOLUTION 3
You create two properties in class A:
var vcB: B!
var vcC: C!
keep if just below the definition of class A
now on OK button clicked:
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
vcB = storyBoard.instantiateViewControllerWithIdentifier("ViewControllerBIdentifier") as! ViewControllerB
vcB.dataToPass = dataOne
vcC = storyBoard.instantiateViewControllerWithIdentifier("ViewControllerCIdentifier") as! ViewControllerC
vcC.dataToPass = dataOne
Be sure to present vcC when you want to open C view controller in screen and be sure to present vcB when you want to open B view controller.
when you click button or from trigger to show view of C: just put following code in function/Action:
self.presentViewController(vcC, animated: true, completion: nil)
when you click button or from trigger to show view of B: just put following code in function/Action:
self.presentViewController(vcB, animated: true, completion: nil)
HOPE THIS IS WHAT YOU WANT, Is It?
I have a managedObject that is being passed from 1 view controller to another the first pass works fine but when I try to pass the next object after the relationship has been set it doesn't send anything and comes back as either nil or if I try to use other methods comes back with a syntax error. The code I am using for the view controllers is as follows
View Controller 1, The first object set:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
switch identifier {
case "popOver":
if let VC = segue.destinationViewController as? ClassDeckNameViewController
{
if let ppc = VC.popoverPresentationController {
VC.modalPresentationStyle = UIModalPresentationStyle.Popover
ppc.permittedArrowDirections = UIPopoverArrowDirection.Any
ppc.delegate = self
}
VC.classSave = (sender as! ClassSelection)
}
default: break
}
}
}
#IBAction func buttonPriest(sender: AnyObject) {
let entity = NSEntityDescription.entityForName("ClassSelection", inManagedObjectContext: classMOC!)
let newObject = ClassSelection(entity: entity!,insertIntoManagedObjectContext: classMOC)
newObject.classname = "Priest"
var error: NSError?
if let err = error {
println(err)
} else {
classMOC?.save(&error)
self.performSegueWithIdentifier("popOver", sender: newObject)
}
}
This passes the object without problem to the second view controller but this is the one that won't pass any further to the final presenting controller offering the user the final selections for their "Deck":
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showCardSelection" {
let detailVC: CardSelectionViewController = segue.destinationViewController as! CardSelectionViewController
detailVC.passedDeckObject = (sender as! Deck)
}
}
#IBAction func enterButton(sender: AnyObject) {
let entityDescription = NSEntityDescription.entityForName("Deck",inManagedObjectContext: managedObjectContext!)
let storeDeck = Deck(entity: entityDescription!,insertIntoManagedObjectContext: managedObjectContext)
storeDeck.deckname = usersDeckName.text
storeDeck.classSelected = classSave!
var error: NSError?
managedObjectContext?.save(&error)
if let err = error {
status.text = err.localizedFailureReason
} else {
usersDeckName.text = ""
status.text = "Deck Saved"
self.performSegueWithIdentifier("showCardSelection", sender: storeDeck)
}
}
I made passedDeckObject a variable of type Deck? in the final view controller to set the final relationship methods I know I am doing something wrong but I am unsure what! Any help with this would be amazing!
This looks to be a misconfiguration issue where the segue is being triggered directly in the storyboard rather than calling your code. As such the sender is a button rather than the new entity instance you're expecting.
To fix, disconnect the segue in the storyboard and connect (if it isn't already) the button to your action method in the view controller.