I have a view and a tableview which it shows file list. When user click button in view then I open tableview. User click the file list row then I started to download file from my server and when download start I changed the file name like "11111", when download finish change file name like "22222" (now change the file name later I will put progress view)
In first run everything is working correctly. Download and change name working. But in tableview when I come back to view and go to tableview again, download is working but not change then file name in tableview.
What is wrong in my code and how can I show text value?
PS: When print the text value in tableview before return cell, it shows correct text.
My codes:
func setProgressValue(_ dict : NSDictionary){
//Download progress value
let cellProgressValue = dict.value(forKey: "value") as! Float
if cellProgressValue < 1.0{
fileList[cellNum].title = "111111"
updateTable()
}
else{
fileList[cellNum].title = “22222”
updateTable()
}
}
func updateTable(){
self.tableView.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return fileList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "fileCell"
let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
cell.textLabel?.text = self.fileList[indexPath.row].title
return cell
}
//Call in viewdidload
func getFiles(){
for index in songList{
let urlString = "https://www.myapis.com/data/files"
Alamofire.request(urlString).validate().responseJSON { response in
let result = JSON(response.result.value)
if let items = result["items"].array {
for item in items {
//print(item)
let id = item["fileId"].stringValue
let title = ["fileName"].stringValue
let file = Files()
file.id = id
file.title = title
self.fileList.append(song)
}
self.tableView.reloadData()
}
}
}
}
Related
I would like to retrieve data from my simple Firestore database
I have this database:
then I have a model class where I have a method responsible for retrieving a data which looks like this:
func getDataFromDatabase() -> [String] {
var notes: [String] = []
collectionRef = Firestore.firestore().collection("Notes")
collectionRef.addSnapshotListener { querySnapshot, error in
guard let documents = querySnapshot?.documents else {
print("Error fetching documents: \(error!)")
return
}
notes = documents.map { $0["text"]! } as! [String] // text is a field saved in document
print("inside notes: \(notes)")
}
print("outside notes: \(notes)")
return notes
}
and as a UI representation I have tableViewController. Let's take one of the methods, for example
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("tableview numberOfRowsInSection called")
return model.getDataFromDatabase().count
}
Then numberOfRows is 0 and the output in the console is:
and I am ending up with no cells in tableView. I added a breakpoint and it doesn't jump inside the listener.
And even though I have 3 of them, they are kinda "late"? They are loaded afterwards. And then the tableView doesn't show anything but console says (later) that there are 3 cells.
If needed, there is also my method for showing the cells names:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("Cells")
let cell = tableView.dequeueReusableCell(withIdentifier: "firstCell", for: indexPath)
cell.textLabel!.text = String(model.getDataFromDatabase()[indexPath.row].prefix(30))
return cell
}
but this method is not even loaded (no print in the console) and this method is written below the method with numberOfRowsInSection.
I have also 2 errors (I don't know why each line is written twice) and these are:
but I don't think it has something to do with the problem.
Thank you for your help!
As #Galo Torres Sevilla mentioned, addSnapshotListener method is async and you need to add completion handler to your getDataFromDatabase() function.
Make following changes in your code:
Declare Global variable for notes.
var list_notes = [String]()
Add completion handler to getDataFromDatabase() method.
func getDataFromDatabase(callback: #escaping([String]) -> Void) {
var notes: [String] = []
collectionRef = Firestore.firestore().collection("Notes")
collectionRef.addSnapshotListener { querySnapshot, error in
guard let documents = querySnapshot?.documents else {
print("Error fetching documents: \(error!)")
return
}
notes = documents.map { $0["text"]! } as! [String] // text is a field saved in document
print("inside notes: \(notes)")
callback(notes)
}
}
Lastly, call function on appropriate location where you want to fetch notes and assign retrieved notes to your global variable and reload TableView like below:
self.getDataFromDatabase { (list_notes) in
self.list_notes = list_notes
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
Changes in TableView:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("tableview numberOfRowsInSection called")
return self.list_notes.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("Cells")
let cell = tableView.dequeueReusableCell(withIdentifier: "firstCell", for: indexPath)
cell.textLabel!.text = String(self.list_notes[indexPath.row].prefix(30))
return cell
}
All you need to do is refresh the table cell every time you retrieve the data. Put this code after you set your data inside the array.
self.tableView.reloadData()
I've written a program where a user can take pictures and track information about their miniature collections and everything works as intended. Now I'm looking to add some different functionality for the tableview, and I'm not quite sure what would be the best way to go about it. At present when the user adds a model, it appends to a single dictionary of dictionaries then displays it in the tableview in the order it was appended. I would like to sort or separate the data into separate sections based on what codex the model is from.
Whether it would be better to generate separate sections programmatically or use an index, I'm not sure. But in either case, I am at a complete loss of how to accomplish this.
Here is the tableview code I currently have, in case it helps
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 models.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Dequeue reusable Cell
let cell = tableView.dequeueReusableCell(withIdentifier: "modelCell", for: indexPath) as! modelCellTableViewCell
// Fetch Item
let model = models[indexPath.row]
// Configure Table View Cell
cell.modelNickname?.text = model.modelNickname
cell.modelNickname.textColor = UIColor(red:0.24, green:0.31, blue:0.35, alpha:1.0)
cell.modelName?.text = model.modelName
cell.codexName?.text = model.codexName
cell.modelOption1?.text = model.modelOption1
cell.modelOption2?.text = model.modelOption2
cell.modelOption3?.text = model.modelOption3
cell.modelOption4?.text = model.modelOption4
let fileManager = FileManager.default
let imageName = model.codexName + model.modelName + model.modelOption1
let imagePath = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent(imageName)
if fileManager.fileExists(atPath: imagePath){
cell.modelImage.image = UIImage(contentsOfFile: imagePath)
}else{
print("No Image found")
}
return cell
}
Any help/suggestions you could offer would be a big help.
My task is user data appending into items array with strut method and assigning to tableView tableData array. In my code multiple places I used tableData for some validations.
Now, my problem is I can able to see my table data when I move background to foreground but If I remove application from background then again If I am open my application, There is empty tableView. So, I need to understand. how tableData store into UserDeafult and then retrieve to load tableView for avoid data loss.
// Array declaration
var items = [Item]()
var tableData = [Item]()
public func documentPicker(_ controller: UIDocumentPickerViewController,didPickDocumentsAt urls: [URL]) {
// Here I am getting user selected file url and its name from iCloud.
// I skipped to paste here.
// User picked file data appending into items array
items.append(Item(url: bookurl, title: name))
// Assign items data to tableData
if let data = UserDefaults.standard.data(forKey:"items") {
do {
let itemsUser = try PropertyListDecoder().decode(Array<Item>.self, from: data)
tableData = itemsUser
} catch { print(error) }
}
}
// MARK - TABLE VIEW DELEGATIONS
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.tableData.count
}
// TableView data-load
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell
let item = tableData[indexPath.row]
cell.name_label.text = item.name
}
return cell
}
For above scenario UserDefault should be outside of the picker delegation. If we maintain within viewDidload with some logic then It will work well.
//Within ViewDidLoad
if let data = UserDefaults.standard.data(forKey:"items") {
do {
let itemsUser = try PropertyListDecoder().decode(Array<Item>.self, from: data)
tableData = itemsUser
} catch { print(error) }
}
I have a particular conundrum where I need a specific UILabel inside a UITableViewCell to update every minute. Currently, every minute, the whole entire cell refreshes and displays beneath the previous one, see below, all I want to do is refresh that UILabel called watchTime:
Here's my tableView where I initialize the watch time minute count from the model
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "watchTimeCell", for: indexPath) as! WatchTimeCell
if userModel.count > indexPath.row {
//this is the value i want to update
cell.watchTime.text = "\(String(describing: userModel[indexPath.row].watchTime!))"
}
return cell
}
And here's how I update my cell currently:
#objc func updateCounting(){
watchTime += 1
if watchTime % 60 == 0 {
let userRef = Database.database().reference().child("users").child(uid!).child("watchTime")
userRef.runTransactionBlock({ (currentData: MutableData) -> TransactionResult in
let newValue: Int
if let existingValue = (currentData.value as? NSNumber)?.intValue {
newValue = existingValue + 1
} else {
newValue = 1
}
currentData.value = NSNumber(value: newValue)
//this is the line where I reload the cell
DispatchQueue.main.async(execute: {
self.watchTableView.reloadData()
})
return TransactionResult.success(withValue: currentData)
})
watchTime = 0
}
}
What's the best way to go about this? Thanks!
EDIT: Added numberOfRowsInSection
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userModel.count
}
What you're doing is essentially correct for a table view. You update the model and call reload to propagate that thru cellForRowAt to the table view. You could, in this situation, save some overhead by calling reloadRows(at:with:) so as to reload only the one cell.
Except...
You have only one cell. But a one-cell table view is ridiculous. What's its purpose? To make the interface scrollable? Then just make a scroll view. Now you can update the label directly.
I would create that one cell, with a reference to it in the ViewController that is holding the tableView.
let mainCell = WatchTimeCell()
Inside the WatchTimeCell class I would add a public func to update the time count
public func updateTimeCountLabel(_ count: Int) {
self.nameOfLabel.text = "\(count)"
}
Then within the updateCounting() I would call the updateTimeCountLabel inside the WatchTimeCell.
self.mainCell.updateTimeCountLabel(newValue)
But there is something happening within the numberOfRowsForSection, could you post that?
I am new to Swift , I am parsing my JSON by using ObjectMapper but I want data to be displayed in TableView. But I have a problem:
libc++abi.dylib: terminating with uncaught exception of type NSException
I get it after the method numberOfRowsInSection. My array is not nil, array has a 2193 elements
I do not understand why it happened
It my code for parsing JSON :
let timeStamp = NSNumber(value: Date().timeIntervalSinceNow)
var programs = [PrograToDayModel]()
override func viewDidLoad() {
super.viewDidLoad()
super.viewDidLoad()
let timeStamp = NSNumber(value: Date().timeIntervalSinceNow)
self.downloadPrograms(for: timeStamp)
}
func downloadPrograms(for timestamp: NSNumber) {
Alamofire.request("http://52.50.138.211:8080/ChanelAPI/programs/\(timestamp)").responseArray { (response: DataResponse<[PrograToDayModel]>) in
let programlArray = response.result.value
if let programlArray = programlArray {
for program in programlArray {
self.programs.append(program)
print(program.title as Any)
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
it good i print element in console :
my code for table:
// 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
print(self.programs.count as Any)
return self.programs.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ProgramTableViewCell", for: indexPath) as! ProgramTableViewCell
cell.title.text = self.programs[indexPath.row].title
return cell
}
}
All identifiers in place
I using tab bar, tableView, tableViewCell
How can I solve this problem?
To identify the issue, you can just try this -
it might be a reason for that issue
So go to Main.storyboard, and right-click on View Controller at the top of the phone outline and remove any outlets with yellow flags (if any).
I was getting a similar non descriptive error when trying to initialize a uitableviewcontroller when trying to add a section/number of rows. Did you register a tableview cell class? I see that you have a custom tableview cell created, so if that isn't registered with your tableview that might be causing this error.
tableView.register("ProgramTableViewCell".self, forCellReuseIdentifier: "ProgramTableViewCell")