UITableview prototype cell not populating properly - ios

I am trying to populate a UITableView prototype cell with "Facebook status". It has images and videos depending on the response from Facebook server. But I am not able to populate it properly.
I will get the first image from the array. But when I scroll down UITableView and reach the top again, the image disappears. And when I print the value of i it exceeds the count of array soon after the tableview is loaded.
I am adding the edited code here.
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var rows : Int = 0
if section < numberOfRowsAtSection.count{
rows = numberOfRowsAtSection[section]
print(section,rows)
}
return rows
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("Section,Row",indexPath.section, indexPath.row)
switch(combinedArray[indexPath.section][indexPath.row]){
case "photo" :
let cell = tableView.dequeueReusableCell(withIdentifier: "ImageDetail", for:indexPath) as! imageCell
if(im < imageDetails.count){
let imageUrl = imageDetails[im]
let url = URL(string:imageUrl)
let data = try? Data(contentsOf: url!)
cell.storyDetailsLbl1.text = photoStoryDetails[im]
print(imageDetails[im])
cell.images1.image = UIImage(data: data!)
}
im = im + 1
return cell
case "video" :
let cell1 = tableView.dequeueReusableCell(withIdentifier: "VideoCell", for: indexPath) as! videoPlayerCell
cell1.videoView.isHidden = false
if vd < videoURLs.count{
print(videoURLs.count)
let urls = videoURLs[vd]
cell1.videoPlayerItem = AVPlayerItem.init(url: urls)
cell1.videoLabel.text = videoStoryDetails[vd]
}
vd = vd + 1
return cell1
case "link":
let cell2 = tableView.dequeueReusableCell(withIdentifier: "LinkCell", for: indexPath) as! linkCell
if ln < linkDetails.count {
cell2.linkLbl.text = linkDetails[ln]
}
ln = ln + 1
return cell2
default:
let cell3 = tableView.dequeueReusableCell(withIdentifier: "StatusCell", for: indexPath) as! statusCell
if st < statusDetails.count{
cell3.statusLbl.text = statusDetails[st]
}
st = st + 1
return cell3
}
}

Tableviews deque already created cells and reuse them to enhance performance. So a cell with the identifier "ImageDetail" could be reused and populated with data from any of your case statements. This will cause oddities like images disappearing when they shouldn't.
One way to solve this is to ensure you are handling each cell property in every case. cell.storyDetailsLbl1.text isn't handled in the photo case. cell.images1.image isn't handled in the link case, etc.
Better would be to define a cell with a unique identifier for each one of your cases. i.e. PhotoCell, LinkCell, VideoCell, etc.

Related

Add A UITableView Cell After Another One

Essentially I have a tableview with two types of cells. One is a cell that contains a number that is being pulled from an array. The second cell is just supposed to act as a spacing cell but can contain information. However, I've only seen solutions that have the second cell come after the first if it its row is equal to 0 or if it is sectioned off.
The array of numbers is [1,2,3,4,5,6,7] and the first cell type pulls from it to display each number
I'm trying to get something that looks like this:
celltypeone: 1
celltypetwo
celltypeone: 2
celltypetwo
celltypeone: 3
celltypetwo
celltypeone: 4
celltypetwo
... and so on.
I thought about trying to get the space cell to come after every next cell but I didn't think that made sense.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let index = indexPath.row
if index >= 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "productsblock", for: indexPath ) as! DetailTBCell
return cell
}
else {
let spacecell = tableView.dequeueReusableCell(withIdentifier: "spacingcell", for: indexPath ) as! DetailSpaceTBCell
return spacecell
}
}
This prints all the numbers from the array and displays them but the second cell does not show at all.
I'm unsure of what to do or how to get it to work.
if (index % 2) == 0 | try if index number is double put the first tableCell else put the other one
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let index = indexPath.row
if (index % 2) == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "productsblock", for: indexPath ) as! DetailTBCell
return cell
}
else {
let spacecell = tableView.dequeueReusableCell(withIdentifier: "spacingcell", for: indexPath ) as! DetailSpaceTBCell
return spacecell
}
If you notice your product cell needs to be shown at even position and the spacing cell at odd position.
`index >= 0 ` needs to replaced with `index % 2 == 0 `
Moreover in order to fetch data from product array you will be needing to divide the index by 2.
let products = [1,2,3,4,5,6,7]
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let index = indexPath.row
if index % 2 == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "productsblock", for: indexPath ) as! DetailTBCell
// Fetching data from array products defined above
let data = products[index / 2]
return cell
}
else {
let spacecell = tableView.dequeueReusableCell(withIdentifier: "spacingcell", for: indexPath ) as! DetailSpaceTBCell
return spacecell
}

unable to dequeue a cell with identifier in a two TableView View Controller Swift4

I'm using two TableViews in a ViewController but I get this error when it gets to the second TableViewCell, cartProductCell. They both have custom classes, and outlets, as it was the problem for many in other posts. Is the first time I'm doing this and I can't find a solution for this. May it be just because I'm using custom classes for the cells? In the tutorials I found about two TableViews weren't used custom classes.
In the Storyboard editor everything is connected well and identifiers are both correct.
Here's the function:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell:UITableViewCell?
// if tableView == self.worksTableView && CartViewController.bookedWoksArray.count > 0 {
if tableView == self.worksTableView {
let cell = tableView.dequeueReusableCell(withIdentifier: "cartWorkCell", for: indexPath as IndexPath) as! CartWorkTableViewCell
cell.workImageView.image = CartViewController.bookedWoksArray[indexPath.row].0
cell.workNameLabel.text = CartViewController.bookedProductsArray[indexPath.row].1
cell.workPriceLabel.text = CartViewController.bookedWoksArray[indexPath.row].2
} // else {return}
// else if tableView == self.worksTableView && CartViewController.bookedProductsArray.count > 0 {
if tableView == self.worksTableView {
let cell = tableView.dequeueReusableCell(withIdentifier: "cartProductCell", for: indexPath as IndexPath) as! CartProductTableViewCell
cell.cartProductImageView.image = CartViewController.bookedProductsArray[indexPath.row].0
cell.cartProductNameLabel.text = CartViewController.bookedProductsArray[indexPath.row].1
cell.cartProductPriceLabel.text = CartViewController.bookedProductsArray[indexPath.row].2
} //else {return}
return cell!
}
As usual many thanks
After a few tries and after fixing a silly mistake I finally made it work by assigning the value of custom cells to cell and the function's code is now:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell:UITableViewCell?
if tableView == self.worksTableView {
let workCell = tableView.dequeueReusableCell(withIdentifier: "cartWorkCell", for: indexPath) as! CartWorkTableViewCell
workCell.workImageView.image = CartViewController.bookedWoksArray[indexPath.row].0
workCell.workNameLabel.text = CartViewController.bookedWoksArray[indexPath.row].1
workCell.workPriceLabel.text = CartViewController.bookedWoksArray[indexPath.row].2
cell = workCell
}
if tableView == self.productsTableView{
let productCell = tableView.dequeueReusableCell(withIdentifier: "cartProductCell", for: indexPath) as! CartProductTableViewCell
productCell.cartProductImageView.image = CartViewController.bookedProductsArray[indexPath.row].0
productCell.cartProductNameLabel.text = CartViewController.bookedProductsArray[indexPath.row].1
productCell.cartProductPriceLabel.text = CartViewController.bookedProductsArray[indexPath.row].2
cell = productCell
}
return cell!
}

Two custom cells with search bar

I'm trying to have two custom cells and search bar in my UIView Controller. at first I was able to do one cell and search bar and it was perfect then I added another cell and then everything became ugly.
Here is the idea of my custom cells
I get data from my server in JSON. Many shops, some of them have a design and some have no design. If a shop has a design I save the design in a variable as a string withDesign
let shop_id = subJson["id"].stringValue
var info = Shops(shopname: shopName, Logo: logoString, family_id: familiy_id , design : designImage )
self.withDesign = self.shops.filter { $0.design != "" }
self.withOut = self.shops.filter { $0.design == "" }
self.allShops = self.NofilteredArr + self.filteredArr
print(self.filteredArr)
The first cell is called design and second is called Cell
I tried multiple ways but I failed because it's my first time to deal with two cells.
What I'm tryting to do is all shops will be listed in Cell and if there is a shop that has a design then make cells with the design if no design then just list the shops. Two separate customs cells
What I'm trying to do
example of design
example of cell
Any help will be appreciated!
please Use code like this
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if searchActive
{
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ShopTableViewCell
let entry1 = auxiliar?[indexPath.row]
cell.shopImage.hnk_setImage(from: URL(string: (entry1?.Logo)!))
cell.shopName.text = entry1?.shopname
return cell
}else{
if withDesign != nil
{
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ShopTableViewCell
let entry = withDesign[indexPath.row]
cell.shopImage.hnk_setImage(from: URL(string: entry.Logo))
cell.shopName.text = entry.shopname
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "design", for: indexPath) as! DesignTableViewCell
let entry = withDesign[indexPath.row]
cell.deignImage.hnk_setImage(from: URL(string: entry.design))
return cell
}
}

How to use UITableViews to create static and dynamic rows with Swift

This question follows up from this: Use UICollectionViews to create dynamic and multiple features.
I am able to create a static cell which displays the name and image of the recipe similar like this app:
Where I am stuck is creating a dynamic row which changes based on the amount of data inside i.e. utensils or nutritional values like the image below:
I know how to display rows of data on tableView normally. But not sure how to embed it into a section inside a tableView. I attempted to add multiple prototype cells and assign them to a subclass of UITableViewCell's. Then I try to use if statements in my cellForRow but this isn't soling my issue.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FirstCell
//set the data here
cell.recipeTitle.text = recipe.name
cell.imageView.image = UIImage(named: "url")
return cell
}
else if indexPath.row == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath) as! SecondCell
//set the data here
return cell
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell3", for: indexPath) as! ThirdCell
//set the data here
return cell
}
}
I have also looked at this demonstration: https://medium.com/ios-os-x-development/ios-how-to-build-a-table-view-with-multiple-cell-types-2df91a206429, which is near to what I want to achieve but I have found it quite difficult to adapt to it.
If someone could direct me on how best to approach this or a good example then I would really appreciate it.
First you can't have static cells and dynamic cells in the same tableView. So how do you work around that? Define each of the static cells in the sections they belong in as well as the dynamic cells in the sections they belong to. That, however doesn't look like what you are trying to do. You just want multiple sections in the same tableView, each section with a different list of data
To do this you will need the number of sections so use the tableView(_:numberOfSections:) function.
override func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
You can then(and probably should) give each of those sections a title by initializing an array with the titles in your tableViewController(assuming thats what you are using. It could also just be a tableView).
let headerTitles = ["Nutritional Values", "Utensils", "Ingredients"]
Then use the tableView(_:titleForHeaderInSection:)
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section < headerTitles.count {
return headerTitles[section]
}
return nil
}
Now you can start defining your rows by the sections.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell
if indexPath.section == 0 {
//Setup up the first row
if indexPath.row == 0 {
//I'm not sure where or how you defined First/SecondCell but this may not work depending on those two questions.
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FirstCell
return cell
} else if indexPath.row == 1 {
let cell = Bundle.main.loadNibNamed("StaticCell", owner: self, options: nil)?.first as! StaticCell
return cell
}
} else if indexPath.section == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! SecondCell
//setup cell1 n your storyboard to have a label with a tag # of 12(or whatever number you want to use)
//you also want an array with your utensil data accessible here
let label = cell.viewWithTag(12) as! UILabel
label.text = utensilNames[indexPath.row]
return cell
} else if indexPath.section == 2 {
let cellIngredients = tableView.dequeueReusableCell(withIdentifier: "Ingredients", for: indexPath)
tableView.deselectRow(at: indexPath, animated: true)
return cellIngreidents
}
cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
return cell
}
The point here is to use sections then rows to distribute your data.
Just to clarify Section 0 Row 0 - N would be where you're static rows are setup. I found it best to use XIB files subclassing TableViewCell.
Hope this helps.
EDIT So the way I'm looking at the "static" cells is in the first section the xib is the only put exactly where you tell it to be placed. In the example above the first section in the second cell is the

How to get table all current row information

I am new to swift . i am doing my project programatically and I load data from api to the tableView and tableView like ios setting page ..
now i need all rows information when click "Add to cart" button. How can i do it?
here is my code sample :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.section {
case 0:
let cell = tableView.dequeueReusableCell(withIdentifier: cartHeaderCell, for: indexPath) as! CartHeaderCell
cell.configureCell(indexPath.item)
return cell
case 1:
let obj = data?[indexPath.row]
var cell = UITableViewCell()
switch obj {
case is Addon:
cell = tableView.dequeueReusableCell(withIdentifier: addonCell, for: indexPath) as! AddonCell
let switchView = UISwitch(frame: .zero)
switchView.setOn(false, animated: true)
cell.accessoryView = switchView
guard let addon = obj as? Addon else {
return cell
}
cell.textLabel?.text = "\(addon.name) + €\(addon.price)"
case is AddonGroup:
cell = tableView.dequeueReusableCell(withIdentifier: addonGroupCell, for: indexPath) as! AddonGroupCell
cell.accessoryType = UITableViewCellAccessoryType.disclosureIndicator
guard let addonGroup = obj as? AddonGroup else {
return cell
}
if let addons = addonGroup.addonList {
cell.detailTextLabel?.text = ""
var selectedAddons = ""
for _addon in addons
{
if _addon.isSelect == true {
selectedAddons = selectedAddons + "\(_addon.name)"
}
}
cell.detailTextLabel?.text = selectedAddons
}
cell.textLabel?.text = addonGroup.name
...........................................
As Fahim was mentioning, you need to set up a data model that records that status of each cell before / during / after the user interaction with each cell. So when the cell goes off screen and then comes back on, it will be presented with the correct state of the model.
Secondly, for the UISwitchViews, you should be instantiating and adding those to the contentView within each cell in order to keep the cellForRow function clean and problem free. The reason leads me into my next point: how to record the status of each UISwitchView after the user has interacted with a UISwitchView. You are going to want to create a protocol and add a delegate within the UICollectionViewCell(that inherits class and the delegate should be a weak var), in order to update the model whenever the UISwitch is tapped.
If you have any more questions i can do my best to help!

Resources