performSegue view not in window hierarchy - ios

I'm trying to perform a segue to another view controller with this code:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
placemarkIndex = indexPath.row
self.view.endEditing(true)
self.dismiss(animated: true, completion: nil)
self.performSegue(withIdentifier: K.Segues.cityViewController, sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == K.Segues.cityViewController {
let destinationVC = segue.destination as! CityViewController
if let index = placemarkIndex, let city = cities?[index] {
destinationVC.location = city
}
}
}
the segue works when I search the table view and select the row while searching, however, it returns the following error when clicking on a row without searching action enabled:
Warning: Attempt to present <Simple_Forecast.CityViewController: 0x7fadc9893a00> on <Simple_Forecast.SearchViewController: 0x7fadcc12f390> whose view is not in the window hierarchy!
How can I solve this?
Thank you

Related

Dismiss my view controller but "prepare" the view controller underneath

I have a modally-presented ViewController that has a UITableView inside it. When the user clicks "Days", it shows this view controller (modally). Here's my Storyboard:
What I want to do is, when I click one of the cells in the table view in the modal, I want to dismiss the modal, but also load new data in the root view controller.
How can I have access to the root view controller from a segued view controller?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.dismiss(animated: true, completion: nil)
}
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destinationVC = segue.destination as! DayView
if let indexPath = tableView.indexPathForSelectedRow {
destinationVC.day = days![indexPath.row]
}
}
I want something like what I've written above, but while my modal does get dismissed, but what I want is for somehow the code in the prepare function to fire when a TableViewCell is clicked.
A shorter and simpler method than protocol/delegate is to create a closure:
For sending a String back, In First ViewController:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let viewControllerB = segue.destination as? ViewControllerB {
viewControllerB.callback = { message in
//Do what you want in here!
}
}
}
In ViewControllerB:
var callback : ((String) -> Void)?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
callback?("Your Data")
self.dismiss(animated: true, completion: nil)
}
you are in the right path,you need to add two more functions 1. unwindsegue and one this is not destination its source so you need change to source from destination.
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destinationVC = segue.source as! DayView
if let indexPath = tableView.indexPathForSelectedRow {
destinationVC.day = days![indexPath.row]
}
}
for Reference : How to Pass Data in an Unwind Segue

Prepare for segue in TableViewDelegate methods

[SOLVED]
Let me explain my problem;
I have a bunch of Event object which appears in tableView cells.
And these objects store in events array
var events: [Event]
Also i have another view controller(DetailViewContoller), in that view controller i have a variable for receiving event object from tableview.
import UIKit
class EventDetailViewController: UIViewController {
var event: Event?
override func viewDidLoad() {
super.viewDidLoad()
print(event?.name)
}
When i tapped the cells i want to pass my current event object to variable(indicated on above) in DetailViewController. So i tried this ->
extension EventListViewcontroller:UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Whenever cells tapped, related object needs to pass DetailViewController
performSegue(withIdentifier: K.Segues.eventListToDetail, sender: self)
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == K.Segues.eventListToDetail {
let VC = segue.destination as! EventDetailViewController
VC.event = events[indexPath.row]
}
}
}
But unfortunately after segue performed when i checked the event variable in DetailViewController it was shown as a nil. So question is, how can i pass my tapped event cell to other view controller?
Note: i've found this topic How do you test UIStoryBoard segue is triggered by didSelectRow(at:) but it didn't help my problem...
In your class
class ViewController: EventListViewcontroller {
var selectedIndex: IndexPath? = nil
}
And change your code in didSelect as
extension ViewController:UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Whenever cells tapped, related object needs to pass DetailViewController
self.selectedIndex = indexPath
performSegue(withIdentifier: K.Segues.eventListToDetail, sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let indexPath = self. selectedIndex, segue.identifier == K.Segues.eventListToDetail {
let VC = segue.destination as! EventDetailViewController
VC.event = events[indexPath.row]
}
}
}
Hope this helps

Receiver has no segue with identifier 'goToDetail'

I've double-checked that my identifier is spelled correctly in the storyboard and that the segue is pointed in the right direction, but I keep receiving this error. Could someone take a look at my code to see if I'm missing something?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedData = tableData[indexPath.row]
tableView.deselectRow(at: indexPath, animated: true)
performSegue(withIdentifier: "goToDetail", sender: selectedData)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? DetailViewController {
destination.vocab.text = sender as? String
}
}
Edit:
segue can be used only with if you have a UINavigationController that will handle the navigation. You performing a segue to UINavigationController. UINavigationController should be at the root of your storyboard.
In your didSelectRowAt function change
performSegue(withIdentifier: "goToDetail", sender: selectedData)
To NavigationController Identifier (Note if you do not have identifier already set up in storyboard you will need to do that first)
performSegue(withIdentifier: "goToNavigationConrtoller", sender: selectedData)
And then change the prepareForSegue function to below.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//check destination for segue is Navigation controller
if let navVC = segue.destinationViewController as? UINavigationController {
// get hold of the first viewController.
if let destination = navVC.viewControllers[0] as? DetailViewController {
destination.vocab.text = sender as? String
//all the other code you need
}
}
}

Swift/XCode - Why can't I pass data to the next view controller when a tableViewCell is pressed?

Apologies if this has already been answered elsewhere- I have spent the last two hours trying different things using similar suggestions but I still can't solve it!
Basically, I have a UITableView with custom cells(for different quiz topics) that when pressed, should allow the app to go to the next VC and pass an integer so the next VC knows which quiz to load. In my table view VC I have this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
rowPressed = indexPath.row
print ("rowPressed = \(rowPressed) before VC changes")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.destination is GameVC {
let vc = segue.destination as? GameVC
vc?.toPass = rowPressed
print("I ran...")
} else {
print("You suck")
}
and in my GameVC I have this:
var toPass = Int()
override func viewDidLoad() {
super.viewDidLoad()
print("toPass = \(toPass)")
The console output when run is this:
I ran...
toPass = 0
rowPressed = 3 before VC changes
So it looks like the VC changes before the previous one can send the correct value of toPass. How do I fix this?
Many thanks in advance!
According to the segue description:
For example, if the segue originated from a table view, the sender parameter would identify the table view cell that the user tapped.
So, sender is a UITableViewCell and you can do like below:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let cell = sender as? UITableViewCell else { return }
guard let idx = tableView.indexPath(for: cell) else { return }
guard let vc = segue.destination as? GameVC else { return }
vc.toPass = idx.row
}
The problem of your code was prepare(segue:) was invoked before tableView(didSelectRowAt:).
As suggested you have to call the performSegue method:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// not necessary
//rowPressed = indexPath.row
//print ("rowPressed = \(rowPressed) before VC changes")
performSegueWithIdentifier("Your id", sender: indexPath)
//You can set the identifier in the storyboard, by clicking on the segue
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Your id"{
var vc = segue.destinationViewController as! GameVC
vc.toPass = (sender as! IndexPath).row
}
}
Because prepareForSegue is called before didSelectRowAt, you can also remove the segue from the Storyboard, drag and drop holding ctrl from the UITableViewController's yellow icon in the top to the second ViewController, click on the segue, give it an identifier, so now you can call performSegue:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.pressed = indexPath.row
self.performSegue(withIdentifier: "segue identifier", sender: nil)
}

managing page navigation at swift2

I'm trying to learn page navigation without using "Navigation Controller".
Created a button and function like;
#IBAction func backPressed(sender: AnyObject) {
self.performSegueWithIdentifier("page2ToMain", sender: self)
}
It works fine.
I need to pass data from page 2 to page 3. So, inside "didSelectRowAtIndexPath" I used "performSegueWithIdentifier" and override "prepareForSegue" function as below;
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.selectedRow = self.dataArray[indexPath.row]
self.performSegueWithIdentifier("page2ToPage3", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let nextViewController = segue.destinationViewController as! ViewController3
nextViewController.data = self.selectedRow
print(self.selectedRow)
}
When I click the table cell it works well, jumps to other view controller and I see data variable.
But when I press back button it fails with;
Could not cast value of type 'test3.ViewControllerMain' (0x1045469f0) to 'test3.ViewController3' (0x1045466e0).
How can I solve the problem?
You can test for this with optional binding:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let nextViewController = segue.destinationViewController as? ViewController3 {
nextViewController.data = self.selectedRow
print(self.selectedRow)
}
}
Or look at the segue identifier and then test for that:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "page2ToPage3" {
let nextViewController = segue.destinationViewController as! ViewController3
nextViewController.data = self.selectedRow
print(self.selectedRow)
}
}

Resources