I am currently having a problem with displaying two different types of custom cells on the same uitableview.
What I have managed so far, is receiving the "updates" to the update cell, known as cell. I just cannot figure out how to also get numberOfRowsInSection to return two values, so both of my cells will show.
Let me explain through my code:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return updates.count
return updatesTask.count // I CANNOT DO THIS - what can I do instead?
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:updateTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! updateTableViewCell
let cellTask:tasksTableViewCell = tableView.dequeueReusableCellWithIdentifier("TaskCell", forIndexPath: indexPath) as! tasksTableViewCell
let update = updates[indexPath.row]
let updateTask = updatesTask[indexPath.row]
// Example of the two different cells that need different data from firebase
cell.nameLabel.text = update.addedByUser
cellTask.nameLabel.text = updateTask.addedByUser
As you can probably see, the let updateTask is trying to get an indexPath.row but that is not possible, since I cannot have two return values in the numberOfRowsInSection, which is a problem because that number is referring to the place where the data is stored in my firebase database.. How can I modify this to make it work?
Hope you guys understand where I am going with this, otherwise let me know and I will try to explain better :-)
#Callam's answer is great if you want to put them in two sections.
This is the solution if you want all to be in one section.
First, in numberOfRowsInSection method you need to return the sum of those two array counts like this: return (updates.count + updatesTask.count)
Then you need to configure cellForRowAtIndexPath method like this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row < updates.count{
// Updates
let cell:updateTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! updateTableViewCell
let update = updates[indexPath.row]
cell.nameLabel.text = update.addedByUser
return cell
} else {
// UpdatesTask
let cellTask:tasksTableViewCell = tableView.dequeueReusableCellWithIdentifier("TaskCell", forIndexPath: indexPath) as! tasksTableViewCell
let updateTask = updatesTask[indexPath.row-updates.count]
cellTask.nameLabel.text = updateTask.addedByUser
return cellTask
}
}
This will display all cells followed by all cellTasks.
If updates array and updatesTask array have equal number of items and you want to display them one by one you can use this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row % 2 == 0 {
// Updates
let cell:updateTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! updateTableViewCell
let update = updates[indexPath.row/2]
cell.nameLabel.text = update.addedByUser
return cell
} else {
// UpdatesTask
let cellTask:tasksTableViewCell = tableView.dequeueReusableCellWithIdentifier("TaskCell", forIndexPath: indexPath) as! tasksTableViewCell
let updateTask = updatesTask[indexPath.row/2]
cellTask.nameLabel.text = updateTask.addedByUser
return cellTask
}
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0:
return updates.count
case 1:
return updatesTask.count
default:
return 0
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch indexPath.section {
case 0:
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! updateTableViewCell
let update = updates[indexPath.row]
cell.nameLabel.text = update.addedByUser
return cell
case 1:
let cell = tableView.dequeueReusableCellWithIdentifier("TaskCell", forIndexPath: indexPath) as! tasksTableViewCell
let updateTask = updatesTask[indexPath.row]
cell.nameLabel.text = updateTask.addedByUser
return cell
default:
return UITableViewCell()
}
}
For each row you have to choose if you want to display one type of cell or the other but not both. You should have a flag in numberOfRowsInSection telling your method that you want to load Cell or CellTask and then return the correct number of rows.
You should return total number of rows in your numberOfRowsInSection method. so you can return summation of your both array's count something like,
return updates.count + updatesTask.count
now in your cellForRowAtIndexPath method you can differentiate your cell something like,
let cell:updateTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! updateTableViewCell
let cellTask:tasksTableViewCell = tableView.dequeueReusableCellWithIdentifier("TaskCell", forIndexPath: indexPath) as! tasksTableViewCell
if indexPath.row % 2 == 1 {
//your second cell - configure and return
return cellTask
}
else
{
//your first cell - configured and return
return cell
}
I am not sure what you want to achieve. If you want to display the number of cells updates[] and updatesTask[] have elements you can do it like this
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (updates.count + updatesTask.count)
}
then you can modify your cellForRowAtIndexPath method like this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:updateTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! updateTableViewCell
let cellTask:tasksTableViewCell = tableView.dequeueReusableCellWithIdentifier("TaskCell", forIndexPath: indexPath) as! tasksTableViewCell
if indexPath.row < updates.count{
//update
let update = updates[indexPath.row]
cell.nameLabel.text = update.addedByUser
}else{
let updateTask = updatesTask[indexPath.row]
cellTask.nameLabel.text = updateTask.addedByUser
}
return cell
}
with the if condition you can choose from which array you are taking data.
But be careful to name an array exactly the same as another constant like you did here
let updateTask = updatesTask[indexPath.row]
You can create a simple View Model, that will hold the multiple item types:
enum ViewModelItemType {
case nameAndPicture
case about
case email
case friend
case attribute
}
protocol ViewModelItem {
var type: ViewModelItemType { get }
var rowCount: Int { get }
var sectionTitle: String { get }
}
Then create a model item type for each section. For example:
class ViewModelNameAndPictureItem: ViewModelItem {
var type: ProfileViewModelItemType {
return .nameAndPicture
}
var sectionTitle: String {
return “Main Info”
}
var rowCount: Int {
return 1
}
var pictureUrl: String
var userName: String
init(pictureUrl: String, userName: String) {
self.pictureUrl = pictureUrl
self.userName = userName
}
}
Once you configure all your section items with, you can save them in ViewModel:
class ProfileViewModel {
var items = [ViewModelItem]()
}
And add to you TableViewController:
let viewModel = ViewModel()
In this case, NumberOfSections, NumberOfRows and CellForRowAt methods will be clean and simple:
override func numberOfSections(in tableView: UITableView) -> Int {
return viewModel.items.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.items[section].rowCount
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = viewModel.items[indexPath.section]
switch item.type {
// configure celll for each type
}
}
Configuring the section title will also be very neat:
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return viewModel.items[section].sectionTitle
}
Please check my recent tutorial on this topic, that will answer your question with the details and examples:
https://medium.com/ios-os-x-development/ios-how-to-build-a-table-view-with-multiple-cell-types-2df91a206429
Related
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
}
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
}
}
I am trying to create a custom TableView that will output custom cells depending on the keys contained in a Dictionary. I have created classes and outlets for each custom cell, but when I build and run; the same custom cell is displayed multiple times. I have the correct number of cells being displayed (i.e. the same as number of keys present in the dictionary) but I can't seem to differentiate between outputted cells.
Here is my code:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.dataDict.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if (dataDict.indexForKey("Number") != nil) {
let firstcell:MyFirstCell = self.scanConfirmTable.dequeueReusableCellWithIdentifier("scanfirst", forIndexPath: indexPath) as! MyFirstCell
return firstcell
}
else if (dataDict.indexForKey("Social") != nil) {
let secondcell:MySecondCell = self.scanConfirmTable.dequeueReusableCellWithIdentifier("scansecond", forIndexPath: indexPath) as! MySecondCell
return secondcell
}
else {
let emptycell:ScanEmptyCell = self.scanConfirmTable.dequeueReusableCellWithIdentifier("scanemptycell", forIndexPath: indexPath) as! ScanEmptyCell
return emptycell
}
I have searched previous posts on here and found an option to use something like:
let currentTag = dataDict[indexPath.row]
But I am getting an error:
Cannot subscript a value of type '[String:String]' with an index type 'Int'.
Any help would be hugely appreciated!
Instead of using Dictionary try to use Array that contains your all keys with sorted and use that array with tableViewDataSource methods.
var keysArray = [String]()
keysArray = Array(dataDict.keys).sorted(<)
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.keysArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if (keysArray[indexPath.row] == "Number") {
let firstcell:MyFirstCell = self.scanConfirmTable.dequeueReusableCellWithIdentifier("scanfirst", forIndexPath: indexPath) as! MyFirstCell
return firstcell
}
else if (keysArray[indexPath.row] == "Social") {
let secondcell:MySecondCell = self.scanConfirmTable.dequeueReusableCellWithIdentifier("scansecond", forIndexPath: indexPath) as! MySecondCell
return secondcell
}
else {
let emptycell:ScanEmptyCell = self.scanConfirmTable.dequeueReusableCellWithIdentifier("scanemptycell", forIndexPath: indexPath) as! ScanEmptyCell
return emptycell
}
In my app I created two custom tableview cells.
Problem I am facing now the second tableview cell update with last element of the array only.
In cellForRowAtIndexpath array elements are displaying fine.
Consider [ "Value1", "Value2" ] is my array. In tableView only value2 is displaying in two cells.
var title = ["value1","value2"]
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let row = indexPath.row
let x = Id[indexPath.row]
if x == 0{
let cell1 = tableView.dequeueReusableCellWithIdentifier("Cell1", forIndexPath: indexPath) as! MyCell1
return cell1
}
else{
let cell2 = tableView.dequeueReusableCellWithIdentifier("Cell2", forIndexPath: indexPath) as! MyCell2
for index in 0..<myArray.count{
cell2.titleButton.setTitle(title[index],forState:UIControlState.Normal)
}
return cell2
}
}
I am stuck here, your help will be appreciated.
Following is the solution, reason it was going out of range was because value incremented when cell were dequed as cellforrowAtIndexPath was called every time we scrolled down(since some cells were not visible and these cells were dequed when we scrolled down):-
var name = ["HouseBolo","HouseBolo1","HouseBolo2","HouseBolo3"]
var propertyVal:Int = 0
var projectVal:Int = 0
var type = ["Apartment","Villa","Home","Flat","Plot"]
var arrangedData = [String]()
var flatId = [0,1,2,0,0]
override func viewDidLoad() {
super.viewDidLoad()
// I want the Expected Output in Tableview Cell is
// 1. Apartment 2. HouseBolo 3. HouseBolo1 4. Villa 5. Home
for item in flatId {
if item == 0 {
arrangedData.append(type[propertyVal])
propertyVal+=1
}
else {
arrangedData.append(name[projectVal])
projectVal+=1
}
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrangedData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let flatDetails = flatId[indexPath.row]
// For Property Cell
if flatDetails == 0{
let cell = tableView.dequeueReusableCellWithIdentifier("Cell1", forIndexPath: indexPath) as? PropertyCell
if(cell != nil) {
cell!.pType.text = arrangedData[indexPath.row]
}
return cell!
}
// For Project Cell
else {
let cellan = tableView.dequeueReusableCellWithIdentifier("Cell2", forIndexPath: indexPath) as? ProjectCell
if(cellan != nil) {
cellan!.projectName.setTitle(arrangedData[indexPath.row], forState: UIControlState.Normal)
}
return cellan!
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
You need to use :
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! searchCell
in :
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {}
block.
searchCell : is a class of type : UITableViewCell
After that, go in Storyboard and change the identifier of your cell with : "cell"
In the code...
let cell2 = tableView.dequeueReusableCellWithIdentifier("Cell2", forIndexPath: indexPath) as! MyCell2
for index in 0..<myArray.count{
cell2.titleButton.setTitle(title[index],forState:UIControlState.Normal)
}
return cell2
you are iterating over 'myArray' and assigning the value to 'cell2.titleButton'. Cell 2 will always have the last value assigned to it's title. It's assigning it to 'value1', then reassigning it to 'value2'. Looping through the array seems to be the issue (assuming the cells are displaying - just always showing the title from the last item in the array.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
}
You have to add your custom tableview cell class name in the place of UITableViewCell
Something like this -
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> **custom tableview cell class name**
{
}
And also in Storyboard ,change the identifier of your cell with : "cell1" and "cell2"
I have a PFQueryTableViewController, which downloads my images from parse.
I had it working all correctly, displaying the correct image in all the cells, but today I have added a header cell too and now I have the headerCell displaying the correct information and also changing(which is exactly how I want it to be) but now the cell that displays the imageView shows the latest image uploaded for all of the cells & not different image as it should do...?!
Here is the code which controlls my tableview..But I cant understand what is wrong!
object, is var object: PFObject?
override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 46
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return objects!.count
}
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerCell = tableView.dequeueReusableCellWithIdentifier("PostHeader") as! PostHeaderTableViewCell
let object = objects![section]
let username = object.objectForKey("user")?.objectForKey("username") as? String
headerCell.usernameLabel.text = username!.capitalizedString
let dataFormatter: NSDate = NSDate(timeIntervalSinceNow: -4)
NSLog("Time Ago: %#", dataFormatter.shortTimeAgoSinceNow())
headerCell.timeLabel.text = dataFormatter.shortTimeAgoSinceDate((object.createdAt)!)
if (object.objectForKey("user")!.objectForKey("profilePicture") != nil)
{
let userImageFile:PFFile = object.objectForKey("user")!.objectForKey("profilePicture") as! PFFile
userImageFile.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
headerCell.postProfilePicture.image = UIImage(data: imageData!)
headerCell.postProfilePicture.layer.cornerRadius = 0.1 * headerCell.postProfilePicture.bounds.size.width
headerCell.postProfilePicture.clipsToBounds = true
})
}
return headerCell
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell?
{
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! BaseTableViewCell
cell.titleLabel?.text = object?.objectForKey("title") as? String
let imageFile = object?.objectForKey("imageFile") as? PFFile
cell.cellImageView?.image = UIImage(named: "circle")
cell.cellImageView?.file = imageFile
cell.cellImageView.loadInBackground()
return cell
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
if indexPath.row + 1 > self.objects?.count
{
return 44
}
let height = super.tableView(tableView, heightForRowAtIndexPath: indexPath)
return height
}
If anyone can help, That would be amazing!!
I think your issue has to do with the override cellForRowAtIndexPath method that you're using. Because the generic PFQueryTableViewController is intended for only one section, when you use that method with the signature (tableView, indexPath, object) instead of the default one with signature (tableView, indexPath) the method will always pull the # of images that you returned in numberOfRowsInSection. What I suggest you do instead is use the regular cellForRowAtIndexPath(tableView: UITableView, indexPath: NSIndexPath) -> UITableViewCell! and then get the object based on the indexPath.
It would look like this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell!
{
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! BaseTableViewCell
if let object = objects![indexPath.row] as? PFObject {
cell.titleLabel?.text = object?.objectForKey("title") as? String
let imageFile = object.objectForKey("imageFile") as? PFFile
cell.cellImageView?.image = UIImage(named: "circle")
cell.cellImageView?.file = imageFile
cell.cellImageView.loadInBackground()
}
return cell
}
This will then get you the image for the appropriate index path that you're looking for, rather than always using the first one.
You can also try printing the indexPath.row in that method when it's called so you can see if the indexPath is changing.
EIDT: Actually it seems like even that may not work. You can try to follow the tutorial here, but they explicitly mention that a PFQueryTableViewControlleris not meant to work with multiple sections. You're probably better off subclassing a regular UITableViewController