Multiple UITableViewCells each with UIProgressView very slow to update - swift

I am making a game which has 10 UITableViewCells in a UITableView. Each of the 10 UITableViewCells has one UIProgressView plus a lot of other views. I update the UITableView every 1/10th of a second, this is very slow and lags on older devices. I update it every 1/10th second for UX, gives a smooth progress view feel to game.
Is there a way to just update just the Progress Views in each cell individually rather than having to call the tableView.reloadData() that will update all the views in each cell?
Code example:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "businessCell", for: indexPath) as! BusinessCell
cell.progress.progress = Float( businessArray[indexPath.row-1].getCurrentProgress() )
//lots of other views are updated here
return cell
}
}
could I maybe change this line:
cell.progress.progress = Float( businessArray[indexPath.row-1].getCurrentProgress() )
to something like this:
cell.progress.progress = someVarLocalToViewControllerContainingTableView[indexPath.row]
and when I update this local var it would update only the progressView or something?
I have tried many ways but cannot figure out how to do this...

If you need to update a progress for a specific cell, then call this
func reloadProgress(at index: Int) {
let indexPath = IndexPath(row: index, section: 0)
if let cell = tableView.cellForRow(at: indexPath) as? BusinessCell {
cell.progress.progress = Float( businessArray[index - 1].getCurrentProgress() )
}
}
If you need to reload all bars in the table:
func reloadProgress() {
for indexPath in tableView.indexPathsForVisibleRows ?? [] {
if let cell = tableView.cellForRow(at: indexPath) as? BusinessCell {
cell.progress.progress = Float( businessArray[indexPath.row - 1].getCurrentProgress() )
}
}
}

you can use :
self.tableView .reloadRows(at: <[IndexPath]>, with: <UITableViewRowAnimation>)
rather than using tableView.reloadData()
Please check below link:
Is it possible to refresh a single UITableViewCell in a UITableView?
It might help in your scenario.

Related

Change only one specific tableView row height [duplicate]

This question already has answers here:
How to increase the single row height without reloading UITableView or individual cells [Swift]?
(6 answers)
Closed 3 years ago.
I'm looking for a way to change a specific row in my tableView.
I'm using a Notification to check when I do an action in my cell. According to the answer, my goal is to display the next row.
By default, my cell have this property.
if (indexPath.row == 5){
tableView.rowHeight = 0.0
}
if (indexPath.row == 6){
tableView.rowHeight = 0.0
}
return cell
My goal when I'm in my notification is to change the row height value for the fifth row.
Thank you for your help
You could use a Set<IndexPath> and your tableView delegate methods to achieve this.
Say you have a set of selected index paths selectedIndexPaths and heights largeHeight and normalHeight. Your heightForRow func could look like this:
func tableView(_ tableView: UITableView, heigthForRowAt indexPath: IndexPath) -> CGFloat {
guard !selectedIndexPaths.contains(indexPath) else {
return largeHeight
}
return normalHeight
}
Then you could change the height dynamically the following way:
/// Convenience method for selecting an index path
func select(indexPath: IndexPath, completion: ((Bool) -> Void)? = nil){
selectedIndexPaths.insert(indexPath)
tableView.performBatchUpdates({
self.tableView.reloadRows(at: [indexPath], with: .none)
}, completion: completion)
}
In your tableView delegate, you could call this method in didSelect:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
select(indexPath: indexPath)
}
Do the same if you have a method responding to your notification (assuming you placed your indexPath in your notification's userInfo, under the key "indexPathKey"):
func notifiedShouldEnlargeRow(aNotification: Notification) {
guard let indexPath = aNotification.userInfo["indexPathKey"] as? IndexPath else { return }
select(indexPath: indexPath)
}
For reference, look at performBatchUpdates(_:completion) and reloadRows(at:with:).

How to display a label on the last cell only?

I have a chat message table view with two cells to display, depending on whom sent the message.
I want the last cell to display the time, and only the last one. When I use tableView(:willDisplay cell:forRowAt indexPath:), the last cell doesn't show anything...
How can I display the time on that last cell?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if chatBubbles[indexPath.row].user == UserDefaultsService.shared.userID {
let cell = tableView.dequeueReusableCell(withIdentifier: CustomCell.senderCellIdentifier.rawValue, for: indexPath) as! SenderTVC
populateSenderChatBubble(into: cell, at: indexPath)
return cell
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: CustomCell.conversationCellIdentifier.rawValue, for: indexPath) as! ConversationTVC
populateConversationChatBubble(into: cell, at: indexPath)
return cell
}
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if indexPath.row == chatBubbles.count - 1 {
// What to do in here to display the last cell time?
}
}
Here is the method that display the cell content:
func populateSenderChatBubble(into cell: SenderTVC, at indexPath: IndexPath) {
let bubble = chatBubbles[indexPath.row]
let isoDateString = bubble.date
let trimmedIsoString = isoDateString.replacingOccurrences(of: StaticLabel.dateOccurence.rawValue, with: StaticLabel.emptyString.rawValue, options: .regularExpression)
let dateAndTime = ISO8601DateFormatter().date(from: trimmedIsoString)
date = dateAndTime!.asString(style: .short)
time = dateAndTime!.asString()
if dateAndTime!.isGreaterThanDate(dateToCompare: Date()) {
dateToShow = "\(date!) \(time!)"
}
else {
dateToShow = "\(time!)"
}
cell.senderDateLabel.text = dateToShow
cell.senderConversationLabel.text = bubble.content
}
The cell doesn't know it's last unless you tell it, but the tableView does know who's last. With that in mind, I would add a boolean in your cell like this:
var isLastCell: Bool = false {
didSet {
// do stuff if it's the last cell
if isLastCell {
// configure for isLastCell
} else {
// configure it for !isLastCell
}
}
}
When your custom UITableViewCell class initializes, it'll be with isLastCell = false, so assume that in your configuration. Whenever the boolean is updated to true, the cell will update via the didSet.
Then, in your cellForRow method, test the indexPath to see if it's the last indexPath of the datasource, if so, cell.isLastCell = true and the didSet in the cell will trigger to do whatever adjustments you need to do.
Another thing you'll need to do with this implementation is use cellForRow to update isLastCell for not just the last cell, but the cells that aren't last, since cells are created and destroyed all the time and the last cell at one moment might not be the last cell in another.

Trying to bind an output to my tableviewCell UI Element in RxSwift

I have an output that lives in my VM and based on some change I want my textfield that resides in my custom tableviewCell to change in some way. I am unsure about how to have my UItextfield that lives in the tableviewcell bind to my output.
let index = IndexPath(row: 0, section: 0)
if let cell = tbl.cellForRow(at: index) as? TableViewCell {
print(cell.textF.text)
// Do whatever you want
}
I am not sure when you want to get the data, but if you want to do this when the user touches the cell, just implement the following method of UITableViewDelegate:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) as? YourTableViewCellClass {
let cellLabelText = cell.yourTextField.text
// Do whatever you want with cellLabel
}
}

Swift - UITableViewCell label position is shifted after scrolling

The Table View Cells are laid out correctly during the initial load, but when I scroll up or down, the labels are shifted to the left. I'm not sure what is going on here.
My code for the tableview cell is below.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let workout = self.workouts[indexPath.row] as? Workout
let cell = tableView.dequeueReusableCellWithIdentifier("WorkoutCell") as? WorkOutCell
cell!.textCell?.text = workout?.title
cell!.backgroundColor = workout?.color
cell!.countLabel.text = "\(indexPath.row+1)"
cell!.selectionStyle = UITableViewCellSelectionStyle.None
return cell!
}
Image:
Thanks much!
Is there any changes if use this instead?
let cell = tableView.dequeueReusableCellWithIdentifier("WorkoutCell", forIndexPath: indexPath) as? WorkOutCell

Swift - change height of mapview inside a tableView

(Look at ##Edit##)
I'm a beginner. I used google but can't find a solution for my problem.
How can I change the height of my MapView inside the tableView?
My storyboard looks like:
click here I cant post pictures
I can change the size of the MKMapView however I want but nothing changed "in real/in the emulator".
I tried to change the size of the cells with:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if(indexPath.row == 0) {
return 500
}
return 75.0
}
But indexPath.row=0 ist the first entry "Name: Test"
Can somebody explain how and why I can't change the size? How can I change the size?
Thank u for help.
EDIT
I tried it with two different cells ("Map Cell" and "listAllBakeries") and now it works with the size.
When I click on an entry there isn't an update at the "Map Cell" but on the "listAllBakeries". More than one Map
The code:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//CODE TO BE RUN ON CELL TOUCH
//get the Location with latitude and longitude
var location = CLLocationCoordinate2D(latitude: latitude[indexPath.item], longitude: longitude[indexPath.item])
//resolution
var span = MKCoordinateSpanMake(0.003, 0.003)
var region = MKCoordinateRegion(center: location,span:span)
//here is the mistake
let cell2:ListPlacesTableViewCell = tableView.dequeueReusableCellWithIdentifier("MapCell", forIndexPath: indexPath) as! ListPlacesTableViewCell
//some Text about the Place
var annotation = MKPointAnnotation()
annotation.coordinate = location
annotation.title = namesBakeries[indexPath.item]
annotation.subtitle = "some Text"
//Zooming inside the map
cell2.Map.setRegion(region, animated: true)
cell2.Map.addAnnotation(annotation)
}
I don't really understand this. I use the right identifier (MapCell) but the section/row is wrong. How can I use the right section?
Here is my code how I fill the rows/section
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if(indexPath.section == 0) {
let cell2:ListPlacesTableViewCell = tableView.dequeueReusableCellWithIdentifier("MapCell", forIndexPath: indexPath) as! ListPlacesTableViewCell
//insert MapStuff
//addAnnotations
//MapStuff
return cell2
} else {
//Standard-Labels with text
let cell:ListPlacesTableViewCell = tableView.dequeueReusableCellWithIdentifier("listAllBakeries", forIndexPath: indexPath) as! ListPlacesTableViewCell
cell.nameLabel?.text = namesBakeries[indexPath.row]
cell.adressLabel?.text = "Test 2"
return cell
}
}
Thank for help
(I'm sorry for my english but this isn't my mothertongue).
It looks like your map is above the uiTableView rather than a separate prototype cell in your table.
Set your uiTableView up with 2 prototype cells then put the map in the first.
Then, as you have done, you can test the value of indexpath.row.
Okay I found out.
The trick is to update always the same cell.
I defined an array. Then save my Cell2 in this array and just used this Object to update my map. (My english is awful I know, maybe my code will give more information.)
class ListPlacesTableViewController: UITableViewController {
var constantCell = [ListPlacesTableViewCell?](count: 1, repeatedValue: nil)
....
//other stuff here
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if(indexPath.section == 0) {
let cell2:ListPlacesTableViewCell = tableView.dequeueReusableCellWithIdentifier("MapCell", forIndexPath: indexPath) as! ListPlacesTableViewCell
constantCell[0] = cell2
//other stuff here
}
}
//Clickable Cell Function add:
constantCell[0]!.Map.setRegion(region, animated: true)
constantCell[0]!.Map.addAnnotation(annotation)
Thanks for help