retain scroll position for nested collectionView - ios

After the tableView.reloadData() the visible collectionView display the first row unexpected immediately.
Im building a tableView contains collectionView in its cells, users can scroll multiple images in every single tableView just like Instagram. How can I fix it? Thanks!
tableView DataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return photoRolls.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableViewCell", for: indexPath) as! HomeTableViewCell
if photoRolls.isEmpty {
return UITableViewCell()
}
let user: UserModel = users[indexPath.row]
let photoRoll: PhotoRoll = photoRolls[indexPath.row] //this Model contains post info: likes, comments etc.
let photoUrls: UrlStrings = urls[indexPath.row] //this Model contains a array of urlStrings for each collectionView inside the tableViewCell
cell.urlStrings = photoUrls
cell.photoRoll = photoRoll
cell.user = user
cell.delegate = self
return cell
}
prepareForReuse Method in tableViewCell
override func prepareForReuse() {
super.prepareForReuse()
captionLabel.text = nil
profileImage.image = UIImage(named: "placeholderImg")
profileImageRight.image = UIImage(named: "placeholderImg")
collectionView.scrollToItem(at:IndexPath(item: 0, section: 0), at: .left, animated: false)//tried to remove this method, but the collectionView would not display the first row when it's visible
}
DataSource of collectionView inside tableViewCell
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cellUrlArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HomeCollectionViewCell", for: indexPath) as! HomeCollectionViewCell
cell.url = cellUrlArray[indexPath.row]
return cell
}
Like the question title said. I expect the visible collectionView stays on the current row after the tableView load more data after tableView.reloadData() is called! Thanks again!

I think it is possible with contentOffset cacheing, like below
var cachedPosition = Dictionary<IndexPath,CGPoint>()
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if let cell = cell as? HomeTableViewCell {
cachedPosition[indexPath] = cell.collectionView.contentOffset
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
<<Your Code>>
cell.collectionView.contentOffset = cachedPosition[indexPath] ?? .zero
return cell
}

Related

Populating dynamic data in CollectionView which is inside TableView

Main View Controller
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return CategoryNames.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell=tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath as IndexPath) as! CategoryTableViewCell
cell.lblCategoryName.text = CategoryNames[indexPath.row] as? String
cell.ViewCategogyBackground.layer.cornerRadius=20
cell.ViewCategogyBackground.layer.maskedCorners = [.layerMaxXMinYCorner]
cell.arrayForCollectionView = [["xx","yy"],["zz","vv","tt"],["hello"],["11","44"]]
cell.reloadCollectionView()
return cell
}
Table View Cell
var arrayForCollectionView : [[String]]!
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if (arrayForCollectionView != nil) {
return arrayForCollectionView.count
}
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CategoryItemCollectionViewCell
let rowValue = arrayForCollectionView[indexPath.row]
for i in 0..<rowValue.count {
cell.lblItemName.text = rowValue[i] as? String
}
return cell
}
Outputhere is my output I want to display data like first section of 2D array under t1(Category 1) and second section under t2(category 2). both categories and items in collection view are dynamic
You need to create a CollectionView inside TableView and create outlet of collectionView inside tableViewCell like this:
class TimingSlotsTVCell: UITableViewCell
{
#IBOutlet weak var timeSlotsCV: UICollectionView!
}
Now in your cellForRow of tableView do this:
let cell = tableView.dequeueReusableCell(withIdentifier: "TimingSlotsTVCell") as! TimingSlotsTVCell
cell.timeSlotsCV.reloadData()
return cell
Now collectionView cell will be called from inside tableView

Find in which row of a tableView is a CollectionView

I'm asking for your help because I blocked with something in my Swift App.
I would like to parse an array with some meals.
My array is the next one:
let meals_list: [[String]] = [
["breakfast", "snack_1", "pre_intra_post", "lunch", "snack_2", "diner"],
["breakfast", "snack_1", "lunch", "snack_2", "diner"]
]
As you can see, there are 6 items in the first part, and 5 items in the second one.
These meals are going to be displayed in a horizontal CollectionView.
Each CollectionView needs to be in a table view.
So, I would like to create 6 cards in my first collection view
And 5 cards in my second collection view.
Here is how I manage everything
//Manage meals content
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if section == 0{
return 6
} else {
return 5
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collection_cell", for: indexPath) as! Meal_vc_cell
return cell
}
//Manage day type content (train/rest)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "food_plan_cell", for: indexPath) as! FoodPlanCell
if (indexPath.row == 0){
cell.meals_day_type.text = "Vos repas les\r\njours d'entraƮnement".uppercased()
} else {
cell.meals_day_type.text = "Vos repas les\r\njours de Repos".uppercased()
}
return cell
}
private func tableView(tableView: UITableView,
willDisplayCell cell: UITableViewCell,
forRowAtIndexPath indexPath: NSIndexPath) {
guard let tableViewCell = cell as? FoodPlanCell else { return }
tableViewCell.setCollectionViewDataSourceDelegate(dataSourceDelegate: self, forRow: indexPath.row)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collection_cell", for: indexPath) as! Meal_vc_cell
cell.meal.text = meals_list.[indexPath.section][indexPath.item]
return cell
}

How to show images in horizontal collection view inside tableview swift 5

I want to show image using sdWebImage to an image inside collectionView Cell which is inside tableView cell. How to do it ?
I have array of images and I want to show it in collection view cell
this is what I tried
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let imgUrl = (myArray[indexPath.row] as? String)
{
//myArray = image array
if let url = URL(string: imgUrl as! String) {
cell.imgView.sd_setImage(with: url, placeholderImage: UIImage(named: "product.png"), options: .lowPriority)
}
}
}
I already set
func setCollectionViewDataSourceDelegate(dataSourceDelegate: UICollectionViewDataSource & UICollectionViewDelegate, forRow row: Int) {
collectionVIew.delegate = dataSourceDelegate
collectionVIew.dataSource = dataSourceDelegate
collectionVIew.tag = row
collectionVIew.reloadData()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "DashboardTableViewCell") as! DashboardTableViewCell
cell.delegate = self
cell.selectedIP = indexPath
cell.collectionVIew.tag = indexPath.row
cell.collectionVIew.reloadData()
return cell
}
But I don't know how to show images inside collection view in particular tableview section.
Please Help.
Set Collection view Deleagte and dataSource inside the table view cellforRow method
collectionview.delegae = self
collectionview.datasource = self
As mention by #AjinkyaSharma You have to rearrange your data structure, where it would be an array of arrays.
let myImages: [[String]] = [
["image_url_0_0", "image_url_0_1", "image_url_0_2", "image_url_0_3"],
["image_url_1_0", "image_url_1_1", "image_url_1_2"],
["image_url_2_0", "image_url_2_1", "image_url_2_2", "image_url_2_3", "image_url_2_4"],
["image_url_3_0", "image_url_3_1"]
]
Now you tableview cellForRowAt will set the required array of images for collectionView
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myImages.size
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "DashboardTableViewCell") as! DashboardTableViewCell
cell.delegate = self
cell.selectedIP = indexPath
cell.collectionVIew.tag = indexPath.row
cell.myArray = myImages[indexPath.row]
cell.collectionVIew.reloadData()
return cell
}
Now you need to use your myArray array to display your collectionView cells
One of your url is not able to satisfy if let condition. Set default image to nil to make sure not to show any previous data.
Try This:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
cell.imgView.image = nil // set default image nil
if let imgUrl = (myArray[indexPath.row] as? String)
{
//myArray = image array
if let url = URL(string: imgUrl as! String) {
cell.imgView.sd_setImage(with: url, placeholderImage: UIImage(named: "product.png"), options: .lowPriority)
}
}
}
Try With Below Code
class YourCustomizeTableViewCell: UITableViewCell {
let collectionView: CollectionView
...
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HorizontalSlideCell", for: indexPath) as! YourCustomizeTableViewCell
cell.collectionView.tag = indexPath.row
return cell
}
...
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "InnerCollectionViewCell",
for: indexPath as IndexPath)
//indexPath.section is the collectionview section index but needs to be its parent tableview section's index. How do I get it?
cellCharLabel?.text =
Languages.sharedInstance.alphabets[collectionView.tag].set[indexPath.row].char
...
return cell
}
func setCollectionViewDataSourceDelegate<D:UICollectionViewDataSource & UICollectionViewDelegate>(datasourcedelegate : D, forRow row:Int){
collectionVIew.delegate = datasourcedelegate
collectionVIew.dataSource = datasourcedelegate
collectionVIew.tag = row
collectionVIew.isPagingEnabled = row == 0 ? false : true
collectionVIew.accessibilityHint = row == 0 ? "category" : "offers"
collectionVIew.reloadData()
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
guard let cell = cell as? DashboardTableViewCell else {
return
}
cell.setCollectionViewDataSourceDelegate(datasourcedelegate: self, forRow: indexPath.section)
}
extension HomeViewController : UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return array.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PopularCollectionViewCell.identifier, for: indexPath) as? PopularCollectionViewCell else {
return UICollectionViewCell()
}
cell.backgroundColor = .clear
cell.setValue(data: popularItemList[indexPath.row])
return cell
}
}
extension HomeViewController : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width:120, height: itemWidth + 82)
}
}

Shortage on UITableView cell count

I am currently using two prototype cells to have my collectionView in top cell moving horizontally while all other cells moves vertical. It's still short one cell count at the bottom and I can't seem to figure out why.
This is the code. Can you point out where the issue is please?
//Mark:- Data arrays
var dataArray: [String] = ["c1","c2","c3","c4","c5"]
var cellArray: [String] = ["10","11","12","13","14","15"]
//Mark:- UITableView Delegate
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "MainTableViewCell") as! MainTableViewCell
return cell
} else {
let cell2 = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell") as! CustomTableViewCell
cell2.cellImage.image = UIImage(named: cellArray[indexPath.row])
return cell2
}
}
//Mark:- UICollectionView Delegate
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "InsideCollectionViewCell", for: indexPath) as! InsideCollectionViewCell
cell.myImage.image = UIImage(named: dataArray[indexPath.row])
return cell
}
It seems you want your table view to contain the values in your cellArray array plus one extra special row at index 0.
In order to do this you need to indicate that there is an extra row and your indexing needs to account for the extra row.
But a simpler approach is to use multiple sections in your table view. Use section 0 for the extra special row and use section 1 for the values in your cellArray.
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return section == 0 ? 1 : cellArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "MainTableViewCell") as! MainTableViewCell
return cell
} else {
let cell2 = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell") as! CustomTableViewCell
cell2.cellImage.image = UIImage(named: cellArray[indexPath.row])
return cell2
}
}
Make sure you adjust for the use of multiple sections in any other table view method you may implement (such as didSelectRowAt, etc.).
For the sake of comparison, here is how you would need to change your code if you want all of the rows in one section:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellArray.count + 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "MainTableViewCell") as! MainTableViewCell
return cell
} else {
let cell2 = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell") as! CustomTableViewCell
cell2.cellImage.image = UIImage(named: cellArray[indexPath.row - 1])
return cell2
}
}

Not Recognizing Clicked Cell UITableView inside UITableViewCell using NIB

I'm writing an app that have a dashboard with multiple cells. One of the cells have a question, but the answer are dynamically filled, so I decided to use a UITableView to handle it.
I set the the UITableViewCell as the delegate and dataSource of the internal UITableView and made the configurations for define the cell and the selected state.
extension SurveyTableViewCell: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
answers = model.getSurveyAnswers()
return (answers?.count)!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier, for: indexPath) as! InsideSurveyTableViewCell
cell.idAnswer.text = alphabetQuestion[indexPath.row]
cell.answer.text = answers?[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 100.0
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier, for: indexPath) as! InsideSurveyTableViewCell
cell.selectRow()
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier, for: indexPath) as! InsideSurveyTableViewCell
cell.deselectRow()
}
}
But the click inside the cell in the internal UITableViewCell is not recognized. I need to recognize this click to after send the user answer to the server.
I saw some solutions, but using storyboard. I use only nib's on my projects.
But I still tried with an approach that I saw on youtube wich uses storyboard.
On the cell that will use the internal UITableView I declared a function to set the delegate and dataSource of the internal tableView and gave to it a tag.
extension SurveyTableViewCell {
func setTableViewDataSourceDelegate<D:UITableViewDelegate & UITableViewDataSource>(_ dataSourceDelegate: D, forRow row: Int) {
subTableView.dataSource = dataSourceDelegate
subTableView.delegate = dataSourceDelegate
subTableView.reloadData()
}
}
Than on the viewController that manage the outer UITableView:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView.tag == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier) as! InsideSurveyTableViewCell
cell.idAnswer.text = "A."
cell.answer.text = "QUALQUER COISA"
return cell
}
if retrivedCell is SurveyTableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: SurveyTableViewCell.identifier, for: indexPath) as! SurveyTableViewCell
cell.delegate = self
cell.setTableViewDataSourceDelegate(self, forRow: indexPath.row)
cell.setPositionRow(row: indexPath.row - 1)
cell.subTableView.rowHeight = UITableViewAutomaticDimension
cell.subTableView.estimatedRowHeight = 50
return cell
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView.tag == 1 {
return 3
}
var numberOfCells: Int = 0
if cellsToPresent != nil {
numberOfCells = cellsToPresent!.count
}
return numberOfCells + 1
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView.tag == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier) as! InsideSurveyTableViewCell
cell.selectRow()
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if tableView.tag == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier) as! InsideSurveyTableViewCell
cell.deselectRow()
}
}
The selectRow and deselectRow are methods to change the label of the cell of the inner tableView.
But still without success.
if I use the method:
tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
The app break complaining that I'm trying to dequeue different cells with the same indexPath.
Thanks for your help.
Don't use
let cell = tableView.dequeueReusableCell(withIdentifier:) in didSelect or didDeSelect methods.
Use
let cell = tableView.cellForRow(at: indexPath)
I hope this will help you.

Resources