So I have a main ViewController that has Horizontal Scrolling CollectionViews inside a TableView. When you click on an Item in the CollectionView it transfers all the data from the Item to a new ViewController to display it properly.
The problem I am facing is when you click on the item in the CollectionView a segue is performed that transfers data to an array in the new ViewController but instead of transferring only data from the particular item it send data from more than one item. On some debugging I also found that the items Custom class is what loads the multiple elements in the array and not the segue. Here is some code.
cellForRowAt in main VC
if let cell = tableView.dequeueReusableCell(withIdentifier: "HeadlineRow", for: indexPath) as? HeadlineRow {
cell.latest = self.latest
cell.collectionView.reloadData()
cell.delegate = self
return cell
}
Segue Code in main VC
func pushToNewsVC(latestNews: [LatestNews]) {
self.transferToNewsVC = latestNews
performSegue(withIdentifier: "NewsVC", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "NewsVC" {
if let destination = segue.destination as? NewsVC {
destination.latestNews.append(contentsOf: self.transferToNewsVC)
destination.SuggestedLatestNews.append(contentsOf: self.latest)
}
}
}
Headlinerow which is TableViewCell class
var latest = [LatestNews]()
var transferData = [LatestNews]()
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return latest.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HeadlineCell", for: indexPath) as? HeadlineCell {
let latest = self.latest[indexPath.row]
cell.updateUI(latest: latest)
return cell
}
return UICollectionViewCell()
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as? HeadlineCell {
self.transferData.removeAll()
self.transferData = cell.transferData
delegate?.pushToNewsVC(latestNews: self.transferData)
}
}
HeadlineCell which is CollectioViewCell class
var transferData = [LatestNews]()
func updateUI(latest: LatestNews) {
storyTextView.text = latest.headline
self.thumbnail.sd_setImage(with: URL(string: "\(latest.app_img)"))
self.transferData.append(latest)
}
Related
I am using collectionview in tableview cell,
i need to pass selected collectionview cells value to next viewcontroller, how?
code: here is the code for tableview and collectionview cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CategoryNewTableCell", for: indexPath) as! CategoryNewTableCell
let indexData = self.activeCategories?[indexPath.row]
cell.selectionStyle = .none
cell.catNameLbl.text = indexData?.details?.first?.title
cell.subCategories = indexData?.sub_categories
cell.clcSeller.reloadData()
}
class CategoryNewTableCell: UITableViewCell,UICollectionViewDelegate,UICollectionViewDataSource{
#IBOutlet weak var clcSeller: UICollectionView!
public var subCategories : Array<Sub_categories>?
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return subCategories?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SubCatCollectionCell", for: indexPath) as! SubCatCollectionCell
let subCategory = self.subCategories?[indexPath.item]
cell.lblTitle.text = langType == .en ? subCategory?.details?.first?.title : subCategory?.details?[1].title
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = StoryBoard.main.instantiateViewController(withIdentifier: "SearchResultVC") as! SearchResultVC
vc.subCatId = sub_categories?[indexPath.row].slug ?? ""
self.navigationController?.pushViewController(vc, animated: true)
}
}
here if i use didSelectItemAt for collectionview to send its selected cell value to next view controller
error:
Value of type 'CategoryNewTableCell' has no member 'navigationController'
if i give button action in main class then able to push but value is not going
class CategoryNewVC: UIViewController {
#IBAction func didselectcollectionviewBTn(_ sender: UIButton) {
let vc = StoryBoard.main.instantiateViewController(withIdentifier: "SearchResultVC") as! SearchResultVC
vc.subCatId = //here how to pass value
self.navigationController?.pushViewController(vc, animated: true)
}
}
here how to pass selected collectionview cells value to SearchResultVC please do help
EDIT
according to below answer i have added: still didSelectItemAt not called, why plz do help
class CategoryNewTableCell: UITableViewCell,UICollectionViewDelegate,UICollectionViewDataSource{
override func awakeFromNib() {
super.awakeFromNib()
self.clcSeller.delegate = self
self.clcSeller.dataSource = self
//
You can use protocol to send data to your nextviewcontroller.
protocol CategorySelectionDelegate {
func get(category: Sub_categories)
}
Declare the delegate in your CategoryNewTableCell and use it in didSelectItemAt method of your collectionviewcell like below:
class CategoryNewTableCell:
UITableViewCell,UICollectionViewDelegate,UICollectionViewDataSource{
#IBOutlet weak var clcSeller: UICollectionView!
public var subCategories : Array<Sub_categories>?
var delegate: CategorySelectionDelegate?
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return subCategories?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SubCatCollectionCell", for: indexPath) as! SubCatCollectionCell
let subCategory = self.subCategories?[indexPath.item]
cell.lblTitle.text = langType == .en ? subCategory?.details?.first?.title : subCategory?.details?[1].title
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
delegate?.get(category: sub_categories?[indexPath.row])
}
}
Adopt the protocol in the receiving class
class CategoryNewVC: UIViewController, CategorySelectionDelegate {
func get(category: Sub_categories) {
let vc = StoryBoard.main.instantiateViewController(withIdentifier: "SearchResultVC") as! SearchResultVC
vc.subCatId = category.slug ?? ""
self.navigationController?.pushViewController(vc, animated: true)
}
}
Declare a callback in the tableViewCell subclass.
class CategoryNewTableCell: UITableViewCell {
var onSelectSubcategory: ((_ subcategoryID: String) -> Void)?
}
Assign this callback in your cellForRow like this.
cell.subCategories = indexData?.sub_categories
cell.onSelectSubcategory = { [weak self] (subcategoryID) in
let vc = StoryBoard.main.instantiateViewController(withIdentifier: "SearchResultVC") as! SearchResultVC
vc.subCatId = subcategoryID
self?.navigationController?.pushViewController(vc, animated: true)
}
Invoke this callback from collectionView didSelectItem like this.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let subcategoryID = sub_categories?[indexPath.item].slug ?? ""
self.onSelectSubcategory?(subcategoryID)
}
I have here my collection view controller and when I click on the collection view cell it is updating between true and false but my label is not updating from added to not added. any help would be greatly appreciated I have been at this for 4 days. I have tried reading the apple documentation and it doesn't give examples as to how to accomplish this with using a boolean value. What I want to happen is for the Label atop my collection view cell label to change from added to not added when the collection view cell has been pressed. I know it is working because when I tested it with print statements when I press the collection view cell it changes back and forth from true to false. I am just having trouble with the label changing from added to not added with it.
import UIKit
class ShoppingListCollectionViewController: UICollectionViewController {
var shoppingItemController = ShoppingItemController()
var shoppingItemCollectionViewCell = ShoppingItemCollectionViewCell()
override func viewDidLoad() {
super.viewDidLoad()
shoppingItemCollectionViewCell.updateViews()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShoppingListDetailSegue" {
guard let shoppingListDetailVC = segue.destination as? ShoppingListDetailViewController else {
return
}
}
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
// MARK: UICollectionViewDataSource
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return shoppingItemController.shoppingItems.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ShoppingItemCell", for: indexPath) as? ShoppingItemCollectionViewCell else {
fatalError("Collection view cell identifier is wrong or the cell is not a ShoppingItemCollectionViewCell")
}
// Configure the cell
let shoppingListItem = shoppingItemController.shoppingItems[indexPath.item]
cell.imageView.image = shoppingListItem.image
cell.shoppingItemLabel.text = shoppingListItem.imageName
if shoppingListItem.added == true {
cell.hasBeenAddedLabel.text = "Added"
} else {
cell.hasBeenAddedLabel.text = "Not Added"
}
return cell
}
// MARK: UICollectionViewDelegate
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
var chosenItem = shoppingItemController.shoppingItems[indexPath.item]
chosenItem.added = !chosenItem.added
shoppingItemController.shoppingItems[indexPath.item] = chosenItem
if chosenItem.added == true {
chosenItem.updateViews()
}
print(chosenItem.added)
}
You only need to get the cell from cellForItem(at:) method inside the didSelectItemAt method and update the label text to your desired string:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! ShoppingItemCollectionViewCell
var chosenItem = shoppingItemController.shoppingItems[indexPath.item]
chosenItem.added.toggle()
cell.label.text = chosenItem.added ? "Added" : "Not Added"
}
I want to make it so that when I tap on a CollectionView Cell, it segues to another view. I also want to pass a user ID to this view so I can query the database. This is what I have implemented so far:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionview.dequeueReusableCell(withReuseIdentifier: "userCell", for: indexPath) as! UserCell
print(user[indexPath.row].imagePath!)
cell.userImage.sd_setImage(with: URL(string: user[indexPath.row].imagePath!))
cell.nameLabel.text = user[indexPath.row].username
cell.userID = user[indexPath.row].userID
let destinationVC = ProfileViewController()
destinationVC.sentUserID = user[indexPath.row].userID!
// Let's assume that the segue name is called playerSegue
// This will perform the segue and pre-load the variable for you to use
//destinationVC.performSegue(withIdentifier: "toProfileFromSearch", sender: self)
cell.addButtonTapAction = {
// implement your logic here, e.g. call preformSegue()
self.performSegue(withIdentifier: "toProfileFromSearch", sender: self)
}
//cell.userImage.downloadImage(from: self.user[indexPath.row].imagePath!)
//checkFollowing(indexPath: indexPath)
return cell
}
This is in the individual creation of each cell. When I click the user profile in the search area, nothing happens.
I followed other stack overflow questions to get this far. Can anyone make a complete solution for other people that need this too?
You can use the didSelectItemAt function, check this
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let VC1 = self.storyboard!.instantiateViewController(withIdentifier: "IDYOURVIEW") as! ProfileViewController
VC1.sentUserID = user[indexPath.row].userID!
self.navigationController?.pushViewController(VC1, animated: true)
}
If you're using segues
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "toProfileFromSearch", sender: user[indexPath.row].userID!)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "toProfileFromSearch"
{
let vc = segue.destination as? ProfileViewController
if let id = sender as! String
{
vc?.sentUserID = id
}
}
}
There is another better way to handle this using protocol
protocol UserCellTapedDelegate: AnyObject {
func didTapButton(_ cell: UserCell, didSelectItemAt id: Int)
}
then your cell would be like:
class UserCell: UICollectionViewCell {
public var userID: Int!
public weak var delegate: UserCellTapedDelegate?
#objc private func addButtonTapAction() {
delegate?.didTapButton(self, didSelectItemAt: userID)
}
}
userID: to store the user ID.
delegate: use an optional weak (in terms of memory management) delegate reference in your cell.
addButtonTapAction func to handle target of the button/or tap gesture on Any UIView.
The next step is to go back to your UIViewController and conform to the custom delegate:
extension ViewController: UserCellTapedDelegate {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionview.dequeueReusableCell(withReuseIdentifier: "userCell", for: indexPath) as! UserCell
...
cell.userID = user[indexPath.row].userID!
cell.delegate = self
return cell
}
func didTapButton(_ cell: UserCell, didSelectItemAt id: Int) {
self.performSegue(withIdentifier: "toProfileFromSearch", sender: id)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toProfileFromSearch" {
let vc = segue.destination as! ProfileViewController
if let id = sender as! String {
vc.sentUserID = id
}
}
}
}
Hope this helps you 😊
I am using a UICollectionView inside UITableViewCell. I am able to select the cells inside the UICollectionView. But when i try to get the UICollectionView or selected cells, the result is always null.I have been stuck on this for a long time. i included my code below for your reference.
class WeekDaysSelCell: UITableViewCell,UICollectionViewDelegate, UICollectionViewDataSource,UICollectionViewDelegateFlowLayout {
var weekdays = ["S", "M", "T", "W", "T", "F", "S"]
var weekdaysSelected = [String]()
#IBOutlet var weeklyDaysColView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
self.weeklyDaysColView.delegate = self
self.weeklyDaysColView.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 7
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell : WeekDaysCollCell = weeklyDaysColView.dequeueReusableCell(withReuseIdentifier: "weekday", for: indexPath) as! WeekDaysCollCell
cell.weekDayLabel.text = weekdays[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell : WeekDaysCollCell = self.weeklyDaysColView.cellForItem(at: indexPath) as! WeekDaysCollCell
if (cell.backgroundColor == UIColor.gray) {
cell.backgroundColor = UIColor.clear
weekdaysSelected.removeAll { $0 == String(indexPath.row)}
//print("Removed from weekdaysSelected:", indexPath.row)
} else {
cell.backgroundColor = UIColor.gray
cell.isSelected = true
//weeklyDaysColView.selectItem(at: indexPath, animated: true, scrollPosition: [])
weekdaysSelected.append(String(indexPath.row))
//print("Added to weekdaysSelected:", indexPath.row)
}
}
}
// Trying to get the collection view from inside a willMove(toParent parent: UIViewController?) method.
override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent: parent)
if parent == nil
{
if let delegate = self.delegate {
print("Inside If condition")
// Code that i use to get the cell
let cell3 = tableView.dequeueReusableCell(withIdentifier: "cell3") as! WeekDaysSelCell
print(cell3.weekdaysSelected)
print(cell3.weeklyDaysColView.indexPathsForSelectedItems)
// Trying to pass selected cells
//delegate.repeatCustomSelection(selectedIdx: String(lastSelection.row),repeatCustomSel: repeatCustomSelection)
}
}
}
You are trying to get a reusable cell in willMove(toParent parent: UIViewController?) , this is not going to return you a expected cell.
You need to get the cell , using a indexPath .
func cellForRow(at indexPath: IndexPath) -> UITableViewCell?
#andyPaul, is right you are generating the new cell in willMove(toParent parent: UIViewController?). Instead of that you have to pass the indexpath pf collection view when ever user selected any cell to your controller from the tableView Cell Class.
Now What is TypeAlias you can read from this link about type alias:- https://www.programiz.com/swift-programming/typealias
Create the typeAlias on above of your tableViewCell Class like this:-
typealias closureBlock = (_ isCapture : AnyObject?) ->()
class tableViewCellClass: UITableViewCell {
var callBack: closureBlock?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
Just Go to CollectionView didSelectItemAt Method and use this code after your coding
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell : WeekDaysCollCell = self.weeklyDaysColView.cellForItem(at: indexPath) as! WeekDaysCollCell
if (cell.backgroundColor == UIColor.gray) {
cell.backgroundColor = UIColor.clear
weekdaysSelected.removeAll { $0 == String(indexPath.row)}
//print("Removed from weekdaysSelected:", indexPath.row)
} else {
cell.backgroundColor = UIColor.gray
cell.isSelected = true
//weeklyDaysColView.selectItem(at: indexPath, animated: true, scrollPosition: [])
weekdaysSelected.append(String(indexPath.row))
//print("Added to weekdaysSelected:", indexPath.row)
}
guard let callBackClosure = self.callBack else {
return
}
callBackClosure(indexPath as AnyObject)
// You can pass any value here either indexpath or Array.
}
}
Now you have to initialise this closure so that it can check whether in which controller it will return the value when you assign the value from CollectionView didSelectItemAt Method.
Go to your ViewController Class where you have added the tableview and their datasources.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//You have to pass your tableview Cell Instance here and their reuse Identifier
let cell = tableView.dequeueReusableCell(withIdentifier: "tableViewCellClass", for: indexPath) as! tableViewCellClass
cell.callBack = { [weak self] (selectedIndexPath) -> ()in
// You will get the current selected index path of collection view here, Whenever you pass any index path from collectionView did SelectItem Method.
print(selectedIndexPath)
}
return cell
}
So I have a TableViewController with a CollectionView and a custom Cell, but I want to prepare a segue from that CollectionView to a TableView.
I tried this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "drawSegue" {
let dst = segue.destination as! DrawerViewController
guard let indexPath = cupCollectionView.indexPathsForSelectedItems else{
return
}
dst.cup = cupboards[indexPath.row] as! Cupboard
}
}
But I get an error Value of type '[IndexPath]' has no member 'row'. Is there another way to do that? Cause in another project I used this from a tableView to a tableView and it worked...
This is my CollectionView:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cupboards.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cupCell", for: indexPath) as! CupboardCollectionViewCell
let cupboard = cupboards[indexPath.row]
cell.cupLabel.text = cupboard.name
return cell
}
Thank you in advance
It looks like cupCollectionView.indexPathsForSelectedItems is returning an array of IndexPath objects, so instead of accessing a single IndexPath.row you're trying to call Array<IndexPath>.row which doesn't exist
In your didSelectCell when you call the performSegue, add your cell as the sender, then you can change it to let indexPath = cupCollectionView.indexPath(for: sender)