I have two segues from one ViewController.
One is supposed to run(crashes for now) when clicking on LogOut Button (Go to LogIn ViewController), another one runs(works good) when clicking on thumbnail of a video.
Since I need to retrieve the video from the server and display it in the ViewController I'm heading to(WatchVideoViewController), I do the code below:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get a reference to a destination View Controller
let detailViewController = segue.destinationViewController as! WatchVideoViewController
// Set the selected video property of the destination view controller
detailViewController.selectedVideo = self.selectedVideo
}
As you might guess this code runs when performing every segue, that's why my LogOut segue crashes.
Could not cast value of type 'AppName.LoginViewController' to 'Appname.WatchVideoViewController'.
Here are pieces of code where I call segues:
#IBAction func pressLogOutButton(sender: AnyObject) {
self.performSegueWithIdentifier("logOutSegue", sender: self)
}
// Handle event when user selects a cell(thumbnail)
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// Take note of which video is selected
self.selectedVideo = self.videos[indexPath.row]
// Call the segue
self.performSegueWithIdentifier("goToVideo", sender: self)
}
Is there any way to avoid running prepareForSegue when calling pressLogOutButton? Thanks a lot!
Check in your prepareForSegue
if segue.identifier == "logOutSegue"
// perform log out logic
Related
I tried to pass the value of the cell that the user clicks on to another view controller.
Here is my code.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
productDisplayed = currentProductList[indexPath.row] as? Product
performSegue(withIdentifier: "ProductDetailSegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if ( segue.identifier == "AddProductSegue"){
let controller: AddProductViewController = segue.destination as! AddProductViewController
controller.delegate = self
}
if ( segue.identifier == "ProductDetailSegue"){
let controller: ProductDetailsViewController = segue.destination as! ProductDetailsViewController
controller.currentProduct = productDisplayed
}
}
Firstly, I didn’t write “performSegue()”.
The issue I met is that the screen will be transferred firstly rather than assign the value to “productDisplayed”, which means the object “currentProduct” in the second VC is nil.
Then I added “performSegue()”.
It still didn’t work. The thing is the second screen was displayed twice. The first time is same with the picture above. And the second time is correct.
But when I tried to click the back button on the left top. It returned to the nil page rather than the ProductDetail page. The screenshot is as follows.
It seems like "prepare" method always being called first then the tableView. How to change the order? If can, this should be fixed I think.
Hope to get your help.
Thank you.
Cause of Problem:
In your storyboard, you may have bound, ProductDetailsViewController with UITableViewCell directly.
It means, upon click/selection of row, it will perform segue (navigation) operation directly.
At the same time, programatically you perform navigation operation using performSegue(withIdentifier: "ProductDetailSegue", sender: self)
Solution:
You have two ways to solve this issue.
I recommend - In your story board, switch segue connection from UITableViewCell to UIViewController (Remove segue connection from UITableViewCell and attach the same segue connection with UIViewController of your tableview)
In your source code, delete/remove this line performSegue(withIdentifier: "ProductDetailSegue", sender: self) and handle data transmission from this function override func prepare(for segue: UIStoryboardSegue, sender: Any?)
I have a UIViewController which should show me DetailInformations depending on what Cell was pressed in the UITableViewController.
For the moment I am passing them through a sequel:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "show" {
var ctrl = segue.destination as! DetailViewController
ctrl.information = _informationList[id]
}
}
The id variable is set through:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
id = indexPath.row
}
Now in my UIViewController I change the information with:
override func viewDidLoad() {
super.viewDidLoad()
setInformation(i: information)
}
Now my problem is, that if I press, lets say cell 2. It switches to the ViewController and shows Information of cell 1. Than I go back to the tableview and I press cell 3. Then it shows me cell 2.
In short, it seems that the viewController is loaded (with the last information), before it sets the new information.
Is there any better way to solve this?
Try using indexPathForSelectedRow in prepareForSegue as of it looks like that you have created segue from UITableViewCell to the Destination ViewController so that prepareForSegue will call before the didSelectRowAt.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "show" {
var ctrl = segue.destination as! DetailViewController
if let indexPath = self.tableView.indexPathForSelectedRow {
ctrl.information = _informationList[indexPath.row]
}
}
}
I am assuming based on what you are describing is that you used a segue in your Storyboard to link directly from the cell to the detail view controller. This is not what you want to do, as mentioned earlier, because you don't get the order of events you would expect. You could use the delegation design pattern for this, but assuming you want to stick with segues you need to make the "show" segue from the table VC itself to the detail VC. You then manually call the segue from the tableView didSelectRowAt code.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
id = indexPath.row
performSegue(withIdentifier: "show", sender: self)
}
Finally, you could then use an unwind segue when you come back to catch any data changes initiated in the detail VC.
i recently made a tableview filled with cells. i was wondering if i can, when i tap a cell display a certain picture instead of a blank screen
i tried linking cells with different image views but i can't do that using a tableview ,so what is the required code
i do know that I'm supposed to use the prepareForSegue statement
override prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {}
i just don't know what to type
i only want to add an image display when i tap one of the cells
thanks for the help
You have to add another view controller in your storyboard which will be opened after tapping the cell.
In tableView(_:didSelectRowAtIndexPath:) you have to remember which cell was tapped, and in prepareForSegue you can pass data to the destination view controller. In destination view controller just load the image based on data passed.
You can use prepareForSegue to pass data from your cell to the next ViewController (the image one).
I would do something similar to this.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.indexTapped = indexPath.row
self.performSegueWithIdentifier("goToImageViewController", sender: self)
}
....
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "goToImageViewController") {
var vc = segue.destinationViewController as! ImageViewController
vc.image = imageDatasource[indexTapped] as! UIImage
}
}
Once you call performSegueWithIdentifier(identifier: String?, sender: AnyObject?) the transition will be automatically handled after executing the prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) code.
I have a Tab Bar application, and one of the tabs, which contains a Table View, segues into a third view when a table cell is pressed. The view controller acts as a delegate for the UITableView, and I trigger the segue programatically as follows:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("cell tapped, starting segue")
performSegueWithIdentifier("showDetails", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
println("prep for segue")
// TODO - more code here
}
Finally, I set up the following code to debug the problem with the third view:
class DetailsViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
println("did load")
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
println("will appear")
}
}
The problem is that when I press a table cell for the first time, the viewWillAppear function never gets called until I interact with the UI in some way (e.g. just a tap anywhere on the screen). The view that I want to segue into doesn't show up, as if the screen didn't get refreshed. However, when I tap the screen, the whole animation runs and I can segue as intended. This is my output when I tap a cell:
cell tapped, starting segue
prep for segue
did load
I tried to find solutions online, but all the issues I found it seems to just not work at all. In my case, it is working, but not immediately.
In case it helps, here's a screenshot of my storyboard:
Sefu found the answer and posted it in the comments, I ran into the same issue and his solution worked for me. The trick is to make it so the cell that is selected that triggers the segue needs to have a selection style set (not None), and I also found that deselecting the cell in tableView:didSelectRowAtIndexPath: also needed to happen.
Ran into a similar problem while having my selectionStyle = .None .
An option you can use, if you're like me and don't want a selectionStyle applied is to set the cell item back to unselected in the prep for segue.
That seemed to stopped the 'issue' I was seeing where the segue would work perfectly once, but all subsequent calls would require selecting the cell twice.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//sending the index path up as the sender so the prep for segue can access the cell
self.performSegueWithIdentifier("segueID", sender: indexPath);
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "segueID"){
if let indexPath : NSIndexPath = sender as? NSIndexPath{
tableViewReference.cellForRowAtIndexPath(indexPath)?.selected = false;
let destinationVC : UIViewControllerClass = segue.destinationViewController as! UIViewControllerClass;
destinationVC.customMethod(/* some value */);
}
}
}
I was following this tutorial http://www.raywenderlich.com/76519/add-table-view-search-swift when I ran into an error. I am adding this feature into an app I was already working on. Once I am in the booths table view, I want to be able to navigate out into the main menu with a button on the navigation bar. Here is the section of code that deals with the segues.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("BoothDetail", sender: tableView)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "BoothDetail" {
let BoothDetailViewController = segue.destinationViewController as UIViewController
if sender as UITableView == self.searchDisplayController!.searchResultsTableView {
let indexPath = self.searchDisplayController!.searchResultsTableView.indexPathForSelectedRow()!
let destinationTitle = self.filteredBooths[indexPath.row].name
BoothDetailViewController.title = destinationTitle
} else {
let indexPath = self.tableView.indexPathForSelectedRow()!
let destinationTitle = self.booths[indexPath.row].name
BoothDetailViewController.title = destinationTitle
}
}
}
}
The error is thrown while trying to use the back button on the booths list that is a direct show segue to the main conference menu. The error is on this line.
if sender as UITableView == self.searchDisplayController!.searchResultsTableView {
You have quite a few problems. Some fatal, some just a headache.
the first headache is you are calling the same segue twice. Both functions call the same segue. Both will execute. Now if you want a double animation, okay. But since one passes data and the other does not, you may have an issue. Eliminate the didSelectRowAtIndexPath function.
In your prepareForSegue method it appears you have two different objects connected to the same segue. A searchDisplayController and a tableView. You want two separate segues. Then your if/else makes changes based on which segue was chosen:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "segue1" {
//code set 1
} else if segue.identifier == "segue2" {
//code set 2
}
}
I had similar problem and I just got it solved. When creating your segue in the tableview do not drag it from the cell, create a manual segue called "BoothDetail"
and connect it to BoothDetailViewController, to create a manual segue select the table view controller and click on "show connection inspector" you will see manual under triggered segue .
My problem the sender was a tableviewcell and not the tableview, so the function
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("BoothDetail", sender: tableView)
}
was never called to pass the tableview controller so when you try to cast it you were getting the error.
good luck