I have a UITableView that has one UICollectionView and
I used numberOfRowsInSection to set the number of rows and cellForRowAt to show cells with title:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return sectionsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let strname = sectionsArray[indexPath.row]
if (strname as AnyObject).isEqual(to: "top"){
tableCellobj = tableView.dequeueReusableCell(withIdentifier: "cellmiddle", for: indexPath) as! TableCellMiddle
tableCellobj.lblMiddle.text = "\(strname)"
return tableCellobj
}
if (strname as AnyObject).isEqual(to: "bottom") {
tableCellobj = tableView.dequeueReusableCell(withIdentifier: "cellmiddle", for: indexPath) as! TableCellMiddle
tableCellobj.lblMiddle.text = "\(strname)"
return tableCellobj
}
As you can see the Array sectionsArray, is:
var sectionsArray: NSMutableArray = NSMutableArray()
sectionsArray.add("top")
sectionsArray.add("bottom")
Now the table shows two rows with headers "top" and "bottom"
I have two diffrent data as NSArray to be shown in the CollectionView:
top = ["1","2","3"]
bottom = ["4","5","6"]
the collectionView has a tag (identified in Storyboard), so i used the tag to defined the view and show data:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
if collectionView.tag == 2 {
objCollectionViewCellMiddle = collectionView.dequeueReusableCell(withReuseIdentifier: "collectioncellmiddle"
, for: indexPath) as! CollectionViewCellmiddle
objCollectionViewCellMiddle.lblMiddle.text = top[indexPath.row] as? String
return objCollectionViewCellMiddle
}
Now, the TableView shows two CollectionViews, but it prints same data from top array in both CollectionView
I want:
1st CollectionView ---> show the top array
2nd CollectionView ---> show the bottom array
I played around with indexPath.section, but couldn't figure away to make it work as it always prints "0"
Okay, I think what we need to do here is change around how you're setting things up. First off, we should change the backing data source to be a value type such as [String]:
MyViewController Class
class MyViewController: UIViewController {
var sectionsArray: [String] = ["top", "bottom"]
var topSection: [String] = ["1","2","3"]
var bottomSection: [String] = ["4","5","6"]
#IBOutlet weak var myTableView: UITableView!
#IBOutlet weak var topCollectionView: UICollectionView!
#IBOutlet weak var bottomCollectionView: UICollectionView!
}
Then, we need to modify the data sources for the table view and collection views:
Table View Data Source
extension MyViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return sectionsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let sectionName = sectionsArray[indexPath.section]
switch sectionName {
case "top":
let tableCell = tableView.dequeueReusableCell(withIdentifier: "cellmiddle", for: indexPath) as! TableCellMiddle
tableCell.lblMiddle.text = sectionName
return tableCell
case "bottom":
let tableCell = tableView.dequeueReusableCell(withIdentifier: "cellmiddle", for: indexPath) as! TableCellMiddle
tableCell.lblMiddle.text = sectionName
return tableCell
default:
return UITableViewCell()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0:
return topSection.count
case 1:
return bottomSection.count
default:
return 0
}
}
}
Here, in the cellForRow method you can actually reduce some of the duplicate code as you're doing the same thing regardless of which cell you're at.
Collection View Data Source
extension MyViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
switch collectionView {
case topCollectionView:
return topSection.count
case bottomCollectionView:
return bottomSection.count
default:
return 0
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
switch collectionView {
case topCollectionView:
let collectionCell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectioncellmiddle", for: indexPath) as! CollectionViewCellmiddle
collectionCell.lblMiddle.text = topSection[indexPath.row]
return collectionCell
case bottomCollectionView:
let collectionCell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectioncellmiddle", for: indexPath) as! CollectionViewCellmiddle
collectionCell.lblMiddle.text = bottomSection[indexPath.row]
return collectionCell
default:
return UICollectionViewCell()
}
}
}
Here what we're doing is switching on the actual collection view itself that gets passed in as an argument rather than a tag that you set, that way we can easily see which collection view is currently being accessed. Once we know this we are able to determine which of your backing arrays we need to access and can assign the text accordingly.
You can also reduce some of the duplicate logic in this method if you'd like.
This is a playground that compiles (I will add functionality later to demonstrate it so it shows up in the timeline). I think it will help you achieve what you're trying to do.
Well I feel the problem is in this function:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
if collectionView.tag == 2 {
objCollectionViewCellMiddle = collectionView.dequeueReusableCell(withReuseIdentifier: "collectioncellmiddle"
, for: indexPath) as! CollectionViewCellmiddle
objCollectionViewCellMiddle.lblMiddle.text = top[indexPath.row] as? String
return objCollectionViewCellMiddle
}
}
The data assignment shows:
objCollectionViewCellMiddle.lblMiddle.text = top[indexPath.row] as?
String
I suggest you change the tableview code by adding the tag code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let strname = sectionsArray[indexPath.row]
if (strname as AnyObject).isEqual(to: "top"){
tableCellobj = tableView.dequeueReusableCell(withIdentifier: "cellmiddle", for: indexPath) as! TableCellMiddle
Add this: tableCellobj.collectionView.tag = 1
tableCellobj.lblMiddle.text = "\(strname)"
return tableCellobj
}
if (strname as AnyObject).isEqual(to: "bottom") {
tableCellobj = tableView.dequeueReusableCell(withIdentifier: "cellmiddle", for: indexPath) as! TableCellMiddle
Add this: tableCellobj.collectionView.tag = 2
tableCellobj.lblMiddle.text = "\(strname)"
return tableCellobj
}
}
And then:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
if collectionView.tag == 1 {
objCollectionViewCellMiddle = collectionView.dequeueReusableCell(withReuseIdentifier: "collectioncellmiddle"
, for: indexPath) as! CollectionViewCellmiddle
objCollectionViewCellMiddle.lblMiddle.text = top[indexPath.row] as? String
return objCollectionViewCellMiddle
}
else {
objCollectionViewCellMiddle = collectionView.dequeueReusableCell(withReuseIdentifier: "collectioncellmiddle"
, for: indexPath) as! CollectionViewCellmiddle
objCollectionViewCellMiddle.lblMiddle.text = bottom[indexPath.row] as? String
return objCollectionViewCellMiddle
}
}
Related
App Scheme
As you can see from the scheme I have UITableView and in UITableViewCells I have UICollectionView. The problem I face is that I can't get correct UITableViewCell number when I set text on labels in my UICollectionView
Here's how I'm trying to check which row of the UITableView currently there's:
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ExploreTableViewCell
cell.currentTableViewRow = indexPath.item
return cell
}
}
And then I set the label values according to the currentTableViewRow variable which I created:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! ExploreCollectionViewCell
switch(currentTableViewRow) {
case 1:
cell.collectionTitle.text = Const.newCollection.subCollections[indexPath.item]
case 2:
cell.collectionTitle.text = Const.freeCollection.subCollections[indexPath.item]
case 3:
cell.collectionTitle.text = Const.mostLikedCollection.subCollections[indexPath.item]
case 4:
cell.collectionTitle.text = Const.healthFitnessCollection.subCollections[indexPath.item]
case 5:
cell.collectionTitle.text = Const.earthCollection.subCollections[indexPath.item]
default:
cell.collectionTitle.text = Const.autumnEditionCollection.subCollections[indexPath.item]
}
return cell
}
Also, I reloadData() of my UITableView in my viewDidAppear()
I think the problem is that cells are reusable and that's why first 4 rows of my UITableView are correct and the other ones not. Any suggestions?
Add function inside UITableViewCell :
func reloadCollectionView() -> Void {
self.collectionView.reloadData()
}
And then use it like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ExploreTableViewCell
cell.currentTableViewRow = indexPath.item
cell.reloadCollectionView()
return cell
}
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
I have 2 arrays with different data
var array : [String] = ["getMeals", "getMeasure", "getWorkouts"]
var array2 : [String] = ["getStep", "getStep", "getStep"]
I am trying to retrieve the images from arrays, but in different collection cells.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.section == 0{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "WorkoutCollectionCell", for: indexPath) as! WorkoutCollectionCell
cell.collectionImage.image = UIImage(named: array[indexPath.row])
return cell
}else{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "WorkoutCollectionCell", for: indexPath) as! WorkoutCollectionCell
cell.collectionImage.image = UIImage(named: array2[indexPath.row])
return cell
}
}
I need to create another UICollectionViewCell or I can use the current cell?
you can use the same cell, with this code:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "WorkoutCollectionCell", for: indexPath) as! WorkoutCollectionCell
if indexPath.section == 0{
cell.collectionImage.image = UIImage(named: array[indexPath.row])
}else{
cell.collectionImage.image = UIImage(named: array2[indexPath.row])
}
return cell
}
You need 1 array like
let array = [["getMeals", "getMeasure", "getWorkouts"],["getStep", "getStep", "getStep"]]
func numberOfSections(in collectionView: UICollectionView) -> Int {
return array.count
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return array[section].count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "WorkoutCollectionCell", for: indexPath) as! WorkoutCollectionCell
cell.collectionImage.image = UIImage(named: array[indexPath.section][indexPath.row])
return cell
}
I want to display two array values in tableview using single cell.
Suppose i have two array and both contains same no of elements.
FirstArray and SecondArray. there is two label in tableview cell Lbl1 and Lbl2, now Lbl1 should fill with FirstArray and Lbl2 Should fill with SecondArray. I know that we can not use two array for uitableview datasource . I can not figure out how to do this.
Please help me.
I also tried using multiple custom tableview cells with section. but it did not give the desired result.
I have two Array -
let datalist1 = ["firstCell1" , "firstCell2" , "firstCell3" , "firstCell4"]
let datalist2 = ["secondCell1" ,"secondCell2" , "secondCell3" ,"secondCell4"]
In tableview numberOfRowsInSection :
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0
{
return datalist1.count
}
else {
return datalist2.count
}
}
In cellForRowAt :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as? FirstCell
cell?.initData(name: datalist1[indexPath.row])
return cell!
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath) as? SecondCell
cell?.initData(lbl: datalist2[indexPath.row])
return cell!
}
}
Actual Output :
FirstCell1
FirstCell2
FirstCell3
FirstCell4
SecondCell1
SecondCell2
SecondCell3
SecondCell4
Expected Output:
FirstCell1
SecondCell1
FirstCell2
SecondCell2
FirstCell3
SecondCell3
FirstCell4
SecondCell4
Hello You not need to add two section just do as bellow.
This is your arrays.
let datalist1 = ["firstCell1" , "firstCell2" , "firstCell3" , "firstCell4"]
let datalist2 = ["secondCell1" ,"secondCell2" , "secondCell3" ,"secondCell4"]
Number of rows
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return datalist1.coun
}
Cell for row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as? FirstCell
cell.Lbl1.text = datalist1[indexPath.row]
cell.Lbl2.text = datalist2[indexPath.row]
return cell!
}
Based on the explanation and code, you have provided, the requirement is not clear.
However, there may be two cases based on the above details:
Case-1:
Cell-1 : FirstCell1
Cell-2 : SecondCell1
Cell-3 : FirstCell2
Cell-4 : SecondCell2
Then you can implement something like below:
In tableview numberOfRowsInSection :
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (datalist1.count + datalist2.count)
}
In cellForRowAt :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row % 2 == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as? FirstCell
cell?.initData(name: datalist1[indexPath.row])
return cell!
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath) as? SecondCell
cell?.initData(lbl: datalist2[indexPath.row])
return cell!
}
}
Case-2:
Cell-1 : FirstCell1 SecondCell1
Cell-2 : FirstCell2 SecondCell2
Cell-3 : FirstCell3 SecondCell3
Cell-4 : FirstCell4 SecondCell4
Then you can implement something like below:
In tableview numberOfRowsInSection :
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return datalist1.count
}
In cellForRowAt :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as? FirstCell
//Single custom cell can implement both the labels
cell?.initData(name: datalist1[indexPath.row],lbl: datalist2[indexPath.row])
return cell!
}
}
You've to Declare as
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section %2 == 0 {
let cell1 = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as? FirstCell
cell1.lbl1.text = datalist1[indexPath.row]
return cell1!
}
else {
let cell2 = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath) as? SecondCell
cell2.lbl2.text = datalist2[indexPath.row]
return cell2!
}
}
The best way is using models. You have to declare data model such as
struct MyModel {
var firstValue: String?
var secondValue: String?
}
Then you have to convert your two arrays to a single array of MyModel objects
var myData = Array<MyModel>()
Then by using for loop you can iterate over one array and fill the myData array.
for (index, _) in datalist1 {
let object = MyModel()
object.firstValue = datalist1[index]
object.firstValue = datalist2[index]
myData.append(object)
}
Then just implement tableview protocol methods and fill your custom cell with MyModel objects.
1. Create a single array from datalist1 and datalist2 using zip(_:_:), that we'll be using as dataSource for tableView.
lazy var dataList = Array(zip(self.datalist1, self.datalist2))
dataList is of type [(String, String)].
Example:
If datalist1 and datalist2 are,
let datalist1 = ["firstCell1" , "firstCell2" , "firstCell3" , "firstCell4"]
let datalist2 = ["secondCell1" ,"secondCell2" , "secondCell3" ,"secondCell4"]
then, dataList contains
[("firstCell1", "secondCell1"), ("firstCell2", "secondCell2"), ("firstCell3", "secondCell3"), ("firstCell4", "secondCell4")]
2. You need a single cell to display all that data. There is no need to create 2 different UITableViewCells for this. Example:
class CustomCell: UITableViewCell {
#IBOutlet weak var lbl1: UILabel!
#IBOutlet weak var lbl2: UILabel!
}
3. Now, your UITableViewDataSource methods look like,
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomCell
cell.lbl1.text = self.dataList[indexPath.row].0
cell.lbl2.text = self.dataList[indexPath.row].1
return cell
}
This may be a little confusing, but i have a view controller that has a table view that contains a custom table cell and within this table cell, I have a collectionView with each cell holding a single image. I am trying to configure my collectionCell's image using the userToView variable seen below.
The delegate function below is where the table cell is being configured:
OtherUserAccountViewController.swift
class OtherUserAccountViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var userToView = User()
...
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:OtherUserPhotosTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: photosCellReuseIdentifier) as! OtherUserPhotosTableViewCell
cell.selectionStyle = .none
return cell
}
...
}
The delegate function below is where this image is being set:
OtherUserPhotosTableViewCell.swift
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "otherUserPhotosCollectionCell", for: indexPath) as! OtherUserPhotosCollectionViewCell
//Configure cell with photo from parent ViewController
return cell
}
My custom CollectionViewCell can be seen below:
OtherUserPhotosCollectionViewCell.swift
class OtherUserPhotosCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var imageView: UIImageView!
}
I have only really been getting accustomed to Swift & iOS development over the last couple weeks so any help is much appreciated. Thank you for your time!
Answered by assuming that you have UIImages already available to you
Assuming you have a single UICollectionViewCell
In OtherUserPhotosTableViewCell.swift
var photoFromTableView: UIImage?
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "otherUserPhotosCollectionCell", for: indexPath) as! OtherUserPhotosCollectionViewCell
//Configure cell with photo from parent ViewController
if let photo:UIImage = photoFromTableView {
cell.imageView.image = photo
}
return cell
}
In OtherUserAccountViewController.swift
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:OtherUserPhotosTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: photosCellReuseIdentifier) as! OtherUserPhotosTableViewCell
cell.selectionStyle = .none
cell.photoFromTableView = //Image you want to pass
return cell
}
Assuming you have multiple UICollectionViewCells
In OtherUserPhotosTableViewCell.swift
var photosArray: [UIImage]?
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "otherUserPhotosCollectionCell", for: indexPath) as! OtherUserPhotosCollectionViewCell
//Configure cell with photo from parent ViewController
if let photo_array:[UIImage] = photosArray {
cell.imageView.image = photo_array[IndexPath.row]
}
return cell
}
In OtherUserAccountViewController.swift
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:OtherUserPhotosTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: photosCellReuseIdentifier) as! OtherUserPhotosTableViewCell
cell.selectionStyle = .none
cell.photosArray = //Array of Images you want to pass
return cell
}