UITableView willDisplay cell is drawing all cells before I scroll down - ios

I'm trying to implement infinite scrolling in UITableView by checking the indexpath at "willDisplay cell", however once the table is loaded with data, the "willDisplay cell" func seems to be drawing all cells before I scroll down although the mobile screen can only fit one or two cells at a time.
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
print(indexPath.row)
}
Output without scrolling down:
0
1
2
3
4
5
When I scroll down the "willDisplay cell" func works properly and it prints the indexpath.row one at a time.
What could be the problem here?
Here's a look at the full code:
override func viewDidLoad() {
super.viewDidLoad()
postsTableview.delegate = self
postsTableview.dataSource = self
let nibName = UINib(nibName: "postTableViewCell", bundle:nil)
postsTableview.register(nibName, forCellReuseIdentifier: "postTableViewCell")
loadPosts()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "postTableViewCell", for: indexPath) as! postTableViewCell
return cell
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
print(indexPath.row)
}
I have tried setting the tableview cell height fixed with large value that exceeds the screen height but I still have the same problem
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat{
return 1000.0;
}

It turned out that I needed to set an estimated row height. I'm coming from Android background and this sounds silly to me but it worked out.
postsTableview.estimatedRowHeight = 400
postsTableview.rowHeight = UITableViewAutomaticDimension

On your edited question:
There's nothing wrong with iOS, all table/collection view cells are drawn before they are shown; imagine a table view that shows nothing in the beginning when it's shown, would that be weird?
I don't know what you are trying to achieve after the cells are shown, but I believe that's suitable for another question, good luck!

Related

Cannot Set Dynamic TableView Row Height

I'm trying to set dynamic row height in TableView. But unfortunately is not working.
The only thing is working is to set the heightForRowAt indexPath to a constant number.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 470 //or any other number
}
If I delete this function, or try to set it to automatic the cell will not show at all. Something like this:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
I also tried to use:
self.tableView.estimatedRowHeight = UITableView.automaticDimension //Tried even with a number
self.tableView.rowHeight = UITableView.automaticDimension
But seems nothing to work besides using a constant height in heightForRowAt indexPath function. Which is not what I want, I want cell to be expanded based on content inside the cell.
I also tried to set height in heightForRowAt indexPath function to automatic but set a fixed height on constraints in the content of the cell xib file, still same (I did just to see what happens). So seems, something is wrong around this function.
Note:
My TableView and UITableViewCell are divided in a storyboard and a xib files.
If you want to set the dynamic tableview row height, you must use autolayout.
Please set the constraint on the UI components in the cell as shown in the image below.
I used UILabel as sample code. For UILabel, be sure to set Lines to 0.
Here is the sample code
class ViewController: UIViewController {
let stringArr = ["aaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"]
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableView.automaticDimension
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return stringArr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "TestCell", for: indexPath) as? TestCell else {
return UITableViewCell()
}
cell.label.text = stringArr[indexPath.row]
return cell
}
}
You need to register the cell in the tableview.
tableView.register(UINib(nibName: “YourCellNibName”, bundle: nil), forCellReuseIdentifier: “YourCellReuseIdentifier”)

Vertical UIStackView: how to keep same space between elements and center them?

I have a table view with each cell containing a vertical UIStackView,
and I would like to center all elements inside this stack view, with a spacing of 8px between each of them. Here is visually what I would like to achieve:
But here is what I am getting so far:
And the code I tried:
In UITableView's delegate
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TVCell", for: indexPath) as! TVCell
(0...indexPath.row).forEach { i in
let label = UILabel()
label.text = "Label #\(i)"
cell.stackView.addArrangedSubview(label)
}
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
5
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
180
}
TVCell
class TVCell: UITableViewCell {
#IBOutlet weak var stackView: UIStackView!
}
What should I do to get the expected result?
Thank you for your help
Since you are using a fixed height for your rows, you want to vertically center the stack view in the cell:
Stack view properties:
and, give the stack view an Intrinsic Height Placeholder to avoid complaints from Interface Builder:
Note that you'll want to change your cellForRowAt code... Cells are reused, and as it stands now you'll be adding more and more labels to each cell as you scroll up and down.
One method (this would be bad practice, but it works for understanding your layout issue):
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TVCell", for: indexPath) as! TVCell
// remove existing labels
cell.stackView.arrangedSubviews.forEach {
$0.removeFromSuperview()
}
(0...indexPath.row).forEach { i in
let label = UILabel()
label.text = "Label #\(i)"
cell.stackView.addArrangedSubview(label)
}
return cell
}
and here's how it looks:

Action when tableview row selection has changed

I have a tableview implemented. The list shows fine, but I want to detect when the row selection has been changed. I am trying to do a certain action when the row selection is changed, for example, print("row selection has changed ")
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "exploreCell", for: indexPath)
cell.textLabel?.text = tableArray[indexPath.row]
return cell
}
I hope the question is clear.
You can observer selection state from this 2 delegate methods. So when you select any cell it will call didSelectRowAt delegate method and if you tap on selected cell again it will call didDeselectRowAt
Make sure to set tableView selection property to multiple selection incase if you want user to select multiple cell
tableView.allowsMultipleSelection = true
Swift Delegate methods.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//Your code here
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
//Your code here
}

Reload tableview with UITableViewAutomaticDimension , Bounce

Today I have gone through with the very varied behaviour of the table view. It's bounce every time I call reload function as my table view cells are dynamic and having a different type of contents, so I used the property of UITableViewAutomaticDimension for row height.
So, to resolve this bounce issue on the reload function of the table. I have to store the height of the row from tableView willDisplay cell method and used the same in estimatedHeightForRowAt. Please find the code below. Hope it will help someone.
var height_OfCells = NSMutableDictionary()
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
if let height = height_OfCells.object(forKey: indexPath) {
return height as! CGFloat
}
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
height_OfCells.setObject(cell.frame.size.height, forKey: indexPath as NSCopying)
}

How to add animation on tableView Swift

I want to add animation when UITableView load cells (From top to Bottom) But getting no idea about this, please someone help me.
Below added a simple table view delegate methode with an array(arrHeader).
Thanks in advance
let arrHeader = ["City1", "City2", "City3"]
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrHeader.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell")
cell?.textLabel?.text = arrSubHeader[indexPath.row]
return cell!
}
Put your animation code in this UItableView delegate method:
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
//TODO: animation code
}
this delegate method is called every time a new or old cell is going to be displayed on the iPhone/iPad screen.
So if you try something in this block with the cells it will reflect on them.
optional func tableView(_ tableView: UITableView,
didEndDisplaying cell: UITableViewCell,
forRowAt indexPath: IndexPath) { }
this method gets called after a cell is being displayed.
Here is the github link for the Example:
Visit: https://github.com/subhajitregor/TableCellCardDropAnimationExample

Resources