I have a UITableView and do performSegueWithIdentifier in the didSelectRowAtIndexPath delegate method.
I am getting this weird bug. Well not sure if it is a bug. When I first open the ViewController with the UITableView everything looks fine (see picture 1 below). But when I click a row and it does the push segue it like graphically smashes the row upward (see picture 2).
When I click the back button from the pushed ViewController, it still stays smashed like that. Never seen this happen before. Why would it be happening?
my code the ViewController that performs the segue
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("showChatVC", sender: self)
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
(appDelegate.getMenuTableVC() as MenuTableViewController).selectedMenuItem = 70
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showChatVC" {
let vc = segue.destinationViewController as ChatViewController
if let selectedItemIndex = tableView.indexPathForSelectedRow()?.row {
vc.partnerUserId = idsWithMessages[selectedItemIndex]
}
}
}
Related
I have 3 VC - VC1, VC2 & VC3
I'm creating an unwind segue where -
VC1 is destination
VC2 is source
So, I've add Marker function in VC1 -
#IBAction func unwindToHomeViewController(_ sender: UIStoryboardSegue) {}
and in VC2 I've created two variable -
var userSelectedPlacesLatitude: Double = 0
var userSelectedPlacesLongitude: Double = 0
which will be updated in tableView -
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.userSelectedPlacesLatitude = suggestedPlacenames[indexPath.row].geometry.coordinates[1]
self.userSelectedPlacesLongitude = suggestedPlacenames[indexPath.row].geometry.coordinates[0]
print("In didSelectRowAt", userSelectedPlacesLatitude, userSelectedPlacesLongitude)
}
and then prepare for segue -
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destinationVC = segue.destination as! VC1
print("In Segue preperation",userSelectedPlacesLatitude, userSelectedPlacesLongitude)
destinationVC.userSelectedPlacesLatitude = self.userSelectedPlacesLatitude
destinationVC.userSelectedPlacesLongitude = self.userSelectedPlacesLongitude
destinationVC.reloadWeatherDataStatus = true
}
But from print value I'm seeing that prepareforSegue is called earlier than didSelectRowAt. Hence I'm not getting expected value in prepareforsugue
In Segue preperation 0.0 0.0
In didSelectRowAt 49.3227937844972 31.3202829593814
Hence 0.0 0.0 is passing all the time to VC1. How can I fix this problem?
The problem you are experiencing results from having at the unwind segue linked directly from the table view cell in your storyboard. As soon as the user taps the row, the unwind segue fires. The didSelectRow(at:) function is called after, but this is too late; You are already back in VC1.
While you can use prepareForSegue to send data to VC1, a better approach is to use the sender passed to unwindToHomeViewController to let VC1 get the data from VC2.
This means that VC2 doesn't need to know anything about VC1. You can also get rid of the reloadWeatherDataStatus property and simply reload the weather data status in the unwind function.
You should:
Remove the segue from the table view row in VC2
In your storyboard, ctrl-drag from the "View controller" icon at the top of VC2 to the "Exit" icon at the top of VC2 and select unwindToHomeViewController
Select the newly created unwind segue and give it an identifier, say unwindToVC1
In VC2 you have
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.userSelectedPlacesLatitude = suggestedPlacenames[indexPath.row].geometry.coordinates[1]
self.userSelectedPlacesLongitude = suggestedPlacenames[indexPath.row].geometry.coordinates[0]
self.performSegue(withIdentifier:"unwindToVC1", sender: self)
}
Remove prepare(for segue: sender:) from VC2
In VC1
#IBAction func unwindToHomeViewController(_ sender: UIStoryboardSegue) {
if let sourceVC = sender.source as? VC2 {
self.userSelectedPlacesLatitude = sourceVC.userSelectedPlacesLatitude
self.userSelectedPlacesLongitude = sourceVC.userSelectedPlacesLongitude
// Do whatever is required to reload the data based on the new location
}
}
Try the code below and let me know if it works -
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let destinationVC = VC1()
destinationVC.userSelectedPlacesLatitude = suggestedPlacenames[indexPath.row].geometry.coordinates[1]
destinationVC.userSelectedPlacesLongitude = suggestedPlacenames[indexPath.row].geometry.coordinates[0]
destinationVC.reloadWeatherDataStatus = true
destinationVC.performSegueWithIdentifier("DestinationSegueName", sender: self)
}
Adding modifications to this answer since some people might have problems with creating the VC instance -
Step 1 - Create a manual segue named "SegueToDestinationVc" from source(VC1) to destination(VC2) view controller and write this code in source view controller -
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "SegueToDestinationVc") {
let vc = segue.destination as! VC2
vc.dataToPass = someData
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
someData = placeName[indexPath.row]
}
Step 2 - In destination view controller(VC2) has a public property named "dataToPass" and use it.
Happy to help, Thanks.
Happy Coding
Let me know if you need any other help.
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.
This question relates to:
SWIFT: Push segue is resulting in a modal segue instead
'Show' segue in Xcode 6 presents the viewcontroller as a modal in iOS 7
I understand this question might be very similar to others. But I have been unable to use some of the answers to solve my issue.
Here is how my storyboard looks:
The viewController has a segmentControl that controls two viewControllers. I now want to segue to the DetailViewController, but it is appearing as modal segue which hides the tabBar and navigationBar.
I have tried deleting and recreating the segue as the some off the answers have suggested but it doesn't solve anything. Is there anything someone could suggest me or direct me to?
Edit:
After testing out the demo that the pod provides I was able to outline the issue I am struggling with. I have implemented the same methods in which it is practically identical. The only difference is that my method for this PageMenu does not use nib files like the demo has done.
In my tableView delegate I am trying to pass a recipe data to the DetailView. This is how my prepareForSegue and didSelect looks:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "detail", sender: recipe)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "detail" {
let vc = segue.destination as! DetailViewController
let indexPath = tableView.indexPathForSelectedRow!
vc.recipe = RecipeManager.shared.recipes[indexPath.row]
}
}
Here is the demo's didSelect:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let newVC : UIViewController = UIViewController()
newVC.view.backgroundColor = UIColor.white
newVC.title = "Favorites"
parentNavigationController!.pushViewController(newVC, animated: true)
}
When comparing it with the demo I am struggling to understand where to implement the parentNavigationController!.pushViewController(newVC, animated: true) which I believe will solve my issue.
Assuming you implemented the parentNavigationController as they did in the demo, you are almost all set.
Delete your existing Segue - you won't be using it.
Give your DetailViewController a Storyboard ID - such as "DetailVC"
Change your code to instantiate the DetailVC and push it onto the parent Nav Controller
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let vc = self.storyboard?.instantiateViewController(withIdentifier: "DetailVC") as? DetailViewController {
vc.recipe = RecipeManager.shared.recipes[indexPath.row]
parentNavigationController!.pushViewController(vc, animated: true)
}
}
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