Swift cellforrow load asynchronously - uitableview

When i load data array into uitableview cellforrow, how can i make it show only in top most visible cell first? Each custom cell is just over half size screen and when user scrolls, it changes the top most visible to next one. The code is what i am currently using
let cell = tableView.dequeueReusableCell(withIdentifier: cellId,
for: indexPath) as! TaskCell
let task =
self.data[indexpath.row]
cell.taskTitle.text = task.title
cell.taskNote.text = task.note
return cell

Related

How to implement a tableview cell that contains 2 more cells inside counts are not same

I am trying to achieve a layout like this
My tableview has 3 cells (SubItemsCell (main cell), SubItemsListCell(n number of products inside main cell) and noSubstituteCell (fixed cell after the second cell count 1))
SubItemsCell has a "SELECT" Button that will expand and show SubItemsListCell, this cell will load as (dynamic) any count of products under the main SubItems Cell.
NoSubstitute Cell comes after the n number of products loads.
What my expected result is first load main cell that is self.archivedProducts and when u click each archived product's select button it expand and load self.newproducts and nosubstitute static cell. So main cell paired up with those 2 cells and show at once when we expand only.
So far I just loaded Main Cell only. I have no idea what should I do next, please give me an idea with a code or very similar example.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "SubItemsCell", for: indexPath) as? SubItemsCell else {
fatalError ("SubstituteItems Cell not found!")
}
let product = self.archivedProducts[indexPath.row]
cell.titleLabel.text = product.title ?? ""
cell.priceLabel.text = "AED \(String(format: "%.2f", product.price ?? 0.00))"
cell.scaleLabel.text = product.uom ?? ""
cell.unavailableLabel.isHidden = false
cell.selectBtn.isHidden = false
let imageUrl = product.image?.url
let escapedUrl = imageUrl?.replacingOccurrences(of: "\\", with: "")
let replacedUrl = "\(escapedUrl ?? "")"
let url = URL(string: replacedUrl)
let plImage = UIImage(named: "whiteBg")
DispatchQueue.main.async {
cell.thumbImage.kf.setImage(with: url, placeholder: plImage, options: [.transition(.fade(0.2))])
}
}
I don't think you need a nested tableview. You can achieve the above with other elements. In order to achieve the layout above, my suggestion is the following:
Subclass your UITableViewCell into at least 2 different formats. There's a lot of ways to do this, my suggestion is to use a CocoaTouch Class w/ XIB (tutorial here) so you can be meticulous with the layout and constraints:
a. CellWDropdown (Cell #1 shown): You have a cell prototype that will include the image, the 3 labels in question and a button which act as the dropdown and will invoke a UIPickerView.
b. CellWStepper (Cells #2 and #3) : You'll have your image and 2 labels and what's called UIStepper for the +/- buttons
c. LastCell (Cell #4 if that's a cell? It could also be a view): likewise create the needed UI elements and constrain them appropriately.
Register the nib file you created:
tableView.register(UINib(nibName: "CellWDropDown", bundle: nil), forCellReuseIdentifier: "cellWDropdownIdentifier")
In cellForRowAt method, you invoke the XIBs created above to make the cell views based on the index OR desired cell type for example:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: cellWDropdownIdentifier, for: indexPath) as! CellWDropdown
}
else if(indexPath.row == 1 || indexPath.row == 2){
let cell = tableView.dequeueReusableCell(withIdentifier: cellWStepperIdentifier, for: indexPath) as! CellWStepper
}
//do other actions/styling
}
Hook up delegate relationships/methods between the nibs you created and the UITableViewController. This is going to application specific.
There's a lot of detail that will have to fill in the gaps but this should put a major dent in getting started

UICollectionView doesn't create a new cell if the cell is shown using (indexPath.item % n == 0). It doesn't create new cell for multiples of a number

I'm using this condition to create specific UICollectionViewCells.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if(indexPath.item % 3 == 0){//multiples of 3
let cell = myCollectionView.dequeueReusableCell(withReuseIdentifier: "Cell1id", for: indexPath) as! Cell1
cell.backgroundColor = .white
if(cell.wasCreated){
cell.cellLabel.text = "Cell(1) \(indexPath.item) was created before."
}
else{
cell.cellLabel.text = "Creating cell(1) \(indexPath.item) first time."
cell.wasCreated = true
}
return cell
}
else{
let cell = myCollectionView.dequeueReusableCell(withReuseIdentifier: "Cell2id", for: indexPath) as! Cell2
cell.backgroundColor = .gray
if(cell.wasCreated){
cell.cellLabel.text = "Cell(2) \(indexPath.item) was created before."
}
else{
cell.cellLabel.text = "Creating cell(2) \(indexPath.item) first time."
cell.wasCreated = true
}
return cell
}
}//end cellForItemAt
Here 'wasCreated' is a variable within the cell that I'm using to check if the cell is being created for the first time, if it is I set wasCreated = true and this should be done, for the first time, for every cell but it isn't.
The condition is: if the indexPath.item is a multiple of 3, deque cell 1 otherwise cell 2.
Now normally, when a cell is to be displayed for the first time the cell's init() method is called but in this case it's not called and the older cell is being dequed for some reason.
I have no idea why this is happening.
I have uploaded a sample project that reproduces the problem. Here's the link: https://github.com/AfnanAhmadiOSDev/IndexMultiplesTest.git
The behaviour you describe is expected. To reduce memory use, cells are reused as the collection view scrolls.
When you call dequeueReusableCell UIKit checks to see if there is a cell with the requested identifier that has moved offscreen and is therefore eligible for reuse. If there is, then this cell is returned. In this case init will not be called. If there is no candidate cell then a new cell instance is returned and init will be called.
When you run your code you will see that cells are being created at first, but after you scroll up and down sufficiently to build up a large enough cell reuse pool cells are re-used and no new cells are created.
Cell reuse is independent of the IndexPath for which the cell was previously used.

Swift iOS -How to change TableView Cell Properties for All Visible Cells Already Loaded On Screen Without Reloading Data?

I'm using a splitViewController and on the master side I have a tableView and on the detail side I have whatever info will be shown from the selected cell.
I send a notification from the detail side to the master side to change the color of the textLabels inside the cells after they have already been loaded on the screen (I don't want to reload).
cells are first loaded with black textlabel's
while the cells are still on screen, a notification gets sent, I want change the textLabels to lightGray
while the cells are still on screen, a different notification gets sent and I want to change the textLabels back to black
Everything works fine but the problem is the way I'm doing it now I can only change each individual visible cell separately but I want to change them all at once. If had 10 cells there would be a lot of code so I know there has to be a more efficient way.
#objc fileprivate func changeTextLabelColorToLightGray(){
let indexPathZero = NSIndexPath(row: 0, section: 0)
let cellZero = tableView.cellForRow(at: indexPathZero as IndexPath) as! MyCustomCell
cellZero.textLabel.text = UIColor.lightGray
let indexPathOne = NSIndexPath(row: 1, section: 0)
let cellOne = tableView.cellForRow(at: indexPathZero as IndexPath) as! MyCustomCell
cellOne.textLabel.text = UIColor.lightGray
}
#objc fileprivate func changeTextLabelColorBackToBlack(){
let indexPathZero = NSIndexPath(row: 0, section: 0)
let cellZero = tableView.cellForRow(at: indexPathZero as IndexPath) as! MyCustomCell
cellZero.textLabel.text = UIColor.black
let indexPathOne = NSIndexPath(row: 1, section: 0)
let cellOne = tableView.cellForRow(at: indexPathZero as IndexPath) as! MyCustomCell
cellOne.textLabel.text = UIColor.black
}
How can I do the above and access all the visible cells and change their properties at once instead individually?
You have access to the property visibleCells for UITableView and UICollectionView. Here's an example of what you can do:
tableView?.visibleCells.forEach { cell in
if let cell = cell as? YourCell {
cell.changeTextLabelColorBackToBlack()
}
}

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 display two cells each with different functionality in the same tableView simultaneously?

I have tableView.
I have tableViewCellOne. tableViewCellOne allows text input and image input by the user which gets saved in a database. This part works fine.
I want to build tableViewCellTwo. This cellTwo has a different function than cellOne. cellTwo will retrieve this data and display it.
I want both cells to be visible at the same time. tableViewCellOne above, and tableViewCellTwo below, on the same tableView. Here is the relevant portion of the code I wrote. The way that it works as of now, it only displays tableViewCellOne which is not my intent. This code is written in my TableViewController.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
if (indexPath.row == 0) {
let cellIdentifier = "MyTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! MyTableViewCell
// Fetches the appropriate data for the data source layout.
let data = myData[indexPath.row]
cell.MyData1.text = data.1
cell.MyData2.image = data.2
cell.MyData3.text = data.3
return cell
} else {
let cellIdentifier = "TheirDataTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! TheirDataTableViewCell
// Fetches the appropriate data for the data source layout.
let data = theirData[indexPath.row]
cell.TheirData1.text = data.1
cell.TheirData2.image = data.2
cell.TheirData3.text = data.3
return cell
}
}
With this if else statement, I am trying to say: when we are loading cell 1 then load the following data, when we are loading cell2 then load the following other data. But I think instead its interpreting it as: if (something) load cell1, if (something else) load cell2. I could be way off base with that diagnosis though.
How do I display two cells each with different functionality in the same tableView simultaneously?

Resources