Display cells of tableview gradually using autolayout? - ios

I have a tableview inside my UIViewController to display comments. The height of this tableview depends on the number and the size of comments. The cells are dynamic.
I use autolayout, so my tableview has a height constraint. I set this constraint programmatically :
override func viewDidLayoutSubviews() {
self.heightCommentsTableView.constant = self.commentsTableView.contentSize.height + 50
}
It works if I display all the comments at once.
BUT, I would like display the comments 5 per 5, using this method : load more for UITableView in swift because of performance issue to display my view
(all my comments are loaded before pushing the view)
I noticed when I set my constraint, it calls cellForRowAtIndexPath for all the comments, not only the first 5.
I don't know how to do.
EDIT
var allCommentsArray: NSMutableArray = []
var elements: NSMutableArray = []
var range = 5
var currentPage = 0
var nextpage = 0
override func viewDidLoad() {
super.viewDidLoad()
allCommentsArray = NSMutableArray(array: comments)
elements.addObjectsFromArray(allCommentsArray.subarrayWithRange(NSMakeRange(0, range)))
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return comments.count
}
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
self.nextpage = self.elements.count - 5
if indexPath.row == nextpage {
self.currentPage++
self.nextpage = self.elements.count - 5
self.elements.addObjectsFromArray(self.allCommentsArray.subarrayWithRange(NSMakeRange(self.currentPage, self.range)))
tableView.reloadData()
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CommentCustomTableViewCell", forIndexPath: indexPath) as! CommentCustomTableViewCell
self.configureCell(cell, atIndexPath: indexPath)
return cell
}

The table view will call tableView(_:cellForRowAtIndexPath:) for every visible cell it is about to create, the amount of it will determine based on how many sections and how many cells in each section. You let the table vie know these amounts by implementing the tableView(_:numberOfRowsInSection:) and numberOfSectionsInTableView(_:) methods of UITableViewDataSource. So, if you want to control how many cells could possibly be created and visible at a given time, you'd have to manage that state in your data source by adding and removing according to whatever logic you desire. In the answer that you linked, you can see that he is called elements.addObjectsFromArray to progressively add more elements in batches.

Related

TableView empties when interacting

So I have 2 Views that are shown inside a UIView, based on what is selected on the SegmentViewController. I create dummy data, returning 20 row of a custom cell. This works great.
Everything is fine, till I interact with the TableView.
Bellow is my code:
GoalsViewController.swift
import UIKit
class GoalsViewController: UIViewController {
#IBOutlet weak var goalsTableView: UITableView!
let goalCellIdentifier = "goalCell"
override func viewDidLoad() {
super.viewDidLoad()
goalsTableView.delegate = self
goalsTableView.dataSource = self
goalsTableView.register(UINib(nibName: "GoalsViewCell", bundle: nil), forCellReuseIdentifier: goalCellIdentifier)
goalsTableView.reloadData()
}
}
extension GoalsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = goalsTableView.dequeueReusableCell(withIdentifier: goalCellIdentifier, for: indexPath) as! GoalsViewCell
cell.goalTitle.text = "aaaaa"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Selected \(indexPath.row)")
}
}
After any of the empty rows is selected, the didSelectRowAt is not called, so the cells are not there at all. I tried to find a solution, but I was only to find issues about empty lists, before being populated.
What could be the reason for the empty tableview?
I might be wrong here but one thing that I've noticed is that you are not implementing a function which sets the height of each cell.
// Specify the height of your cells
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100 // Or your given cell height.
}
So here is my theory: If you are using constraints something is missing and your cell's height can't be identified by constraints alone or you are not using constraints at all thus you must use heightForRowAt function to specify each cell's height.
I will explain what was the issue for people who probably did not know (like me).
So my UITableView is inside a UIView that changes based on what the user is selecting. In total I had 2 different Views that where switching. The reason that it was emptying it was because my parent ViewController, could not access the delegate for UITableView. To fix that, after adding a subview to the UIView, you need also to move the ViewController to the parent controller. In code it goes like this.
// Empty array of UIViewControllers
var views: [UIViewController]!
// Add the UIViewControllers to the array
views = [UIViewController()]
views.append(EventsViewController())
views.append(GoalsViewController())
for view in views {
// Needed to adjust the size of Subview to size of View
view.view.frame = containerView.bounds
// Add the subviews
containerView.addSubview(view.view)
}
// Bring the view in position 1 to the front of the UIView
containerView.bringSubviewToFront(views[1].view)
// Add Views[1] UIViewController as a child to the parent controller
self.addChild(views[1])
views[1].didMove(toParent: self)
// After done with everything with the UIViewController remove it
views[1].removeFromParent()
addChild Apple.com
didMove Apple.com

Table View Cells Double When I Add A New Cell - Swift 4

I am currently making a golf round tracker that displays your rounds in a table view with the cells being xibs. When I add one round, it appears fine on the table view, but when I add another round it adds that cell and doubles the cells. Here is a picture of what happens: https://i.stack.imgur.com/SOgN4.png. Here is my code:
class RoundDisplay: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
arrayOfCellData.append(roundData(id : arrayOfCellData.count + 1, date : datePlayedFC, course : currentCourse, score : String(score1234) ))
tableView.reloadData()
print(arrayOfCellData)
}
override func numberOfSections(in tableView: UITableView) -> Int {
//Shows how many cells it should display; number of current cells
return arrayOfCellData.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//Shows how many cells it should display; number of current cells
return arrayOfCellData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//Defines what xib to use for cells
let cell = Bundle.main.loadNibNamed("TableViewCell", owner: self, options: nil)?.first as! TableViewCell
//Adds array data to each cell
cell.DateLbl.text = arrayOfCellData[indexPath.row].date
cell.CourseName.text = arrayOfCellData[indexPath.row].course
cell.ScoreLbl.text = arrayOfCellData[indexPath.row].score
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
//Shows how high to make cells; height of xib
return 68
}
}
The number of sections function needs to be set to one. I realized that it decided how many cells it should show for the data from the array. Which means if you have the function set as I did, for instance, if you had two sets of data in the array, it would put two cells for each data. Thanks to #Magnas and #vacawama for the help!

How to access to a textLabel in a static cell

After much searching and reading I unfortunately do not come from the following. I want to use static tables to display certain data. (Are there better options?)
In my view I first put an onion picture with a container view underneath. The container view again refers to a Table View Controller.
I made an outlet from the cells and then I thought I could easily adjust the text.
Now I want to change the text of the fields in the table, but unfortunately I do not succeed.
When I start the app then the table is completely empty as seen on the screenshot.
What am I doing wrong ?
class TableViewController: UITableViewController {
var data: [String] = ["Muis", "Aap", "Koe", "Vis"]
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return data.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let iets = data[indexPath.row]
cell.textLabel?.text = iets
return cell
}
}
If you want to use static cells
Forget dequeueing UITableViewCell instances and all tableview data source and delegate methods.
In Interface Builder select the table view and select Static Cells from the Content popup
Drag the amount of static cells you need into the canvas
In the view controller declare IBOutlets and connect them directly to the UI elements in the cells
You need to change your way of thinking for this one. You do not own the cells, the UITableView does. It will provide cells as it seems fit by using your implementations of UITableViewDataSource:
func numberOfSections(in: UITableView) -> Int
func tableView(UITableView, numberOfRowsInSection: Int) -> Int
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell
Normally, the texts (your actual data) would be held in a list available to this data source.
Example:
var data: [String] = []
// Other functions
func numberOfSections(in: UITableView) -> Int {
return 1
}
func tableView(UITableView, numberOfRowsInSection: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "YOUR_IDENTIFIER")
cell.text = data[indexPath.row]
return cell
}
Now, if you want to change this cell's text, all you have to do is update your data list and reload the data.
What I have done after a lot of testing and reading. I have create a segue to the statutable class.
if (segue.identifier == "myEmbeddedSegue") {
let childViewController = segue.destination as! hondDetialTableViewController
childViewController.hondId = hondData["hondId"]!
}
In this segue I send only the hondId, everything else i ask entities.
I'm sorry but this is not at all how UITableView works. The UITableViewCell that you define in the Xib/Storyboard within the tableview are just "models" or templates, they don't actually exists until you dequeue them.
You can read how UITableView works here: http://www.thomashanning.com/uitableview-tutorial-for-beginners/
You have to return numberOfSections > 0 if you want anything displayed in your tableview; similarly, that section has to also have numberOfRows > 0 otherwise again, nothing will be displayed (ok, maybe headers and footers if those are properly setup).
At any rate, cells are only accessible after you dequeue them. Creating an outlet in a XIB to a UITableViewCell is useless in most cases.
You can explore other options, such as UIStackView, or maybe what you need is just plain custom UIView with labels that you properly set and layout using NSLayoutConstraints. There are plenty of resources out there, this is just one I quickly Googled for you to get started: https://www.youtube.com/watch?v=de0sthle44I
Good Luck.

Identify when the UITableView loads the visible set of rows in iOS

I am looking for the event that gets fired when the tableview loads the rows to fit the screen. I know tableview loads only the number of rows that fit the screen. I want to execute a set of code when the rows that fit the screen are loaded.
Any pointers on how to determine this?
I think you'd need to implement UITableViewDelegate and override the WillDisplay(UITableView, UITableViewCell, NSIndexPath) method. Be sure to set the delegate of your tableview to the class that implements UITableViewDelegate.
Unfortunately WillDisplay is called per cell, not per row.
I am not good at c#, so please translate this from Swift. I've added another solution down below which is, sort of a manual calculation of visible rows in tableview.
The Recommended Solution:
var isFirstTime:Bool = true
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if let indexPaths = tableView.indexPathsForVisibleRows {
if indexPath == indexPaths.last {
if isFirstTime {
self.visibleCellsLoaded()
}
isFirstTime = false
}
}
}
The isFirstTime (Very important) flag will restrict your specific "set of code" (self.visibleCellsLoaded()) to execute only once. You can remove it if you want it to be executed every time you scroll - which apparently negates the purpose of your question.
Another solution that also works:
Here we manually calculate and get what tableView.indexPathsForVisibleRows returns us (Result is mostly similar to the previous method)
fileprivate func getLastVisibleIndexPath() -> IndexPath {
//This samples the first indexPath only, so this works only with rows that have static height, not with UITableViewAutomaticDimension.
let firstIndexPath = IndexPath.init(row: 0, section: 0)
let tableViewHeight = self.tableView.bounds.height
let rowHeight = tableView.rectForRow(at: firstIndexPath).size.height
let numberOfVisibleRows = tableViewHeight / rowHeight
return IndexPath.init(row: Int(numberOfVisibleRows - 1), section: 0)
}
var isFirstTime:Bool = true
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if indexPath == getLastVisibleIndexPath() {
if isFirstTime {
self.visibleCellsLoaded()
}
isFirstTime = false
}
}

iOS Swift - Is there a way to add a subview into a table cell and position it correctly?

So, here`s my problem:
I want to create a table to display some flight data. The idea is that each cell represents a trip (i.e all flights necessary to go from city A to B). Each flight has 3 properties which I would divide in separate labels, composing the line. Because of that, the cell must adjust its height accordingly.
This is what I`ve done so far:
My view controller class:
#IBOutlet weak var dispTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
dispTable.dataSource = self
dispTable.delegate = self
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCellWithIdentifier("DispCell") as? DispTableViewCell {
let flight1: Flight = Flight()
let flight2: Flight = Flight()
curFlightsIda = [Flight]()
Flight1.Num = "1221"
Flight2.Num = "1331"
curFlightsIda.append(Flight1)
curFlightsIda.append(Flight2)
for voo in curFlightsIda {
let lbl = UILabel(frame: CGRectMake(0, 0, 100, 50))
lbl.textAlignment = NSTextAlignment.Center
lbl.text = voo.Num
cell.contentView.addSubview(lbl)
}
return cell
} else {
return DispTableViewCell()
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 100
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
The flight.num there is just for tests. I was able to create the label programmatically, but I feel that the way Im going, the layout wont work properly.
So, how can I create the labels representing the lines and place them correctly? Am I on the right track? I searched around and found about XIBs, but I'm not sure if it's better..? Honestly I`m completely lost and any help would be deeply appreciated.
I`m not sure if this helps, but it would be something like this:
TRIP CELL:
Flight1Num Flight1Departure Flight1Duration
Flight2Num Flight2Departure Flight2Duration
Flight3Num Flight3Departure Flight3Duration
Don't.
Why:
cellForRowAtIndexPath gets called for every cell (also during scrolling). If you are trying to do too much, it won't scroll smooth. Furthermore cells are reused (as dequeue indicates it). iOS only creates the number of visible cells instances plus one or two extra cells. If a cell is reused, you would need to remove the already added labels before adding new ones which leads to the next problem: How can you identify them (either search for all subviews and check if it is an instance of UILabel or use a tag and find them by tag.... anyway: If you are trying to do too much, it won't scroll smooth.
Instead:
Define the labels in your template cell 'DispCell' or create different template cell in your storyboard (and dequeue the correct one by name). There you can use storyboard layout constraints in order to position your labels correctly.
If you have a flexible number of curFlightsIda, you can either think about a max number (and hide empty labels) or maybe it works out to use a textfield instead of a label.

Resources