how can I pass data using prepareForSegue in TableView? - swift

I get an error trying to set indexPath: initializer for conditional binding must have optional type
override func prepareForSegue(segue: UIStoryboardSegue,
sender: AnyObject?) {
if segue.identifier == "toStation" {
let detailViewController = segue.destinationViewController
as! toStation
if let indexPath = tableView.indexPathForSelectedRow! {
detailViewController.selectedStation = stationNameArray[indexPath.row]
}
}
}

if let indexPath = tableView.indexPathForSelectedRow!
You are binding indexPathForSelectedRow to indexPath view an if let statement to unwrap the optional indexPathForSelectedRow. This is a good approach. But at the same time you are using the bang statement ! to unwrap the optional. Effectively you are unwrapping the optional twice. Either use ! or an if let statement.

You receive this error because tableView.indexPathForSelectedRow! is no longer an optional value (you've unwrapped it). To fix this error, try removing the force unwrap operator ! at the end of if let.
if let indexPath = tableView.indexPathForSelectedRow { ... }

According to your question subject, I think you are trying to pass Indexpath into prepareForSegue, in your case, you can use didSelectRowAtIndexPath to pass a sender:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("toStation", sender: indexPath);
}
then in prepareForSegue :
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "toStation") {
let detailViewController = segue.destinationViewController
as! toStation
let row = (sender as! NSIndexPath).row; //we know that sender is an NSIndexPath here.
detailViewController.selectedStation = stationNameArray[row]
}
}
Hope this solve your problem.

Related

Object is nil segue in swift

I'm trying to pass an object from one view controller to another, but the receiver receives nil and throws an error saying:
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "sPropSegue" {
if let indexPath = tableView.indexPathForSelectedRow {
let prop = modelProperty.props[indexPath.row]
let dVeiwController = segue.destination as? PDViewController
detailVeiwController?.property = prop
}
}
}
code in the UIViewController
var property: Property!
override func viewDidLoad() {
super.viewDidLoad()
propImage.image = UIImage(named: property.imageName)
priceLable.text = property.price
addressTextView.text = property.address //propImage.image = UIImage()
// Do any additional setup after loading the view.
}
please help me solving this problem.
Thank you in advance!
You need to make sure segue is established from the Source vc ( not the table cell ) to the destination vc for override func prepare(for segue to be called
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier:"sPropSegue",sender:nil)
}
According to my understanding you are not getting property object in your PDViewController.
So try to debug it. Make sure that prop is not nil.
Try executing the same code after commenting viewDidLoad() code.
Also make sure you have self.table.delegate = self in the controller where you are trying to perform segue.

How to fix delayed data on prepare segue?

My data got delayed 1 time when I want to pass the data in my tableview and pass it to another viewcontroller
i'm using prepare for segue.
now, in order to get the right data i need go back to the table view and press the same row
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toRequestCorrection"{
let destinationVC = segue.destination as! RequestCorrectionViewController
if let indexPath = tableView.indexPathForSelectedRow{
destinationVC.Shift = self.correction["Shift"].stringValue
destinationVC.LogDate = self.correction["LogDate"].stringValue
destinationVC.logIn = self.correction["ActualLogIn"].stringValue
destinationVC.logOut = self.correction["ActualLogOut"].stringValue
destinationVC.breakEnd = self.correction["ActualBreakEnd"].stringValue
destinationVC.breakStart = self.correction["ActualBreakStart"].stringValue
destinationVC.overTimeIn = self.correction["ActualOverTimeIn"].stringValue
destinationVC.overTimeOut = self.correction["ActualOverTimeOut"].stringValue
destinationVC.transactionStatusID = self.correction["TransactionStatusID"].intValue
}
}
}
it's should pass the data on the row right after i pressed the row
Additionally, you do not have to use segue, you can instantiate your view controller inside didSelectRowAt method with following code without prepareForSegue.
EDIT: You didn't indicate it's an async task. So, I am using Alamofire with completion handler. I think it will be useful for you.
typealias yourCompletionHandler = (Data?, _ message: String?, _ errorStatusCode: Int?) -> Void
func yourAsyncTask(handler: #escaping yourCompletionHandler) {
let parameters: [String: Any] = ["unitId": 4124]
let url = ""
Alamofire.request(url, method: .get, parameters: parameters)
.responseObject { (response: DataResponse<BaseListResponseParser<YourModel>>) in
switch response.result {
case .success:
if let result = response.result.value {
handler(result.listItems, nil, nil)
}
case .failure(let error):
print(error)
}
}
}
yourAsyncTask { [weak self] (yourModel, error, _) in
DispatchQueue.main.async {
guard let destination = UIStoryboard(name: "Main", bundle: nil)
.instantiateViewController(withIdentifier: "ViewControllerId") as? RequestCorrectionViewController else { return }
destination.Shift = yourModel.shift
navigationController?.pushViewController(destination, animated: true)
}
}
Using that way you do not need to create segue or prepareForSegue method.
You should implement tableVew(_:willSelectRowAt:) method of UITableViewDelegate in your viewController and set your correction property to selected row's value.
class YourSourceViewController: UITableViewDelegate{
var correction: Correction!
func tableView(_ tabelView: UITableView, willSelecRowAt indexPath: IndexPath) -> IndexPath?{
correction = correctionList[indexPath.row]
return indexPath
}
func prepare(for segue: UIStoryBoardSegue, sender: Any?){
//passing data to RequestCorrectionViewController.
//And also you don't need to check if indexPath is nil.
//Because this block will called only any row of tableView is selected.
}
}
Also note that you could do same thing in tableView(_:didSelectRowAt:) method but this method works after performing segues and cause the problem you are encountered too.
Edit
If you think using willSelectRowAt: method is a misusing, you can set your segue's source your viewController (instead of template cell) and set identifier on Storyboard and call it programmatically. like #vadian's said
func tableView(_ tabelView: UITableView, didSelecRowAt indexPath: IndexPath){
correction = correctionList[indexPath.row]
performSegue(withIdentifier "mySegueIdentifier", sender: self)
}

Swift prepare for segue - Send data from one view controller to another

I want to move from one view controller to another and send userId:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("chosenPerson", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "chosenPerson" {
let chosenPerson = segue.destinationViewController as? chosenPersonViewController
let indexPaths = self.collectionView!.indexPathsForSelectedItems()!
let indexPath = indexPaths[0] as NSIndexPath
chosenPerson!.userID = self.usersArray[indexPath.row].userId
}
by clicking I get: "fatal error: unexpectedly found nil while unwrapping an Optional value"
what I do wrong?
If you have given segue in StoryBoard DON'T Call this self.performSegueWithIdentifier("chosenPerson", sender: self) method in didSelectItem
If you have given segue in storyboard override func prepareForSegue - this method calls first after that didSelectItem calls
Please refer storyBoard once (below Image for Sample)
I think problem is in self.usersArray[indexPath.row].userId May this returns nil
Swift2:
self.performSegueWithIdentifier("chosenPerson", sender: self)
Swift3:
self.performSegue(withIdentifier: "chosenPerson", sender: self)
Swift2:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "chosenPerson" {
let chosenPerson = segue.destinationViewController as? chosenPersonViewController
let indexPaths = self.collectionView!.indexPathsForSelectedItems()!
let indexPath = indexPaths[0] as NSIndexPath
chosenPerson!.userID = self.usersArray[indexPath.row].userId //May it found nil please re - check array values
}
Swift3:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "chosenPerson" {
let chosenPerson = segue.destination as! chosenPersonViewController
if let indexPath = collectionView.indexPathForSelectedItem {
chosenPerson!.userID = self.usersArray[indexPath.row].userId //May it found nil please re - check array values
}
}
}
When performing the segue, pass the indexPath as the sender and try using this switch statement. If you see "unknown segue" printed out when you select a cell, the destination controller is not of type chosenPersonViewController.
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("chosenPerson", sender: indexPath)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
switch (segue.destinationViewController, sender) {
case (let controller as chosenPersonViewController, let indexPath as NSIndexPath):
controller.userID = usersArray[indexPath.row].userId
default:
print("unknown segue")
break
}
}
One possible issue could be that in your storyboard you have not set the identifier for your segue to be "chosenPerson". To fix this first make sure you have a segue between your first and second view controllers, which can be created by control dragging one from one view controller to another.
Then make sure the segue has an identifier, specifically: "chosenPerson". To do this: click on the segue between the first two view controllers, then navigate to the attributes tab, and then set the identifier box to be "chosenPerson". Save the storyboard with your changes and now you should be able to call prepareForSegue with that identifier and not experience a fatal error.

Why is my data not passing to my second view controller from a table view?

I'm trying to pass the value to a second view controller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "headToDetail" {
let indexPath = self.tableView.indexPathForSelectedRow!.row
print("SELECTED INDEX \(indexPath)")
let destVC = segue.destinationViewController as! SecondViewController
print("Segue TEst \(self.toPass)")
destVC.vieSegue = self.toPass
}
I've remove the didSelectRowAtIndexPath and just going to use the Segue but when I tap on a row cell I get:
fatal error: unexpectedly found nil while unwrapping an Optional value
This is a major update to the original code.
the problem is, that you are creating a new ViewController at line
var destination = SecondViewController()
but then the controller simply deallocates because nothing is pointing to it (there is no reference to it and it falls out of scope). You haven't pushed into it, nor you performed any segue.
How to fix it? Basically there are two options.
Storyboard
If you're working with storyboards, you have to create a segue between the first ViewController and SecondViewController. You have to give it an identifier.
Then in didSelectRowAtIndexPath you will write
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
let row = indexPath.row
print(fruits[row])
self.toPass = fruits[row]
performSegueWithIdentifier("YOUR_SEGUE_IDENTIFIER", sender: nil)
}
then in prepareForSegue you'll set the property to the destination SecondViewController.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "YOUR_SEGUE_IDENTIFIER" {
let destVC = segue.destinationViewController as! SecondViewController
destVC.vieSegue = self.toPass!
}
}
Manual Push
The second option is to push the controller manually within the didSelectRowAtIndexPath method
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
let row = indexPath.row
print(fruits[row])
let destination = SecondViewController()
destination.vieSegue = fruits[row]
// if you are using navigation controller
navigationController?.pushViewController(destination, animated: true)
// if you want to present it modally
// presentViewController(destination, animated: true, completion: nil)
}
The second version doesn't force you to create segues.
It's empty because you create a new object...
var destination = SecondViewController()
...pass it your data...
destination.vieSegue = self.toPass!
...and then let it go out of scope without showing it, saving it, or doing anything else useful.

prepareForSegue collectionView indexPath using swift

I don't find any good example to do what I want using swift so I allow myself to ask the question.
Im using a collectionView to display PFObjects and I want to send the displayed cell data to a second controller using prepareForSegue.
At this point, Im struggling to make this part of the code works:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "goto_answerquestion"){
var indexpath : NSIndexPath = self.collectionView.indexPathsForSelectedItems()
}
}
this line:
var indexpath : NSIndexPath = self.collectionView.indexPathsForSelectedItems()
triggers the following error:
(UICollectionView, numberOfItemsInSection: Int)-> Int does not have a member named 'indexPathsForSelectedItems'
please let me know if Im using the wrong method, or if you need additional data to have the appropriate overview of the problem.
ANSWER
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "segue_identifier"){
// check for / catch all visible cell(s)
for item in self.collectionView!.visibleCells() as [UICollectionViewCell] {
var indexpath : NSIndexPath = self.collectionView.indexPathForCell(item as CollectionViewCell)!
var cell : CollectionViewCell = self.collectionView!.cellForItemAtIndexPath(indexpath) as CollectionViewCell
// Grab related PFObject
var objectData:PFObject = self.questionData.objectAtIndex(indexpath.row) as PFObject
// Pass PFObject to second ViewController
let theDestination = (segue.destinationViewController as answerPageViewController)
theDestination.questionObject = objectData
}
}
}
If you just are trying to just find the index path of the cell tapped, and not have multiple, you could do this in your prepareForSegue method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let indexPath = collectionView.indexPathForCell(sender as UICollectionViewCell)
// do what you need now with the indexPath
}
sender in this situation is the cell you tapped, so you just need to cast it to UICollectionViewCell (or a custom cell subclass, if you made one).
UPDATE:
Swift 1.2 introduced as! to replace as for this purpose, so to keep with safety, you can try this inside prepareForSegue using multiple bindings:
if let cell = sender as? UICollectionViewCell, indexPath = collectionView.indexPathForCell(cell) {
// use indexPath
}
This may Solve your problem :
var indexPath : NSArray = self.collectionView.indexPathsForSelectedItems()