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]
}
}
Related
I'm trying to use a unwind Segue with an ImageView. In my main VC I created this function for the unwind segue:
#IBAction func didUnwindFromSelectIcon(_ sender: UIStoryboardSegue){
guard let imageSelectionImage = sender.source as? SelectIconViewController else {return}
addEatImageViewEat.image = imageSelectionImage.img
}
Then I use this in my second VC
var img: UIImage?
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedImage = collectionView.cellForItem(at: indexPath) as! SelectIconCollectionViewCell
img = selectedImage.selectIconImageView.image
}
I selected the didUnwindFromSelectIcon at the CollectionViewCell on the Storyboard. I get no error or something and I'm not sure what my mistake is. My addEatImageViewEat won't show my selected Image... Hope someone see what's my mistake - Thanks in advance
Update:
MainVC:
#IBAction func didUnwindFromSelectIcon(_ sender: UIStoryboardSegue){
guard let imageSelectionImage = sender.source as? SelectIconViewController else {return}
addEatImageViewEat.image = imageSelectionImage.img
}
SecondVC:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "unwindSegue" {
if let cell = sender as? SelectIconCollectionViewCell {
img = cell.selectIconImageView.image
}
}
}
The problem is that the segue happens before func collectionView(_:didSelectItemAt:) is called.
Instead of using that function, use prepare(for:sender:) to set the image:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "unwindSegue" {
if let cell = sender as? SelectIconCollectionViewCell {
img = cell.selectIconImageView.image
}
}
}
Make sure to set the identifier for your segue and use that in place of "unwindSegue" above. To set the segue identifier, find the unwind segue in the Document Outline view and select it. Then in the Attributes Inspector on the right, set the Storyboard Segue Identifier to your identifier.
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.
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.
}
I'm new in Swift. I want to learn that, how to call another View Controller when the user clicks on cell of Table View.
Thanks in advance.
write following code :
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("showItemDetail", sender: tableView)}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "showItemDetail" {
let indexPath:NSIndexPath = self.tableView.indexPathForSelectedRow()!
let detailVC:ItemDetailViewController = segue.destinationViewController as ItemDetailViewController
detailVC.item = items[indexPath.row] as Item
}
}
add a viewController from storyboard and add a segue from tableview cell to viewController with identifier "showItemDetail";
will navigate too detailViewController
In First View Controller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!)
{
if segue.identifier == "showItemDetail"
{
let indexPath:NSIndexPath = self.tableView.indexPathForSelectedRow()!
let destination = segue.destinationViewController as DetailViewController
destination.name = "llkin Elimov"
}
In second Detail View Controller
public DetailViewController
{
var name = "name" as String //After you created the segue in main.storyboard, in name , you will get your parsed string automatically
}
Hope my code is clear for you :)