How to Send a segue in didSelectItemAt - iphone

I am trying to make a segue that sends a string to a variable in the next view controller. This string is the text inside a label that is inside a collection view cell, the text inside the label is from CoreData. When the user presses the cell, it is meant to segue to the next view controller and have the text of the cell previously selected in a variable. I am not sure how to do this, this is my didSelect function which is able to get the text of the selected cell:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellP", for: indexPath) as! CCCollectionViewCell
let project = projectList[indexPath.row]
if project.name! == "Hi " {
print("Yes")
}
else {
print("no")
}
print(project.name!)
}
The problem is I am not sure how to send this value (project.name!) of the selected cell in a segue to the next view controller variable.

Try like this way.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! NextViewController //Your ViewController class
if let cell = sender as? UICollectionViewCell,
let indexPath = self.collectionView.indexPath(for: cell){
let project = projectList[indexPath.row]
print(project.name)
vc.name = project.name
}
}
Now simply in your NextViewController create one property of type String.
var name = String()

Related

How to send UITableViewCell data to another ViewController in swift?

I am trying to send a single message from the messageArray to another UIViewController so that I can load up the message's comments. How can I send the message data structure over when the cell is clicked on?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "messageCell") as? feedMessagesCell else {return UITableViewCell()}
let message = messageArray[indexPath.row]
cell.configureCell(content: message.content, userName: message.userName)
return cell
}
First of all don't guard reusing cells. The code must not crash. If it does it reveals a design mistake. And use the API which returns a non-optional cell.
let cell = tableView.dequeueReusableCell(withIdentifier: "messageCell", for: indexPath) as! feedMessagesCell
To send data to another view controller create a segue in Interface Builder by connecting the table view cell to the destination controller.
In prepare(for segue the sender is the cell. Change PushFeedDetail to the real identifier and MyDestinationController to the real class. Create a message property in the destination controller. Get the index path from the cell and pass the item in the data source array.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "PushFeedDetail" {
let selectedIndexPath = tableView.indexPath(for: sender as! feedMessagesCell)!
let destinationController = segue.destination as! MyDestinationController
let message = messageArray[selectedIndexPath.row]
destinationController.message = message
}
}

Seguing from uicollectionview that is inside of a tableview

I've put a uicollectionview inside of a uitableview. I'm having trouble seguing to another viewcontroller after selecting a collectionview cell that is inside of the table view cell.
// if the user selects a cell, navigate to the viewcontroller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// we check did cell exists or did we pressed a cell
if let cell = sender as? UICollectionViewCell {
let cell2 = tableView.dequeueReusableCell(withIdentifier: "cell") as! TestingTableView
// define index to later on pass exact guest user related info
let index = cell2.collectionView?.indexPath(for: cell)!.row
print(index as Any)
// if segue is guest...
if segue.identifier == "guest" {
// call guestvc to access guest var
let guestvc = segue.destination as! GuestCommunityViewVC
// assign guest user inf to guest var
guestvc.guest = communities[index!] as! NSDictionary
}
}
}
}
I'm getting an error at the line:
let index = cell2.collectionView?.indexPath(for: cell)!.row
because it is saying the value is nil. Does anyone know a better method to do this?
Here is an example of how to use a delegate:
1) Create a protocol outside of a class declaration:
protocol customProtocolName:class {
func pushToNewView(withData:[DataType])
}
note: use class in order to prevent a reference cycle
2) Create a delegate inside of the UITableViewCell that holds the reference to the UICollectionView:
class customUITableViewCell {
weak var delegate:customProtocolName? = nil
}
3) Inside the UIViewController that holds the reference to the UITableView, make sure you add the protocol besides the class declaration and add the function we created to ensure that the protocol specifications are satisfied:
class customViewController: customProtocolName {
func pushToNewView(withData:[DataType]) {
//inside here is where you will write the code to trigger the segue to the desired new UIViewController
//You can take this new data and store it in this ViewController and then during the segue pass it along
}
}
4) In the UITableViewDelegate function, "cellForRowAt", set the delegate inside the customUITableViewCell to self:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! customUITableViewCell
cell.delegate = self
return cell
}
5) Inside the customUITableViewCell, where the UICollectionView delegate function handles "didSelectItemAt" delegate function, you trigger the protocol function there like so:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
delegate?.pushToNewView(withData:[DataType])
}
This is a very simplified example, if you wanted to pass an IndexPath, then you can modify the function to do so. you can also pass back anything you want as well, it isn't limited.

How to send indexpath when performing segue on collectionViewCell?

I have a collection view controller with similar cells and I want each cell open next viewController with specific content. I think I must send indexpath.row with the segue but in this part I face an error:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let newViewController = segue.destination as? myCollectionViewController {
newViewController.passedValue = indexPath.row // exactly here
}
}
There are a couple of things wrong in your code. indexPath.row variable is not declared in your prepare function. To pass the selected cell info to another view controller you first need to add a global variable in your class.
var selectedCell = 0
Next implement the function didSelectRowAtIndexPath in your class. This function is called every time you select a cell. In this function get the cell info and assign it to selectedCell var
override func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
selectedCell = indexPath.row
}
Now in your prepare method use this var value and pass it on to the next controller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "YourIdentifier" {
let VC = segue.destination as! YourViewController
VC.passed = selectedCell
}
}
Hope this helps.
In the above code:
newViewController.passedValue = indexPath.row // exactly here
what is the value of indexPath here? Is is an instance variable?
Instead of using segue, use UICollectionViewDelegate method:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
//Push your controller here
}

How can I segue from UICollectionView's cells embedded inside UITableViewCell (Swift)?

I have used this tutorial to successfully embed a UICollectionView inside a UITableView I have in my ViewController.
The following will probably make more sense if you have a quick look at the linked tutorial's code (plus its a nifty thing to learn too!):
The next step for me is to perform segues from the cells of the UICollectionView inside the UITableViewCells of the tableView, but as the collectionView outlet is established in a separate View Controller, I am not sure how to reference it in the main ViewController.
In the TableViewCell.swift there is:
class TableViewCell: UITableViewCell {
#IBOutlet private weak var collectionView: UICollectionView!
}
extension TableViewCell {
func setCollectionViewDataSourceDelegate<D: protocol<UICollectionViewDataSource, UICollectionViewDelegate>>(dataSourceDelegate: D, forRow row: Int) {
collectionView.delegate = dataSourceDelegate
collectionView.dataSource = dataSourceDelegate
collectionView.tag = row
collectionView.reloadData()
}
}
And in the ViewController.swift I need to be able to, for instance, call the collectionView that is in the TableViewCell in the prepareForSegue function that would go in the ViewController.swift file. I just need to fill the gaps with the collectionView outlet:
let destination = segue.destinationViewController as! SecondViewController
let indexPaths = self.___________.indexPathsForSelectedItems()
let indexPath = indexPaths![0] as NSIndexPath
let arrayObject = self.arrayObjects[indexPath.row]
destination.object = arrayObject
'object' is implemented in SecondViewController like so, var object: PFObject!.
I now need to fill the gap, ________, in the above code with the collectionView in order to display the correct 'object' in SecondViewController (the destinationViewController)
Add Push Segue from UICollectionViewCell to YourViewController from IB.
Give an identifier to your segue ("YourSegueIdentifier").
In your custom UITableViewController or UIViewController, override prepareForSegue() method.
This is:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "YourSegueIdentifier" {
if let collectionCell: YourCollectionViewCell = sender as? YourCollectionViewCell {
if let collectionView: UICollectionView = collectionCell.superview as? UICollectionView {
if let destination = segue.destination as? YourViewController {
// Pass some data to YourViewController
// collectionView.tag will give your selected tableView index
destination.someObject = tableObjects[collectionView.tag].someObject
destination.productId = collectionCell.product?.id
}
}
}
}
}
Hi I had the same problem
This is what I did :
1. set a tag for the cell in the extension in your first View Controller
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("advertisementCollectionCell",forIndexPath: indexPath) as! AdvertisementCollectionViewCell
// set your cell information
cell.tag = indexPath.row
return cell
}
2. Do a segue on the storyboard from the UIcollectionViewCell to the new View Controller
3. In prepareForSegue of the first TableViewController :
else if segue.identifier == "identifier"
{
let destinationViewController = segue.destinationViewController as! DestinationViewController
if let cell = sender as? MyCollectionViewCell
{
let indexPath = cell.tag
// use indexPath :D
}
}
i just played with the code you attached. i added segue in story board manually. its easy to call .
1) just add viewcontroller to the storyboard.
2) Controll+ drag and drop segue from collectionview cell
In your code, the reference to CollectionView is connected in TableViewCell.
So, you can refer it through TableViewCell and set datasource and delegate.
To add connect into your code, you have to select the CollectionView with RightButton and drag and drop into #IBOutlet private weak var collectionView: UICollectionView!
When clicks the CollectionViewCell, collectionView(collectionView:UICollectionView, didselecteditemAtIndexPath indexPath: NSIndexPath) function of UICollectionViewDelegate will be called by delegate.
At that time, you can segue by calling performSegue in this function.
As you write in your code, the index of tableview is set by tag, so you can get the index by tag using collectionView.tag()
The code will be as following.
var tableViewIndex = collectionView.tag()
So you can refer the correct cell using tableViewIndex and indexPath.
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
var tableViewIndex = collectionView.tag;
self.performSegueWithIdentifier("segueIdentifier", sender: self)
}

Swift: UICollectionView selecting cell indexPath issues

I am trying to do a Collection View whereby someone selects a cell and for each selection it takes them to another View Controller that holds content of the selection. However I'm running into difficulties as once I apply this line of code to didSelectItemAtIndexPath;
self.performSegueWithIdentifer("showDetail", sender: self)
and then run it in the Simulator the cell selection is working according the indexPath but its remembering the selections each time I select new cell. So for example each cell has a photo and label and if I select the first cell in the indexPath the segue takes me first to blank view and then to my selected cell. If I select another cell, number 3 on the indexPath the blank view is now the first cell from my previous choice after which it takes to my selected third cell . Its doing that every time. If I remove the performSegueWithIdentifer code (from Xcode 6.2 (in 6.1.1 it was random)) the selection is my previous choice and never my 'selectedCell', but then at least its only selecting once instead of twice to get to a view. There is something going wrong on the indexPath. This is the code for my prepareForSegue
override func prepareForSegue(segue: UIStoryBoardSegue, sender: AnyObject?) {
if segue.identifer == "showDetail" {
let detailVC:DetailViewController = segue.destinationViewController as DetailViewController
detailVC.selectedImageName = selectedImage
detailVC.selectedLabel = selectedLabels
}
}
I'm stuck on what to do & what solution to apply. Do I keep performSegueWithIdentifer code & create an Equatable to implement find(array, selection) on the indexPath? Or could I write a loop, (which seems much easier), that would run through the indexPath based upon the selections and that would remove the cell that is no longer selected. However I'm not sure what condition to write in the loop because I don't know the value of the property of the 'selectedCell' because its optional.
for (index, value) in enumerate(cellItems) {
//something here to remove 'selectedItem' in the indexPath
}
If I remove performSegueWithIdentifer code from didSelectItemAtIndexPath what can I do in my prepareForSegue to get the selection on the correct indexPath?
EDIT the complete code at didSelectItemAtIndexPath
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
selectedImage = cellImages[indexPath.row] as String
selectedLabels = cellLabels[indexPath.row] as String
self.performSegueWithIdentifer("showDetail", sender: self)
}
I've tried changing sender in the performSegueWithIdentifer to indexPath but the problem still remains.
EDIT 2 Complete code to my CollectionViewController
class CollectionViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var collectionView: UICollectionView!
var selectedImage = String()
var selectedLabels = String()
var cellImages:[String] = ["1.jpg", "2.jpg", "3.jpg", "4.jpg", "5.jpg", "6.jpg", "7.jpg", "8.jpg", "9.jpg", "10.jpg", "11.jpg", "13.jpg", "14jpg"]
var cellLabels:[String] = ["Photo 1", "Photo 2", "Photo 3", "Photo 4", "Photo 5", "Photo 6", "Photo 7", "Photo 8", "Photo 9", "Photo 10", "Photo 11", "Photo 12", "Photo 13", "Photo 14"]
func collectionView(collectionView: UICollectionView, numberOfNumberItemsInSection: Int) -> Int {
return cellImages.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: PhotoViewCell = collectionView.dequeueReuseableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as PhotoViewCell
cell.labelCell.text = cellLabels[indexPath.row]
cell.ImageCell.image = UIImage(named: cellImages[indexPath.row])
return cell
}
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
selectedImage = cellImages[indexPath.row] as String
selectedLabels = cellLabels[indexPath.row] as String
self.performSegueWithIdentifer("showDetail", sender: self)
}
override func prepareForSegue(segue: UIStoryBoardSegue, sender: AnyObject?) {
if segue.identifer == "showDetail" {
let detailVC:DetailViewController = segue.destinationViewController as DetailViewController
detailVC.selectedImageName = selectedImage
detailVC.selectedLabel = selectedLabels
}
}
}
PhotoViewCell
class PhotoViewCell: UICollectionViewCell {
#IBOutlet var labelCell: UILabel!
#IBOutlet var ImageCell: UIImage!
}
EDIT 3 - Amended
I tried your suggestion and unfortunately the problem is still persisting on double views - it's still passing two views before it takes me to the actual selected cell. I also amended the code slightly in the didSelectItemAtIndexPath but it still didn't fix the problem.
if let cell = collectionView.cellForItemAtIndexPath(indexPath) as? PhotoViewCell {
performSegueWithIdentifier("showDetail", sender: cell)
}
However following your other suggestion, in my StoryBoard I have added a segue from my Collection View cell to my DetailViewController, which has the identifier "showDetail". If I remove segue nothing can be selected from my cells.
Although it seems the performSegueWithIdentifer code is the trigger for the double views because when I remove it, the cell is only being selected once, the problem was that the indexPath of the cell selection was not correct, because it's first selecting on a blank view (is that to do with the showDetail segue?), which then puts my indexPath out of sync.
EDIT - Solved
This stopped the double selections (the performSegueWithIdentifier line was removed): -
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
if let cell = collectionView.cellForItemAtIndexPath(indexPath) {
cellLabels[indexPath.row] as String
cellImages[indexPath.row] as String
}
}
Many Thanks for your help !!!!
(NOTE: I updated this for Swift 4 and more modern practices.)
I stick to UIView objects as much as possible.
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let cell = collectionView.cellForItem(at: indexPath) else { return }
performSegue(withIdentifier: "showDetail", sender: cell)
}
Then in prepare(for:sender:)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.identifier {
case "showDetail":
guard let indexPath = (sender as? UIView)?.findCollectionViewIndexPath() else { return }
guard let detailViewController = segue.destination as? DetailViewController else { return }
detailViewController.selectedImageName = cellImages[indexPath.row]
detailViewController.selectedLabel = cellLabels[indexPath.row]
default: return
}
}
I used an extension I created a while ago findCollectionViewIndexPath()
extension UIView {
func findCollectionView() -> UICollectionView? {
if let collectionView = self as? UICollectionView {
return collectionView
} else {
return superview?.findCollectionView()
}
}
func findCollectionViewCell() -> UICollectionViewCell? {
if let cell = self as? UICollectionViewCell {
return cell
} else {
return superview?.findCollectionViewCell()
}
}
func findCollectionViewIndexPath() -> IndexPath? {
guard let cell = findCollectionViewCell(), let collectionView = cell.findCollectionView() else { return nil }
return collectionView.indexPath(for: cell)
}
}
I have a suspicion that you have a segue in the storyboard already and don't need func collectionView(, didSelectItemAtIndexPath:), but either way, the prepare segue should work.
Swift 3.0
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let iPath = self.collectionView.indexPathsForSelectedItems
let indexPath : NSIndexPath = iPath![0] as NSIndexPath
let rowIndex = indexPath.row
print("row index = \(rowIndex)")
}