A collection view inside a table view - swift

I have the following implementation in the app, where there is a table view, where each table view cell holds a collection view.
Here, the number of cells in a collection view will depend on the data in a tableview cell and will be different for each row. For example, there are 3 cards in 1st row, 1 in 2nd row and 2 in 3rd row.
The issue is I am getting 3 collection view cells instead of 1 in the second cell.
Table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.groupedJobIds.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: JobTableViewCellCollection.CellReuseIdentifier) as? JobTableViewCellCollection {
var jobIds = self.groupedJobIds[indexPath.row]
cell.delegate = self
if jobIds != [] {
cell.bindJobs(jobIds)
}
return cell
}
return UITableViewCell()
}
Tableview cell / Collection view
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.jobIds.count //It returns 1
}
//This method is called 3 times
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: JobCollectionViewCell.CellReuseIdentifier, for: indexPath) as? JobCollectionViewCell
cell?.delegate = self
cell?.bindJob(jobId)
return cell!
}
As you can see, the first method returns 1 as numberOfItemsInSection. But the second method is called 3 times. Am I missing something?

I think you are using dequeueReusableCell method of the table view, which is causing the problem.
Can you try a new collection view on table view every time it load?

You should populate your UICollectionView from the right source. In your code you populate all of your collection view cells from the same self.groupedJobIds. You should either populate your collection view from table view cell's bound jobIds or keep an array of jobId groups in your UIViewController and populate from there.

I found a solution to this. I missed the reloadData() on the collection view which was causing the issue

Related

Make the first cell automatically selected when VC opens is not working

I have a ViewController that have a collectionView and I managed to make it selectable and all but the problem is that I have a checkmark image that stays in the first cell when the VC opens but in fact the cell is not selected at all and still the checkmark is there!
Code of the VC:
var selected = IndexPath(item: 0, section: 0)
var properties = connectedProperties(StatusCode: 0)
var propertiesNew = connectedProperties(StatusCode: 0)
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return properties.Result?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "dashboardCollectionViewCell", for: indexPath) as? dashboardCollectionViewCell else { return UICollectionViewCell() }
let currentPropertie = properties.Result?[indexPath.row]
cell.checkMarkButton.isHidden = !(indexPath == selected)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedCell = properties.Result?[indexPath.row]
changeCustomerKey.DefaultsKeys.keyTwo = indexPath.row
changeCustomerKey.DefaultsKeys.keyThree = selectedCell!.id!
let previous = selected.dropLast()
selected = indexPath
collectionView.reloadItems(at: [previous, selected])
}
If you want to tell your collection view to select a specific cell, you need to call the UICollectionView method selectItem(at:animated:scrollPosition:).
The "tricky bit" is that you can't call that until the collection view has finished populating itself (by calling your data source methods) and the first cell has been added to the collection view.
You might need to resort to something a bit hacky like adding an "initialDisplay" bool property who's value starts as true.
In your data source method that returns cells, check if initialDisplay==true and the requested indexPath is (0,0). If so, set initialDisplay=false, and fire a one-shot timer with a short delay. In the timer's closure, call selectItem(at:animated:scrollPosition:). The timer delay will return control to the event loop and give the system time to add the cell to the collection view.
There might be a better way to do this, but I can't think of it offhand, since you can't be sure when you will be asked to return your cell at IndexPath (0,0)

How to control the contents of a table view based on cell selection in a collection view?

I have a collection view with 4 cells and a table view inside the same view controller. Each cell in the CV must make the table view display a different list; one table cell type per CV cell. When the user taps on one CV cell the table must reload its data and display the correct type of cell, (basically a different list with a custom cell). Each of these lists contains only 1 section.
I created and registered all my different cells with the table view and have all my data ready, with the views set and so on. How do I make the table view discard its current cells (list) and show a new list with different cells when the user has tapped on another CV cell?
I suppose that the solution is in the didSelectItem method from the CV delegate but I cannot find any information that shows how to make the table dequeue a different type of cell when the user has changed the cell selection inside the CV; or discard the previous one if needed.
At the moment I only register and dequeue one type of cell, and inside the delegate method for the CV I am calling empty functions that are supposed to put the new list inside the table.
The number of rows for each list is dynamic and this implies that I would have to call the delegate method on the table view again.
I have found an example of the MVVM pattern but I cannot apply it to my logic as that is more static.
Any help would be much appreciated. Thank you.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedMenuTab = indexPath.item
switch selectedMenuTab { // show different type of cell.
case 0: showAList()
case 1: showBList()
case 2: showCList()
case 3: showDList()
default:
print("no such main tab")
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableOfConversations.dequeueReusableCell(withIdentifier: rowID, for: indexPath) as! ConversationsListCell
let messageInChatList = listOfOneToOneChats[indexPath.item]
cell.messageInChatList = messageInChatList
cell.selectionStyle = .none
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listOfOneToOneChats.count
}
I think this should be as simple as calling tableView.reloadData() at the end of the collection view delegate's didSelectItemAt method.
The tableView should have it's data source based on a common array, for example:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return displayingList.count
}
And then in the didSelect of the collection view set that array and then tell the tableView to reload:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedMenuTab = indexPath.item
switch selectedMenuTab { // show different type of cell.
case 0: displayingList = listA
case 1: displayingList = listB
case 2: displayingList = listC
case 3: displayingList = listD
default:
print("no such main tab")
}
tableView.reloadData()
}
And for dequeueing cells, check based on the type if they are different, or based on selectedMenuTab:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UICollectionViewCell
let selectedMenuTab = indexPath.item
switch selectedMenuTab { // show different type of cell.
case 0: cell = tableOfConversations.dequeueReusableCell(withIdentifier: rowID, for: indexPath) as! ConversationsListCell
//And so on
default:
fatalError("No cell for this tab")
}
let item = displayingList[indexPath.item]
// Cell setup
return cell
}
The type of the values will be something to consider in order to avoid an array of Any, which would not be super type-safe, but that will depend on the types of your objects.

How to have different images at different sections of a single TableViewcell, having collection view placed inside a tableviewCell

I want different images under different heading
//ViewController.swift
var categories = ["Good Afternoon","Daily Mixes","Bollywood Gems","Daily Mixes","Bollywood Gems","Bollywood Romantic","Pop","Trending Now","Top Charts","Saavn Originals","New Releases","Top Playlists","Radio Stations","Editorial Picks" ]
func numberOfSections(in tableView: UITableView) -> Int {
return categories.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return categories[section]
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! CategoryRowTableViewCell
return cell
}
// custom.swift
#IBOutlet weak var pic: UIImageView!
#IBOutlet weak var text: UILabel!
func execute()
{
print("This is working")
pic.layer.cornerRadius = pic.frame.size.width/2
pic.clipsToBounds = true
print("working")
}
// CategoryRowTableViewCell.swift
let main : [String] = ["black forest","butterscotch","Truffle","a","b","c","d","e","j","k","u","q","s","z"]
#IBOutlet weak var collect: UICollectionView!
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 14
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collect.dequeueReusableCell(withReuseIdentifier: "videocell", for: indexPath) as! custom
cell.execute()
cell.pic.image = UIImage(named : main[indexPath.row])
cell.text.text = main[indexPath.row]
print(cell.text.text!)
return cell
}
I am having a table view in that I am having an table view cell.
I am having different sections of table view cell .
In That cell a scroll view, on it collection view and collection view cell.
My Collection view cell contain a image and a label
I want to display different images in different row in collection view cell. But I am getting same images in every section of table view cell.Please help me in this.
UICollectionView inherits from UIScrollView meaning you do not need to have a UICollectionView in a Scroll view if I am understanding your structure correctly. The collection view will scroll once the images/content exceed the views frame. UITableViews also inherit from UIScrollView so it will also already have the functionality to scroll once content size exceeds the table view's frame.
Having a collection view in every table view cell might not be the best way to achieve what you need/want. However the issue seems to be that you are assigning the same data source to each collection view, but without seeing any code I will have to guess that you are creating a collection view assigning it the data and then adding it to each table view cell.

Images In a Collection View

I am coding in Swift 3.2, and inside of a TableView Controller have a TableView (scrolling vertically), and each row of the table holds a CollectionView (scrolling horizontally).
I am not sure if it is acceptable coding practice, but I made my ViewController class inherit UITableViewController and UICollectionViewDelegate and UICollectionViewDataSource.
For now, I have a hard coded array of image names and would like three images to scroll horizontally in each collection view, with each row having distinct images. However, as of now each row shows the same three images ("11.png", "12.png", "13.png"), and I was wondering how to fix this.
Below is my ViewController class.
//for the tableView
let event = generateRandomData()
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return event.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
return cell
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
guard let tableViewCell = cell as? TableViewCell else { return }
tableViewCell.setCollectionViewDataSourceDelegate(self, forRow: indexPath.row)
}
//for the collectionView
var images: [String] = [
"11.png", "12.png", "13.png",
"21.png", "22.png", "23.png",
"31.png", "32.png", "33.png",
"41.png", "42.png", "43.png",
"51.png", "52.png", "53.png",
"61.png", "62.png", "63.png",
"71.png", "72.png", "73.png",
"81.png", "82.png", "83.png",
"91.png", "92.png", "93.png",
"101.png", "102.png", "103.png",
]
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
collectionView.isPagingEnabled = true; //makes the horizontal scrolling have no bounce and relatively have a better snap
let myCell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath as IndexPath) as! CollectionViewCell
let myCellImage = UIImage(named: images[indexPath.row])
myCell.eventImage.image = myCellImage
return myCell
}
I am an amateur coder, so any help is greatly appreciated!
I am sure this problem already have an answer somewhere, but since I can't find that I'll just give one here.
Your mistake is here:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
/...
let myCellImage = UIImage(named: images[indexPath.row]) //<< indexPath.row will return 0,1 and 2 for each of your collection view
/...
}
Each collection view in your table view does not communicate amongst themselves to form a larger collective view. Since all of the are provided the int of 3 for numberOfItemsInSection and their numberOfSections defaulted to 1(you did not set any), each of them will request for item at indexpath (0,0), (0,1) and (0,2) when trying to populate their data.
Your hardcoded array however, is an array with 30 items. So each collection view when requesting for cellForItemAt will only get 0,1 and 2 for images[indexPath.row]. Also you should use indexPath.item for collection view, indexPath.row is for table views, it may or may not work if used wrongly.
Solution
You can just set the tag property of the collection view to mark it's row:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
/...
//Make sure you have a subclass for UITableViewCell that have an IBOutlet that connects to the collection view
cell.collectionView.tag = indexPath.row //Note that this only works in your situation where the table view have only 1 section.
/...
}
Then split your array into a 2D array:
var images = [
["11.png", "12.png", "13.png"],
["21.png", "22.png", "23.png"],
["31.png", "32.png", "33.png"],
["41.png", "42.png", "43.png"],
["51.png", "52.png", "53.png"],
["61.png", "62.png", "63.png"],
["71.png", "72.png", "73.png"],
["81.png", "82.png", "83.png"],
["91.png", "92.png", "93.png"],
["101.png", "102.png", "103.png"]
]
Finally read the array in the collection view like:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
/...
let myCellImage = UIImage(named: images[myCell.tag][indexPath.item])
/...
}

Cell takes index of its previous cell - Collectionview inside Tableview

I want to dynamically add collection view inside tableview. I have make following code.
A cell class for collectionview
class NewsFeedCollectionViewCell : UICollectionViewCell{
#IBOutlet weak var imageViewSlider: UIImageView!
}
Than assign collectionview in Tableview cellforrow at indexpath
cell.collectionViewNewsFeed.tag = indexPath.row
cell.collectionViewNewsFeed.reloadData()
Than added following delegates of collectionview
// MARK: - Collection view Delegates / Datasources
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return (mutableArrayNewsFeed[collectionView.tag]["images"] as! NSArray).count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("NewsFeedCollectionViewCell", forIndexPath: indexPath) as! NewsFeedCollectionViewCell
print("tag : \(collectionView.tag) , Row : \(indexPath.row)")
let img = (mutableArrayNewsFeed[collectionView.tag]["images"] as! NSArray)[indexPath.row] as? String ?? "imgLoginScreenLogo"
cell.imageViewSlider.image = UIImage(named: img)
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
print("Collection View Row : \(indexPath.row)")
}
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize{
return collectionView.frame.size
}
Its adjusting properly but the indexes gets changes while i scroll the collectionview. For ex, I scroll collectionview upto 3 cell than i go to the tableview 4th index than it also set the index of 4th index of collectionview to 3rd.
Simply want to add collection view with multiple images inside Table view. I have added, but after scrolling the collection view to 3rd image on 1st cell of tableview, i move to 4th cell of Tableview, there also the collectionview gets scrolled automatically upto 4th cell.
Need to get out of this.
There was an issue with the indexpath, I get the solution by keeping the indexpath in an array.
Refer Following link
https://github.com/ashfurrow/Collection-View-in-a-Table-View-Cell