I am creating a scrollable view filled with monthly calendars. I'm using a collection view to display a calendar inside a table view full of calendars. So each table view cell is a calendar for a specific month, and each collection view cell is a day. I have a separate swift file for the tableview cell from the view controller. Since each tableview cell is going to look different (because different months), the tableview cell needs to know which row it is placed in inside the tableview during its creation in dequeque cell function.
tableView.dequeueReusableCell(withIdentifier: "CalendarTableViewCell", for: indexPath)
I need to get the indexPath in the "for: indexPath" parameter inside the tableview cell file because the collectionview inside the tableview cell gets created when the tableview cell is dequeued. The contents of the collection view depends on which tableview row it's in. So how do I get that parameter?
Sorry for the long explanation, please help if possible. Thank you!
Create an array in UITableViewCell subclass and use that array in collection view data source methods.
class MonthCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
let datesArray = [String]()
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return datesArray.count
}
}
In tableView cellForRowAt method assign the date values and reload the collectionView
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell") as! MonthCell
cell.datesArray = []//dates based on indexPath
cell.collectionView.reloadData()
return cell
}
Or
Assign a reference to the table view cell index path in tableView cellForRowAt method
class MonthCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
var tableIndexPath:IndexPath?
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let tableIndexPath {
// return value based on tableIndexPath
} else {
return 0
}
}
}
//cellForRowAt
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MonthCell") as! MonthCell
cell.tableIndexPath = indexPath
cell.collectionView.reloadData()
return cell
}
Related
I have a UICollectionView with flow layout, about 140 cells each with a simple UITextView. When a cell is recycled, I pop the textView onto a cache and reuse it later on a new cell. All works well until I reach the bottom and scroll back up. At that point I can see that the CollectionView vends cell number 85, but then before cell 85 is displayed it recycles it again for cell 87 so I now lose the content of the cell I had just prepared.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FormCell", for: indexPath) as! FormCollectionViewCell
let textView = Cache.vendTextView()
textView.text = "\(indexPath.row)"
cell.addSubview(textView)
cell.textView = textView
return cell
}
And on the UIcollectionViewCelC
override func prepareForReuse() {
super.prepareForRuse()
self.textView.removeFromSuperView()
Cache.returnView(self.textView)
}
I would have thought that after cellForItemAtIndexPath() was called, it would then be removed from the reusable pool of cells but it seems it is immediately being recycled again for a neighbouring cell. maybe a bug or I am possibly misunderstanding the normal behaviour of UICollectionView?
As I understand it, what you're trying to do is just keep track of cell content - save it when cell disappears and restore it when it comes back again. What you're doing can't work well for couple of reasons:
vendTextView and returnView don't take indexPath as parameter - your cache is storing something and fetching something, but you have no way of knowing you're storing/fetching it for a correct cell
There's no point in caching the whole text view - why not just cache the text?
Try something like that:
Have your FormCollectionViewCell just have the text view as subview, and modify your code like so:
class YourViewController : UIViewController, UICollectionViewDataSource, UICollectionViewDelegate
{
var texts = [IndexPath : String]()
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FormCell", for: indexPath)
if let formCell = cell as? FormCollectionViewCell {
cell.textView.text = texts[indexPath]
return cell
}
}
func collectionView(_ collectionView: UICollectionView,
didEndDisplaying cell: UICollectionViewCell,
forItemAt indexPath: IndexPath)
{
if let formCell = cell as? FormCollectionViewCell {
{
texts[indexPath] = formCell.textView.text
}
}
}
This question already has answers here:
ios 8 Swift - TableView with embedded CollectionView
(3 answers)
Closed 4 years ago.
]2I have use one tableView. Inside tableViewCell i want to populate a collectionView.
I have a static array
Here is my code
var menuImage = ["download.jpeg","download (1).jpeg","download (2).jpeg","download (3).jpeg","download (4).jpeg","download (3).jpeg","download (4).jpeg","download (3).jpeg","download (4).jpeg"]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return menuImage.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:HomeTableViewCell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
// cell.collectionView.reloadData()
return cell
}
inside tableViewCell ->
class HomeTableViewCell: UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
and this is the collectionView
extension HomeVC: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return menuImage.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HomeCollectionViewCell", for: indexPath) as! HomeCollectionViewCell
cell.imgvw.image = UIImage(named: menuImage[indexPath.row])
cell.profileName.text = menus[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Collection view at row \(collectionView.tag) selected index path \(indexPath)")
}
}
Still the static data is not showing. please help
If you want to show all images below username than you need to change the Layout. it's not possible in that layout.
so my suggestion is : You display only user details in HomeVC, once any row is tapped, move to new screen and show user details along with all images like instagram user profile.
Edit of your Demo : https://www.dropbox.com/s/v05k2udqa3pu1dd/Demp1.zip?dl=0
I would suggest you to Follow the steps mentioned in this Video Collection View inside TableView cell
Moreover you can refer this link also:
Collectionview in tableview cell
Hope this helps.
Issue in Image added
As stated by you , you have added the image directly to Xcode i.e. Drag and Drop,
So do cross check if the Target Member Ship is ticked or not.? If not the please tick that.
Also it is suggested to name the image properly like image1,image2,image3,image4,etc so that it gets detected without any issue.
Edit Link to a demo Project added
You can find a demo of Collection View inside a tableView cell here: Demo of imageCollectionView inside TableViewCell
So I have collection view with 3 cells, each of which has a tableview in it. Obviously, each collection view cell has to load it's table view with different data for the table view cells.
My first thought was to make the collection view the delegate and datasrouce of the table view, but even so, the datasource will need to know which collection view cell is being loaded.
Also thought of making a separate class for the DS and delegate for both the collection view and the table view, but then again, I'm stuck on how the tableview DS will know which collection view cell it's being loaded from.
Any thoughts?
EDIT:
After I make the assignment, the datasource field is nil. So what am I missing here?
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) else {
return UICollectionViewCell.init()
}
cell.backgroundColor = UIColor.clear
cell.theTableView.dataSource = RequestedTableDataSource.init()
cell.theTableView.delegate = self
cell.theTableView.reloadData()
return cell
}
class RequestedTableDataSource: NSObject, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 16
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "RequestedCell") as? IntervieologistRequestedCell else {
return UITableViewCell.init()
}
cell.setupCellFor(name: "Dan", image: UIImage.init(named: "dan")!)
return cell
}
}
If I do this:
let foo = RequestedTableDataSource.init()
cell.myTableView.dataSource = foo
then the dataSource field is set, numberOfRows gets called, but cellForRowAtIndexPath doesn't.
You can set the tableView.tag to an unique integer and identify it later in the delegate methods using that, but I would recommend keeping a seperate datasource/delegate for each tableview, much cleaner.
Don't make the CollectionView or any other View object the datasource/delegate.
eg set
collectionViewCell.tableView.tag = index;
collectionViewCell.tableView.delegate = commonDelegate;
or
collectionViewCell.tableView.delegate = uniqueDelegate; // Better choice
i have a uiviewcontroller, where i put a uitableview, with custom cells. Each of these cells, have inside a uicollectionview, with a horizontal scrolling.
I am working programmatically, so the tableviewcell is the delegate/datasource for collectionview.
My datasource structure, is an array of arrays.
My tableview, has, for every tableviewcell, a tableview section.Tableview datasource methods work without problem using the array. I am able to set up the tableview correctly.
Inside this cell, i want to display a collectionview, with horizontal scrolling.
The problem which i am encountering is i cannot assign datasource correctly using the arrays to collectionview. What happens is when i load the tableview, and scrolling down, i see duplicate records, so for example the first 3 rows are displayed correctly(in terms of data and number of items),but from the 4th row, for example, data is duplicated, so i see again the records for the first 3 rows, and of course this does not have to happen, because if i scroll on the 4th line(the collectionview on the 4th row) the app crashes due to an index problem out of range.
This is simple to understand: lets say i have 10 cells on the first row, and 5 on the 4th row/or section, as you prefer... Scrolling the 4th row'scollection view will cause the crash. This is because of the wrong data which is actually the same from the first row: instead, i have only 5 cells to render...
Tecnique i am using is pretty simple: give each tableviewcell's tag the actual indexpath.section. Then in the collectionview, use this tag to loop through array and then the indexpath.item to get the correct array.
Lets get a look into the code now:
Tableview
func numberOfSections(in tableView: UITableView) -> Int {
return DataManager.shared.datasource.count
}
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! DiarioTableViewCell
cell.tag = indexPath.section
return cell
}
TableviewCell
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: .default, reuseIdentifier: "cell")
collectionView.register(DiarioCVCell.self, forCellWithReuseIdentifier: "cellCV")
collectionView.delegate = self
collectionView.dataSource = self
DataManager.shared.istanzaCV = self.collectionView
addSubview(tableCellBG)
addSubview(collectionView)
setConstraints()
}
CollectionView
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let tvCell = collectionView.superview as! DiarioTableViewCell
return DataManager.shared.datasource[tvCell.tag].count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellCV", for: indexPath) as! DiarioCVCell
let celltag = (collectionView.superview as! DiarioTableViewCell).tag
cell.datasource = DataManager.shared.datasource[celltag][indexPath.item]
return cell
}
Notice i have read all the possibly related threads, even ashfurrow's article, and here on stack, but i was not able to find a solution.
Thanks for any help!
Try to reload your collection view when cell is showing. Cells are being reused to save memory so they are created only once - you are setting all data at init and it's staying there forever.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! DiarioTableViewCell
cell.tag = indexPath.section
cell.collectionView.reloadData()
cell.collectionView.collectionViewLayout.invalidateLayout() //just to be sure, maybe it's not necessary
return cell
}
I have a weird issue with UICollectionView and UITabBarController. Inside the UITabBarController i have references to two different ViewControllers. If i put View Controller that has UICollectionView as a first page of UITabBarController then my list with cells (UICollectionView) is loading normally like this:
But if i put it as a second View Controller when i open the tab it is like UIImageView and UILabel are disappearing from the cells:
I have checked collectionView method that is getting the cells and there is always data printed for the Label and Image. Here is a code of the methods for DataSource and Delagate
// tell the collection view how many cells to make
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.items.count
}
// make a cell for each cell index path
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
// get a reference to our storyboard cell
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! AdsCollectionViewCell
// Use the outlet in our custom class to get a reference to the UILabel in the cell
cell.myLabel.text = self.items[indexPath.item]
cell.backgroundColor = UIColor.whiteColor() // make cell more visible in our
cell.layer.borderColor = UIColor.grayColor().CGColor
cell.layer.borderWidth = 1
cell.layer.cornerRadius = 8
cell.layoutIfNeeded()
return cell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let width = collectionView.frame.width - 22;
return CGSize(width: width/2, height: width/2);
}
// MARK: - UICollectionViewDelegate protocol
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
// handle tap events
print("You selected cell #\(indexPath.item)!")
}
Is there someone that had problem like this? How can it be solved?