I seem to be following tutorials to the tee yet I can't get my data to pass to my second view controller without being nil.
I am new to Swift so I'm probably also not using optionals correctly. Here is how I have my code set up.
import UIKit
class DetailsViewController: UIViewController {
var book : PLBook?
override func viewDidLoad() {
super.viewDidLoad()
//View Did Load
print(self.book)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
}
}
Then this is how I am presenting the second view controller and preparing for segue.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("toDetailVC", sender:self)
}
//MARK: Navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "toDetailVC"){
//prepare for segue to the details view controller
if let detailsVC = sender?.destinationViewController as? DetailsViewController {
let indexPath = self.tableView.indexPathForSelectedRow
detailsVC.book = self.books[indexPath!.row]
}
}
}
In the second view controller, the book var is always nil. Any ideas how I can get the data to pass?
Try This:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "toDetailVC") {
//prepare for segue to the details view controller
let detailsVC = segue.destinationViewController as! DetailsViewController
let indexPath = self.tableView.indexPathForSelectedRow
detailsVC.book = self.books[indexPath!.row]
}
You should be using segue.destinationViewController, not sender?.destinationViewController.
Related
[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
I have too much code to paste it all in, so I'm going to be vague. I think I just need a general idea of what to do, and then I'll be able to do it.
I have a beautiful table built with a list of local businesses. When you click on one, it opens up the DetailView, with some great information about the business. I have a MAP button on the DetailView that triggers Thirdview with a Mapview inside of it.
I'm having issues retrieving data from the cells in the TableView in the Thirdview.
My views:
Tableview => DetailView => Thirdview
In my Thirdview, can I call the information from Tableview?
Or is it easier to pass the information from DetailView to Thirdview?
Thanks!
TableView
var objects: [MyObject] = []
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let myObject = objects[indexPath.row]
}
override func prepare(for segue: UIStoryboardSegue, sender: Any) {
if segue.identifier == "mySegue" {
let detailVC = segue.destination as! MyDetailVC
detailVC.dataFromTableView = myObject
}
}
Detail View
var dataFromTableView = MyObject()
override func prepare(for segue: UIStoryboardSegue, sender: Any) {
if segue.identifier == "mySegue" {
let thirdVC = segue.destination as! MyThirdViewController
thirdVC.dataFromDetailView = dataFromTableView
}
}
Third View
var dataFromDetailView = MyObject()
override func viewDidLoad() {
print(dataFromDetailView)
}
What is the right way to segue from a cell in a UITableViewCell to a new view, passing parameter data?
I have specified a segue from my prototype cell in my UITableViewCell, that links it to a new UIVIew and has it set to show (aka push). The new UIView displays, but I am not sure how to make tell the UIView the section/row that was selected or tell it about the object associated with that selection.
An abridged version of my code is shown below:
class MyTableViewController : UITableViewController {
// other code omitted
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let files = filesByDate[filesByDateKeys![indexPath.section]]
self.selectedFile = files![indexPath.row]
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "SegueToFileDetails") {
let destinationViewController = segue.destinationViewController as! FileDetailsViewController
destinationViewController.file = self.selectedFile
}
}
}
class FileDetailsViewController : UIViewController {
var file: NSURL
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
print("File: ", file)
}
}
What I find happening is the tableView() function is called after the prepareForSegue() functon, causing the file attribute to be nil when viewWillAppear() is called.
A little more search turned up this other post, which indicated the right approach. The tableView() function in my question is not needed. Instead (in Swift 2):
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "SegueToFileDetails") {
let destinationViewController = segue.destinationViewController as! FileDetailsViewController
let path = self.tableView.indexPathForSelectedRow
let files = filesByDate[filesByDateKeys![path!.section]]
destinationViewController.file = files![path!.row]
}
}
And if you want to simply do behaviour based on the specific destination class, based on another suggestion:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let destinationViewController = segue.destinationViewController as? FileDetailsViewController {
let path = self.tableView.indexPathForSelectedRow
let files = filesByDate[filesByDateKeys![path!.section]]
destinationViewController.file = files![path!.row]
}
}
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)
}
}
I know what causes it to load twice, but I do not know how to fix the problem. I have looked through many other questions at the same topic, but any of those are matching to my problem.
So here's the code
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
let selectedCell = tableView.cellForRowAtIndexPath(indexPath)! as UITableViewCell
super.performSegueWithIdentifier("TVtoVCSegue", sender: selectedCell)
print("kolm")
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "TVtoVCSegue" {
let otherViewController = segue.destinationViewController as! TTDetailCntr
if let cell = sender as? UITableViewCell, text = cell.textLabel?.text {
otherViewController.CurrentClass = text
}
}
}
As I understand, in tableview function
super.performSegueWithIdentifier("TVtoVCSegue", sender: selectedCell)
Calls out the view to load and the function below itself calls out the view to load,
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
both will load the view so I tried to integrate those methods and I tried to do sth like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let indexPath = tableView.indexPathForSelectedRow! as NSIndexPath
let selectedCell = tableView.cellForRowAtIndexPath(indexPath)! as UITableViewCell
super.performSegueWithIdentifier("TVtoVCSegue", sender: selectedCell)
if segue.identifier == "TVtoVCSegue" {
let otherViewController = segue.destinationViewController as! TTDetailCntr
if let cell = sender as? UITableViewCell, text = cell.textLabel?.text {
otherViewController.CurrentClass = text
}
}
}
But It says an error if I try to access the view that loaded twice before:
The error comes from the line:
super.performSegueWithIdentifier("TVtoVCSegue", sender: selectedCell)
And the error is:
warning: could not load any Objective-C class information. This will significantly reduce the quality of type information available.
(lldb)
The idea of the code is to get the indexPath value and then pass it to another ViewController
The segue that you've created is most probably from a tableViewCell rather than the UIViewController
Delete the segue and create one from the UIViewController instead.
Ctrl + drag from the circle highlighted in the image to your destination VC.
This will solve this problem.
Here is the SecondviewController that receives the passed data:
class TTDetailCntr: UIViewController {
#IBOutlet var ClassIDLbl: UILabel!
#IBOutlet var TeacherIDlbl: UILabel!
var CurrentClass = ""
override func viewDidLoad() {
super.viewDidLoad()
ClassIDLbl.text = CurrentClass
}
write
self.performSegueWithIdentifier
and check instead of
super.performSegueWithIdentifier
Its working fine with this
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
let selectedCell = tableView.cellForRowAtIndexPath(indexPath)! as UITableViewCell
self.performSegueWithIdentifier("TVtoVCSegue", sender: selectedCell)
print("kolm")
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "TVtoVCSegue" {
let otherViewController = segue.destinationViewController as! SecondViewController
if let cell = sender as? UITableViewCell {
otherViewController.CurrentClass = cell.textLabel?.text
}
}
}
SecondControllerClass.swift
class SecondViewController: UIViewController {
var CurrentClass:String!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}