I have a custom UICollection View Cell. One of that cell's property's is cellImageView. When the user selects a cell, I want to pass the image from that view (cellImageView.image) to the next one. Here's what I've tried:
if let indexPath = self.collectionView?.indexPathForCell(sender as! UICollectionViewCell)
{
if segue.identifier == "show"
{
var segue = segue.destinationViewController as! ViewPhoto
segue.imageURL = URLToPass
}
}
but quickly realized that's the wrong route to take. What's the best way to go about doing this? Thanks in advance.
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
if let cell = self.collectionView.cellForItemAtIndexPath(indexPath) as? GalleryClass
{
let imageToPass = cell.cellImageView.image
self.performSegueWithIdentifier(mySegue, sender: imageToPass)
}
}
And in your prepareForSegue function:
if segue.identifier == "show"
{
var segue = segue.destinationViewController as! ViewPhoto
// segue.imageURL = URLToPass
segue.image = sender as! UIImage
}
This way you're keeping code which contains CollectionView stuff in the own delegate method, which is easier to maintain if you have to change it in the future. And you only pass data in your prepareForSegue function which you actually need in there.
Related
I am having a huge problem with my code. My question is, how can I get the data from the tableviewcell the user clicks on to a detail viewcontroller.
I am already getting the data out of an array and displaying it in my tableviewcontroller, but how can I pass that data through to a new viewcontroller using prepareForSegue?
This is my code for displaying data in the cells.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
let cellIdentifier = "MealTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! MealTableViewCell
// Fetches the appropriate meal for the data source layout.
let meal = meals[indexPath.row]
cell.nameLabel.text = meal.name
cell.photoImageView.image = meal.photo
cell.descriptionLabel.text = meal.description
cell.servedWithLabel.text = meal.servedWith
return cell
}
The problem is, that calling a prepareForSegue outside of that means that I cannot use for instance meal.name to pass the name through to the detail viewcontroller? What to do?
Hope you guys can help me - I have been struggling with this for some time now :-(
As pointed out by Arun, you need to use -prepareForSegue. Here's what I'd do to pass something to your detail view controller (say meal.name)
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Use this to determine the selected index path. You can also store the selected index path in a variable using -didSelectRowAtIndexPath
let cell = sender as! UITableViewCell
let indexPath = self.tableView.indexPathForCell(cell)
// Get the relevant detail
let meal = meals[indexPath.row]
// Create reference and pass it
let detailViewController = segue.destinationViewController as! MyDetailViewController
detailViewController.mealName = meal.name
}
And eventually, just hook up the UITableViewCell with MyDetailViewController using your Storyboard (and select whatever presentation and transition style you prefer)
Edited for Swift 4
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let cell = sender as! UITableViewCell
let indexPath = self.tableView.indexPath(for: cell)
// Get the relevant detail
let meal = meals[indexPath!.row]
// Create reference and pass it
let detailViewController = segue.destination as! MyDetailViewController
detailViewController.mealName = meal.name
}
When you click a cell. It will call didSelectRow. In didSelectRow save the indexPath.row and do performseguewithidentifier
Now in prepareForSegue get the object from the array using meals[selectedIndex] and assign to detailViewController Objects.
Make sure segue is from ViewController to ViewController.
I want to populate the prototype cells in a table view with information that the user has typed into a text field in a previous view controller. I want to set the title and subtitle of each cell using this data instead of a pre-determined array. How can I pass data from the text field inside a view controller to the title/subtitle section of a table cell view?
Any help is appreciated.
First of all implement this at the point where you want to segue to your TableViewController:
let textFieldString = yourTextField.text ?? ""
performSegueWithIdentifier("exampleID", sender: textFieldString)
Now create the right segue in your StoryBoard!
1) From the whole ViewController to your destination ViewController
2) And don't forget your unique segue-ID
The delegate method prepareForSegue(...) will be called before you perform the segue. In this method you prepare your destination ViewController.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
let targetvc = segue.destinationViewController as? YourTableViewController
if let targetvc = targetvc
{
let yourTextFieldString = sender as? String
if let yourTextFieldString = yourTextFieldString
{
targetvc.yourTextFieldString = yourTextFieldString
}
}
}
When the segue to your destination ViewController is performed, your variable (here in this case "yourTextFieldString") has the previous set data.
class yourTableViewController : UITableViewController
{
var yourTextFieldString = "" // This variable has the previous set data
override func viewDidLoad()
{
super.viewDidLoad()
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("yourCellID", forIndexPath: indexPath)
cell.textLabel.text = yourTextFieldString
return cell
}
}
Now adjust your prototype cell (don't forget the right identifier) and you're done.
I need to pass a variable containing the index of the selected row in one view to the next view.
firstview.swift
var selectedRow = 0;
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRow = indexPath.row + 1
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "viewAssignmentsSegue") {
let navController = segue.destinationViewController as! UINavigationController
let controller = navController.viewControllers[0] as! AssignmentsViewController
print(selectedRow)
controller.activeCourse = selectedRow
}
}
My issue is that, when a row is selected, the selectedRow variable isn't updated by the tableView method before the segue occurs, meaning that the value is essentially always one behind what it should be. How can I delay the prepareForSegue until the variable is updated or how else can I successfully pass the selected row to the next view without delay?
One possibility: Don't implement didSelectRowAtIndexPath:. Just move that functionality into your prepareForSegue implementation. That, after all, is what is called first in response to your tapping the cell. Even in prepareForSegue you can ask the table view what row is selected.
Another possibility: Implement willSelectRowAtIndexPath: instead of didSelectRowAtIndexPath:. It happens earlier.
in your prepareForSegue:
controller.activeCourse = self.tableView.indexPathForSelectedRow
don't wait for didSelect to be called - react earlier.
Use this on prepareForSegue:
let path = self.tableView.indexPathForSelectedRow()!
controller.activeCourse = path.row
or
let path = self.tableView.indexPathForCell(sender)
controller.activeCourse = path.row
I have added a UILongPressGestureRecognizer to my UICollectionViewCell and created a segue, so by long pressing a cell the user segues to a ViewController.
I already have an ordinary segue using the didSelect method to display cell details, but the secondary long press gesture segue takes the user to a action screen to delete the cell.
Unfortunately when using the prepareForSegue to send the cell data to the destinationViewController I cannot use the standard let indexPaths = self.collectionView.indexPathForSelectedRow() method because I am creating my own selection when using a gesture recogniser.
So how can I send the cell data to the destinationViewController with prepareForSegue using the gestureRecognizer ?
if segue.identifier == "cellAction" {
let vc = segue.destinationViewController as! CellActionViewController
//This will cause an 'array index out of range error
let indexPaths = self.collectionView.indexPathsForSelectedItems()
let indexPath = indexPaths[0] as! NSIndexPath
//Here is how I would send the cell data
let selectedCell = Items[indexPath.row]
vc.selectedItem = selectedCell
}
So you need to access the particular cell that was pressed, and use this information to obtain its index path from within prepareForSegue(_:sender:).
The first thing to note is that sender parameter on the prepareForSegue method. I presume you are calling performSegueWithIdentifier(_:sender:) from your gesture handling function. Notice this also has a sender parameter, which is the same object that is passed to prepareForSegue. You can use this to pass your cell instance.
The second thing to note is that UIGestureRecognizer has a view property, which should most likely be the cell itself. Armed with this knowledge, you can pass the pressed cell instance to prepareForSegue:
func handleLongPress(longPress: UILongPressGestureRecognizer) {
if longPress.state == .Ended {
performSegueWithIdentifier("cellAction", sender: longPress.view)
}
}
Now, within prepareForSegue you can cast sender to your cell class, and get its index path using UICollectionViews indexPathForCell(_:) method:
override prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "cellAction" {
let vc = segue.destinationViewController as! CellActionViewController
if let cell = sender as? MyCell {
let indexPath = collectionView.indexPathForCell(cell)
let selectedItem = items[indexPath.row]
vc.selectedItem = selectedItem
} else {
fatalError("Invalid sender, was expecting instance of MyCell.)
}
}
}
I have a view crontroller called "subcateory2", this viewcontroller has a uicollectionview wit custom cell. I need two segues from my app. One of the called "to_videostable" from the viewcontroller to other view controller and the other calles "reload_collection" from the cell to the same viewcontroller(because the subcategory can have n-level of subcategories). The problem is with my prepareForSegue(i check in this function the identifier , that is defined in the " func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)",and execute different actions). When i select a cell this should happen:
first: go to my "func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)", check the condition and define my identifier for segue.
second: go to the prepareforsegue, check the condition and execute the actions.
but actually this happen:
first: in my ios simulator i select a cell.
second: my code go the prepareforsegue and go always for the segue called "reload_collection"(before going to my func collectionView(...)), and create a white views. In this moment is like a two threads are created, one of them go to the white windows and the other to the next stop.
third: this "second theard" go to the func collectionview(...) and check the condition, define the identifier, call to the performSegueWithIdentifier and go to the prepareforsegue function. In the prepareforsegue check the identifier and execute the differentes actions.
This is my code:
import UIKit
class Subcategory2: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
let viewUtils = ViewControllerUtils()
var result_category: Array<JSON> = []
#IBOutlet weak var collectionview: UICollectionView!
var tit: String!
var url: String!
var end_url:String = "?page_size=100"
var id_category: Int!
var index: NSIndexPath!
var url_children : String = ""
let imagePath = "http://d1rkb03u2ginv9.cloudfront.net/wp-content/uploads/"
override func viewDidLoad() {
self.viewUtils.showActivityIndicator(self.view)
super.viewDidLoad()
self.viewUtils.showActivityIndicator(self.view)
if self.result_category.isEmpty{
var category = category_function()
self.url = self.url + self.end_url
self.result_category = category.load_subcategory(self.url)}
self.viewUtils.hideActivityIndicator(self.view)
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
var cell : UICollectionViewCell = collectionView.cellForItemAtIndexPath(indexPath)!
println("entroooooooo")
if (self.result_category[indexPath.row]["children"].string != nil){
self.url_children = self.result_category[indexPath.row]["children"].string!
//while(self.url_children.isEmpty){}
println("voy a reloadcollection")
performSegueWithIdentifier("reload_collection", sender: cell)
//performSegueWithIdentifier("reload_collection3", sender: self)
}else{
println("voy a to_videostables")
performSegueWithIdentifier("to_videostable", sender: cell)
}
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//println(result_category.count)
return result_category.count }
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) ->UICollectionViewCell {
let cell: CollectionViewCellController2 = collectionView.dequeueReusableCellWithReuseIdentifier("cell2", forIndexPath: indexPath) as! CollectionViewCellController2
println(self.result_category[indexPath.row]["slug"].stringValue)
cell.label.text = self.result_category[indexPath.row]["slug"].stringValue
if (self.result_category[indexPath.row]["images"]["file"].string != nil){
//println("+++++++++++++++++")
var image = self.result_category[indexPath.row]["images"]["file"].stringValue
cell.image.sd_setImageWithURL(NSURL(string:self.imagePath + (image as! String)))
}else{
var image = "http://www.camping-oaza.com/images/joomlart/demo/default.jpg"
cell.image.sd_setImageWithURL(NSURL(string: image))
//cell.image.image = UIImage(named: image)
}
cell.NumberVideosLabel.text = self.result_category[indexPath.row]["videos_count"].stringValue
cell.NumberSubcategoryLabel.text = self.result_category[indexPath.row]["children_count"].stringValue
return cell
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "to_videostable"{
println("-------------")
println("voy a to_videostables")
let cell = sender as! UICollectionViewCell
let index = self.collectionview!.indexPathForCell(cell) // this return -> NSIndexPath?
//if (self.result_category[index!.row]["children"].string != nil){
// self.loaddata(self.result_category[index!.row]["children"].string!)
//}else{
let vc : VideosViewController = segue.destinationViewController as! VideosViewController
println(self.result_category[index!.row]["id"].intValue)
vc.id_category = self.result_category[index!.row]["id"].intValue
}
if segue.identifier == "to_livevideos"{
println("-------------")
println("to_livevideos")
println("-------------------")
let vc : SWRevealViewController = segue.destinationViewController as! SWRevealViewController
}
if segue.identifier == "reload_collection"{
println("-------------")
println("reload_collection")
println("-------------------")
var category = category_function()
let vc : Subcategory2 = segue.destinationViewController as! Subcategory2
vc.url = self.url_children
println(category.load_subcategory(self.url_children + self.end_url))
}
}
}
With this problem, always is created a white windows and after is created a windows with the real information.
the order of the println is :
- "reload_collection"
- "entroooooooo"
- "voy a reloadcollection" or "voy a to_videostables"
In this pictures show my main.stoyboard and the windwos that i can see in my app.
Updated Answer
You have a situation where you want to decide which segue to take when a cell is selected. You have wired one of your segues directly from the cell, which means the storyboard will create that segue for you. You also are calling performSegueWithIdentifier which creates another segue. You need to implement shouldPerformSegueWithIdentifier to cancel the "reload_collection" segue when you want segue to "to_videostables".
In the Original Answer below, I suggested you wire both segues from the viewController, but that won't work because one of your segues is back to the same viewController.
So, another way to do this is to:
Modify didSelectItemAtIndexPath to remove the code that handles the "reload_collection" segue. The Storyboard will be making that segue:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
var cell : UICollectionViewCell = collectionView.cellForItemAtIndexPath(indexPath)!
println("entroooooooo")
if result_category[indexPath.row]["children"].string == nil {
println("voy a to_videostables")
performSegueWithIdentifier("to_videostable", sender: cell)
}
}
Wire the segue "reload_collection" from the cell to the viewController. This will allow the Storyboard to perform this segue for you.
Implement shouldPerformSegueWithIdentifier to tell the Storyboard when it should make this segue:
override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool {
if segue.identifier == "reload_collection" {
let indexPath = collectionView.indexPathForCell(sender as! UICollectionViewCell)
return result_category[indexPath.row]["children"].string != nil
}
return true
}
In prepareForSegue you will need to set up url_children since it is no longer being done by didSelectItemAtIndexPath:
if segue.identifier == "reload_collection"{
println("-------------")
println("reload_collection")
println("-------------------")
var category = category_function()
let vc : Subcategory2 = segue.destinationViewController as! Subcategory2
let indexPath = collectionView.indexPathForCell(sender as! UICollectionViewCell)
url_children = result_category[indexPath.row]["children"].string!
vc.url = url_children
println(category.load_subcategory(self.url_children + self.end_url))
}
Original Answer
Your segue is getting auto-called because you have wired it from the cell. If you want to trigger it with performSegueWithIdentifier it needs to be wired from the viewController like the other segue. Just remove the segue from the cell and rewire it from the viewController and give it the same identifier it had when you wired it from the cell and it should work.