Cannot select rows after tableView.reloadData() - ios

In my application, I have a tableview that constantly (every 5 seconds) needs updating. To do this, I have set a timer which runs a method updating the data source (array) and calling the tableView.reloadData() method immediately afterwards. Unfortunately, a problem arises after the reloadData() method is called, one in which I cannot select a row without first scrolling! It's very strange. All I have to do is slightly make the table view scroll, and I can select cells again. Help!
viewDidLoad
DataHandler.updateCustomers() {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.updateTable()
})
}
let _ = NSTimer.scheduledTimerWithTimeInterval(5, target: self, selector: "updateData", userInfo: nil, repeats: true)
updateData
func updateData(){
DataHandler.updateCustomers() {
if (self.activeIDs.indexPathForSelectedRow == nil){
self.activeIDs.reloadData()
}
}
print("Data updated")
}
Thanks in advance!

Related

Looking for best practices when updating a tableview by monitoring for changes in DB

I'm somewhat new to this and this is my first question on stackoverflow. Thanks in advance for your help and bear with me if my formatting sucks
I've got multiple views within my app (all displaying data using tableview subviews) that need to update automatically when the data changes on the database (Firestore), i.e. another user updates the data.
I've found a way to do this which is working well, but I want to ask the community if there's a better way.
Currently, I am creating a Timer object with a timeInterval of 2. On the interval, the timer queries the database and checks a stored data sample against updated data. If the two values vary, I run viewDidLoad which contains my original query, tableView.reloadData(), etc..
Any suggestions or affirmations would be very useful.
var timer = Timer()
var oldChallengesArray = [String]()
var newChallengesArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
//set tableview delegate
mainTableView.delegate = self
mainTableView.dataSource = self
//set challengesmodel delegate
challengesModel.delegate = self
//get challenges
DispatchQueue.main.async {
self.challengesModel.getChallenges(accepted: true, challengeDenied: false, incomingChallenges: false, matchOver: false)
self.mainTableView.reloadData()
}
scheduledTimerWithTimeInterval()
}
func scheduledTimerWithTimeInterval(){
// Scheduling timer to Call the function "updateCounting" with the interval of 1 seconds
timer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(self.updateTableView), userInfo: nil, repeats: true)
}
#objc func updateTableView(){
ChallengeService.getAllUserChallengeIDs(accepted: true, challengeDenied: false, matchOver: false) { (array) in
if array.isEmpty {
return
} else {
self.newChallengesArray = array
if self.oldChallengesArray != self.newChallengesArray {
self.oldChallengesArray = self.newChallengesArray
self.newChallengesArray.removeAll()
self.viewDidLoad()
}
}
}
}
Firestore is a "realtime database", that means that the database warns you when changes happen to the data. To achieve that the app needs to subscribe to relevant changes in the db. The sample code below can be found here:
db.collection("cities").document("SF")
.addSnapshotListener { documentSnapshot, error in
guard let document = documentSnapshot else {
print("Error fetching document: \(error!)")
return
}
guard let data = document.data() else {
print("Document data was empty.")
return
}
print("Current data: \(data)")
}
Also, I would like to point out that calling viewDidLoad is incorrect, you should never call viewDidLoad yourself, create an func to update the data. Something like this:
DispatchQueue.main.async {
self.mainTableView.reloadData()
}

how can I refresh data in a collection view in swift 4?

I have a function to refresh the data of an array every 2 seconds
var timer = Timer()
func timeRefresh(){
timer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(ViewController.refreshData), userInfo: nil, repeats: true)
}
I receive the data from a post service
#objc func refreshData(){
post(postString, Route) { (res) in
let success = res["success"]
if success == true {
let walkers = res["walkers"]
for secondItem in walkers.array! {
let duration = secondItem["duration"]
self.timeCar.append(duration.stringValue) //this is the info that i need for the collection view
}
}
print("array time \(self.timeCar)")
}else{
self.timeCar = ["-.-","-.-","-.-","-.-","-.-"]
}
}
}
I need refresh the collection view every second
cell.typeLabel.text = timeCar[indexPath.row]
UICollectionView has multiple ways of reloading data.
If you want every cell to be reloaded:
collectionView.reloadData().
If you want a specific section to be reloaded: collectionView.reloadSections(<Array of Sections>).
If you want to reload specific cells: collectionView.reloadItems(<Array of IndexPaths>).

Handle thousands of socket message in iOS

I am working on a code where I need to display socket messages in the tableview and tableview get scroll to bottom with slow animation. I handle that but if the message load from socket is continues (consider 20/30 message per second) then the UI get freeze. I need to show the message like Facebook do on live screen one or two messages per iteration.
Here is my code
override func viewDidLoad() {
super.viewDidLoad()
Timer.scheduledTimer(timeInterval: 0.025, target: self, selector: #selector(self.scrollTableView(_:)), userInfo: nil, repeats: true)
self.designLayout()
}
#objc func scrollTableView(_ timer: Timer) {
guard messagesData.count > 0 else {
return
}
if tableView.contentSize.height > tableView.bounds.height {
tableView.contentInset.top = 0
}
tableView.scrollToRow(at: IndexPath(row: messagesData.count - 1, section: 0), at: UITableViewScrollPosition.bottom, animated: true)
}
self.socket.on(“key”) {data, ack in
print("data type is \(type(of: data))")
let arrayValue = data as Array<Any>
DispatchQueue.global(qos: .background).async {
self.handleCountsAndMessaging(data: arrayValue)
}
}
func handleCountsAndMessaging(data: Array<Any>) {
if let arrData = data[0] as? NSMutableDictionary {
if(arrData.object(forKey: "text") != nil) {
self.tableDataLoading(str: String(describing: arrData.object(forKey: "text")!))
}
}
}
func tableDataLoading() {
print ("sttttttt \(str)")
DispatchQueue.main.async {
self.messagesData.add(str)
self.tableView.reloadData()
}
}
Some times I get messages like 200 at time from socket and again 200 while processing previous messages, CPU consumption is showing like 120 and UI get freeze.
Thanks in advance.
Well, there is 'live' and 'live':)
This a phone we are talking about, so it's useless to update the screen every 0.025 seconds... you can easily slow down your fetch time but let's work one problem at a time :
1/ Don't use tableView.scrollToRow, but rather 'insertRows(at indexPaths: [IndexPath], with animation: UITableViewRowAnimation)' to insert a new row, this way if your user is reading, something, he won't be disturbed if you add 200 new item
https://developer.apple.com/documentation/uikit/uitableview/1614879-insertrows
2/ Slow down your UI updates, you are overusing your main thread... You can fetch every 25ms if you like, but I recommend using a throttling mechanism to update the UI only if you get new content, and only if last UI update wasn't less than 5 seconds ago.

Fail to change the label text with a timer in swift

As a swift beginner, I'm building a simple app which will get data from a website to update the label text.
In the ViewController.swift I begin with a function called requestCycle():
override func viewDidLoad() {
super.viewDidLoad()
requestCycle()
}
In this requestCycle() function I create a timer to call the http request function basicAuthHttpRequest():
func requestCycle(){
self.timer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(ViewController.basicAuthHttpRequest), userInfo: nil, repeats: true);
RunLoop.current.add(self.timer, forMode: RunLoopMode.commonModes);
}
In the basicAuthHttpRequest() function, I set up an http request to get data from a url, and use the data to update the label text:
...
//http request, parse json, store the data in TempIn
let TempInString = String(describing: TempIn!)
self.TempInLabel.text = TempInString
print(TempInString)
print(self.TempInLabel.text!)
...
When I run the app, the data will be printed("33" and Optional("33")) and NO ERRORs occur. However, the text of the label shown is not changed at all.
If I use a button to trigger the function basicAuthHttpRequest(), after clicking the button, the label text will be changed in a few seconds.
What's wrong with my poor timer? =.=
You have to update text on the main thread.
DispatchQueue.main.async {
let TempInString:String = String(describing: TempIn!)
self.TempInLabel.text = TempInString as String
}
Try below code :-
DispatchQueue.main.async(execute: { self.TempInLabel.text = TempInString as String })
The timer is running on background thread. so you have to update label value with main thread. You have to use below code for the same.
self.performSelector(onMainThread: #selector(updateUI), with: nil, waitUntilDone: true)
func updateUI() {
print("here update your UI")
}
Also you can do with OperationQueue.
OperationQueue.main.addOperation({
print("here update your UI")
})

Adding Tme Countdown to App Using a Thread

I'm trying to add a countdown timer to an existing app. Naturally, I don't want this timer to stall the rest of the application so I wanted to use an asynchronous thread dedicated to the timer. This is my current code and it doesn't even get to the update function (I used print statements to test this), but does print "Got". Also, I'm trying to update a label with the correct time and you can't do that within the thread. The time variable is a class variable. Not sure if this is even the correct approach, any suggestions?
Edit: Running the timer on main queue doesn't work as it interferes with a pan gesture I already have on the app. Also, any proposed solutions to the timing inaccuracies of the Timer Class would also be great.
func startTimer() {
time = 30
let queue = DispatchQueue(label: "timer")
queue.async {
print("Got")
_ = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
}
}
func update() {
DispatchQueue.main.sync {
if(time >= 0) {
time -= 1
timer.text = String(time)
} else {
timer.text = "0"
}
}
}

Resources