Finding nil when prepareForSegue Swift - ios

I am not at all sure why this is happening. When I try to use prefpareForSegue I get "Unexpectedly found nil while unwrapping optional values". I know what it means and where it is happening but I have no clue why.
There error happens on either of the lines marked in my code. I have already tested the values dateText and currentDate and neither are nil.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var DestViewController : CreatePostScrollViewController = segue.destinationViewController as! CreatePostScrollViewController
if dateText != "" {
DestViewController.startTimeDateButton.setTitle(dateText, forState: .Normal)// Error Here
} else if dateText == "" {
DestViewController.startTimeDateButton.setTitle(currentDate, forState: .Normal)// Error Here
}
}
Any help is much appreciated!

When prepareForSegue is called, the user interface elements of the destination controller might not yet be initialized. What you should instead do, is have a field in CreatePostScrollViewController like this:
var buttonTitle: String?;
Then in your viewWillAppearMethod within CreatePostScrollViewController set the button's title to the buttonTitle field you have like this:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated);
if let buttonTitle = buttonTitle {
startTimeDateButton.setTitle(buttonTitle, forState: .Normal);
}
}
Finally to finish the tale in the prepareForSegue do:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var DestViewController : CreatePostScrollViewController = segue.destinationViewController as! CreatePostScrollViewController
if dateText != "" {
DestViewController.buttonTitle = dateText
} else if dateText == "" {
DestViewController.buttonTitle = currentDate
}
}

Related

Why does the value of my variable revert in Swift?

I got my code working, but I don't know why it works.
Having this prepareForSegue Function doesn't work, and when my variable is called in the viewController that the prepareForSegue goes to, it returns nil.
The original VC:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == K.latinSegue {
if latinTextField.text != nil && latinTextField.text != "" {
guard let latinText = latinTextField.text else { fatalError("latinText broken")}
let destinationVC = LatinViewController()
destinationVC.latinText = latinText
}
}
}
The destinationVC:
var latinText: String?
#IBOutlet weak var textLabel: UILabel!
override func viewDidLoad() {
let latinTranslator = LatinTranslator()
let latinDefinition = latinTranslator.getHTMLAndDefinition(latinText: **latinText!**)
textLabel.text = latinDefinition
}
latinText! Doesn't have a value when it is force unwrapped here, even though I set its value in the prepare for segue function.
Now, this code works. I kinda know how static variables work, so I'm just really confused as to why the previous code doesn't work - it throws a nil value not found error.
The originalVC:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == K.latinSegue {
if latinTextField.text != nil && latinTextField.text != "" {
guard let latinText = latinTextField.text else { fatalError("latinText broken")}
LatinViewController.latinText = latinText
}
}
}
The destinationVC:
static var latinText: String!
#IBOutlet weak var textLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let latinTranslator = LatinTranslator()
let latinDefinition = latinTranslator.getHTMLAndDefinition(latinText: LatinViewController.latinText)
textLabel.text = latinDefinition
}
The segue is called when a button is pressed. I have checked, there is only one segue. Please help!
The issue you’re having is that you’re creating a new instance of LatinViewController and not using the instance that your app will actually be navigating to.
Instead of this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == K.latinSegue {
if latinTextField.text != nil && latinTextField.text != "" {
guard let latinText = latinTextField.text else { fatalError("latinText broken")}
let destinationVC = LatinViewController()
destinationVC.latinText = latinText
}
}
}
You should be doing something like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard segue.identifier == K.latinSegue, let destination = segue.destination as? LatinViewController else {
return
}
guard let text = latinTextField.text, !text.isEmpty else {
return
}
destination.latinText = text
}
it return nil because the destinationVC you create new LatinViewController not the viewController of your current segue
let destinationVC = LatinViewController()
change to this:
let destinationVC = segue.destination as? LatinViewController

Can't get the value of a var in another class

I'm trying to get the value of a String var from an another class, but when i'm using it on the new class, the value is empty.
I've got the MainViewController.swift class with :
var movieIDSelected = String()
#IBAction func tapPosterButton(_ sender: UIButton) {
switch sender.tag
{
case 101: movieIDSelected = theaterMovieID[0]
print(movieIDSelected) //The value isn't empty
break
}
}
And the second MovieViewController.swift class with :
var HomeView = ViewPop()
override func viewDidLoad() {
super.viewDidLoad()
let movieID = HomeView.movieIDSelected
print(movieID) //The value is empty
}
With your current code try this in MainVC
if let home = UIApplication.shared.keyWindow?.rootViewController as? ViewPop {
print("home exists ",home.movieIDSelected)
}
//
but you should have only 1 segue to the destinationVC and link it to the VC not a segue for every button , then implement prepareForSegue and fire performSegue inside the button action to make the segue
//
#IBAction func tapPosterButton(_ sender: UIButton) {
switch sender.tag
{
case 101: movieIDSelected = theaterMovieID[0]
print(movieIDSelected) //The value isn't empty
self.performSegue(withIdentifier: "goToNext", sender:1)
break
}
}
//
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let des = segue.destination as! MovieViewController
des.sendedValue = self.movieIDSelected
des.buttonNumber = sender as! Int
}
//
class MovieViewController : UIViewController {
var sendedValue = ""
var buttonNumber = 0 // default value won't affect
}

Swift 3, Successfully passed data but var returns nil when used

So here is my code from VC1 and passing the data to VC2.
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedArtist = artists[indexPath.item]
performSegue(withIdentifier: "artistToArtSegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "artistToArtSegue" {
let artCollectionController = ArtCollectionController()
artCollectionController.artist = selectedArtist
artCollectionController.selectedArtist = selectedArtist
}
}
These codes here in VC2 will print the data
class ArtCollectionController: UICollectionViewController {
var artist = Artist() {
didSet{
print(artist.artistId ?? "did not work")
print(artist.name ?? "what name?")
}
}
var selectedArtist = Artist()
but when I use the the variable in these following test codes in VC2. They return a nil.
func fetchArtForArtist() {
guard let artistId = selectedArtist.artistId else {return}
print(artistId)
let fanRef = FIRDatabase.database().reference().child("art_ref").child(artistId)
fanRef.observeSingleEvent(of: .childAdded, with: { (snapshot) in
let artId = snapshot.key
print(artId)
// let dataRef = FIRDatabase.database().reference().child(artId)
// dataRef.observe(.value, with: { (snapshot) in
// let dictionary = snapshot.value as? [String: AnyObject]
// //let art =
// }, withCancel: nil)
}, withCancel: nil)
}
#IBAction func testButton(_ sender: UIBarButtonItem) {
print(selectedArtist.name ?? "no name")
print(12345)
}
override func viewDidAppear(_ animated: Bool) {
selectedArtist = artist
print(artist.name ?? "non")
print(selectedArtist.artistId ?? "no id")
}
override func viewDidLoad() {
super.viewDidLoad()
fetchArtForArtist()
selectedArtist = artist
print(artist.name ?? "non")
print(selectedArtist.artistId ?? "no id")
}
Im doing this in storyBoard. Im even using 2 vars seeing if there is a difference. I dont understand why the data is successfully passed to VC2 to a couple variables but when the variable is used it returns a nil. Please help.
The other responses are good, but I prefer a slightly different approach:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.destination {
case let artCollectionController as ArtCollectionController:
artCollectionController.artist = selectedArtist
artCollectionController.selectedArtist = selectedArtist
case let otherViewController as OtherViewController:
//Code for some other case
}
}
By using a switch statement, you have a prepareForSegue that will handle multiple different segues cleanly.
The case let construct is a cool trick that only executes that case if the variable in the switch can be case to the desired type. If it can be cast, it creates a local variable of the desired type.
I prefer deciding what code to execute based on the class of the destination view controller because it's less fragile than using the segue identifier. If you forget to set the segue identifier, or add a second segue later to the same type of view controller, or have a typo in the name of the identifier, that code won't work. If you have a typo in your class name, though, the compiler throws an error.
Because you set the artist property on a new instance of ArtCollectionController, which is destroyed upon the exit of the prepareForSegue function:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "artistToArtSegue" {
let artCollectionController = ArtCollectionController() // created
artCollectionController.artist = selectedArtist
artCollectionController.selectedArtist = selectedArtist
// destroyed here
}
}
Try this instead:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "artistToArtSegue",
let artCollectionController = segue.destination as? ArtCollectionController
{
artCollectionController.artist = selectedArtist
artCollectionController.selectedArtist = selectedArtist
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "artistToArtSegue" {
let artCollectionController = segue.destination as! ArtCollectionController
artCollectionController.artist = selectedArtist
artCollectionController.selectedArtist = selectedArtist
}
}
try this, you are creating one more ArtCollectionController instead of passing data to segue one

Cannot assign value of type String to type UILabel

I have marked the part of my code where the problem is, it is commented out. The error message is:
Cannot assign value of type String! to type UILabel!.
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "SendDataSegue" {
if let sendToDetailViewController = segue.destinationViewController as? DetailViewController {
var sendingText = metadataObj.stringValue
sendToDetailViewController.messageLabelDos = sendingText
}
}
}
The label it should be changing is in my DetailViewController and it is a label. The code above is from my original ViewController. How can I make this work?
More code to put in context:
if metadataObj.stringValue != nil {
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("SendDataSegue", sender: self)
}
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "SendDataSegue" {
if let sendToDetailViewController = segue.destinationViewController as? DetailViewController {
var sendingText = metadataObj.stringValue
sendToDetailViewController.viaSegue = sendingText
}
}
}
You need to pass the String instead of setting text to label, because when you correct it and set like this sendToDetailViewController.messageLabelDos.text = sendingText, you will get nil error because messageLabelDos is not initialize yet, so try like this. Create one string instance in DetailViewController and use that inside prepareForSegue for passing String and then use that String instance in viewDidLoad to assign Label to text.
class ViewController: UIViewController {
//Your other methods
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "SendDataSegue" {
if let sendToDetailViewController = segue.destinationViewController as? DetailViewController {
var sendingText = metadataObj.stringValue
sendToDetailViewController.messageDos = sendingText
}
}
}
}
Inside DetailViewController
var messageDos: String = ""
override func viewDidLoad() {
super.viewDidLoad()
self.messageLabelDos.text = messageDos
}

Swift PrepareForSegue NSIndexPath error

I want to prepare my segue via:
override func prepareForSegue(segue: UIStoryboardSegue?, sender: AnyObject?) {
if segue?.identifier != "fromOpenChatsToLogIn" {
if let controller: ChatViewController? = segue?.destinationViewController as? ChatViewController {
if let cell: onlineUserCell? = sender as? onlineUserCell {
let user = OneRoster.userFromRosterAtIndexPath(indexPath: tableView.indexPathForCell(cell!)!)
controller!.recipient = user
}
}
}
}
where onlineUserCell is my custom cell. Also, that's my userFromRosterAtIndexPath:
class func userFromRosterAtIndexPath(indexPath indexPath: NSIndexPath) -> XMPPUserCoreDataStorageObject {
return sharedInstance.fetchedResultsController()!.objectAtIndexPath(indexPath) as! XMPPUserCoreDataStorageObject
}
so, when I select my cell it crashes with:
fatal error: unexpectedly found nil while unwrapping an Optional value
on line:
let user = OneRoster.userFromRosterAtIndexPath(indexPath: tableView.indexPathForCell(cell!)!)
What is wrong? How can I fix it?
First of all, Swift introduced type inference, and the best practices is to use it properly, so write your code like this
if let controller = segue?.destinationViewController as? ChatViewController {
if let cell = sender as? onlineUserCell {
}
}
Also you should set an exception breakpoint in Xcode (here), to see more clearly where you're crashing.
In your case it was in
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
You should use the following statement :
if segue.identifier == "fromOpenChatsToLogIn" {
} else {
}
Your Login ViewController doesn't require an user, therefore it crashed at
let user = OneRoster.userFromRosterAtIndexPath(indexPath: tableView.indexPathForCell(cell)!)
Because there is no cell, and the sender in prepareForSegue is definitely NOT a cell.
For you second issue, the fix was easy.
You are sending OneRoster.userFromRosterAtIndexPath(indexPath: indexPath) in
performSegueWithIdentifier("fromUsersListToChatView", sender: OneRoster.userFromRosterAtIndexPath(indexPath: indexPath))
So you don't need a cell or anything since you already have the user object !
Set the recipient like this :
controller?.recipient = sender as? XMPPUserCoreDataStorageObject
Just replace your prepareForSegue method to the following and it will work:
if segue.identifier == "fromOpenChatsToLogIn" {
} else {
let controller = segue.destinationViewController as? ChatViewController
controller?.recipient = sender as? XMPPUserCoreDataStorageObject
}
Just check, wether your cell is nil or your userFromRosterAtIndexPath return nil
Check casting like this.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "PhotoScrollerViewController" {
if let controller = segue.destinationViewController as? PhotoScrollerViewController {
var cell:UICollectionViewCell = (sender as? UICollectionViewCell)!
//Code here
}
}
}
My advise would to set tag on each cell and then in your prepareForSegue method, get the cell, get the tag and then tweak the code for userFromRosterAtIndexPath to take that tag instead. That tag value could be row number of the cell. See if this makes sense in your context.

Resources