How to fix 'data of UITableViewCell exchange'? - ios

My certain two rows of data of UITableViewCell exchange, and this is how it looks.
The row data after exchanged are the right, and they're wrong before exchanged.
And I'm sorry for not have enough reputation to post .gif image.
Is there any way to avoid the data exchange to the wrong row?
my-data-changes-in-uitableviewcell-when-i-scroll-down-and-get-back
ios-uitableview-mixes-up-data-when-scrolling-too-fast
swift-user-input-mixed-up-when-uitableview-reuses-cells
swift-2-tableview-scrolling-changes-data
The links of problems above seem to be related to mine, but I think my problem is not caused by scrolling.
I setup UITableViewCell with DispatchQueue.global(qos: .background).async, and this function is inside the cell itself.
--SensorRecordTableViewCell (UITableViewCell)--
func getNewLiveInfo(shedId: String, sensorCategory: String) {
DispatchQueue.global(qos: .background).async {
let id = self.shedId.cString(using: .utf8)
let shed_id = UnsafeMutablePointer(mutating: id)
let category = self.sensorCategory.cString(using: .utf8)
let sensor_category = UnsafeMutablePointer(mutating: category)
if let data = getliveInfo(shed_id, sensor_category) {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
// record_id, sensor_id, sensor_category, sensor_value, read_time
self.sensorRecord.recordId = String(cString: data.pointee!)
self.sensorRecord.sensorId = String(cString: (data+1).pointee!)
self.sensorRecord.sensorCategory = String(cString: (data+2).pointee!)
self.sensorRecord.value = Double(String(cString: (data+3).pointee!))
self.sensorRecord.time = formatter.date(from: String(cString: (data+4).pointee!))
DispatchQueue.main.async {
self.setValueAndTime()
}
data.deallocate()
}
}
}
And call the function above from func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
--UITableViewDelegate, UITableViewDataSource--
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
var cell = cell as! SensorRecordTableViewCell
cell.getNewLiveInfo(shedId: shed.id!, sensorCategory: config.sensorRecordOrder[indexPath.row])
}
Finally, I setup the cell from func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: sensorCellId, for: indexPath) as! SensorRecordTableViewCell
cell.setUpView(shedId: shed.id!, sensorCategory: config.sensorRecordOrder[indexPath.row])
return cell
}
Is there any way to avoid the data exchange to the wrong row?

Here is a simple example to show what it should looks like,
hope it helps.
class SensorRecord {
var recordID: Int = 0
var sensorID: Int = 0
}
class ViewController: UIViewController {
private var dataSource = [SensorRecord]()
override func viewDidLoad() {
super.viewDidLoad()
// request infos from your server
getNewLiveInfo(completion: { (sensorRecords)
// after the request success, reload data
self.dataSource = sensorRecords
self.tableView.reloadData()
})
}
}
// MARK: UITableViewDataSource
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ID", for: indexPath)
// let your cell to show datas from your server
let sensorRecord = dataSource[indexPath.row]
cell.update(with: sensorRecord)
return cell
}
}

Related

How to parse JSON to the tableView in Swift?

Please give me advise, I can not figure out how to parse data in a table view properly. My goal is to make a tableView with all continents, not just with one "Africa" cell.
Here is my model:
struct ContinentRoot: Codable {
let links: ContinentMiddle
}
struct ContinentMiddle: Codable {
let continentItems: [ContinentsResponse]
}
struct ContinentsResponse: Codable {
let name: String
let href: String
}
In ViewController I add tableView, continentsArray ([ContinentRoot]) and do some regular things for networking.
I guess that the problem may be here, because in the networking method everything seems normal:
private func getContinentsList() {
guard let url = URL(string: "https://api.teleport.org/api/continents/") else { fatalError("URL failed")}
URLSession.shared.dataTask(with: url) { [weak self] (data, response, error) in
if let data = data {
guard let continent = try? JSONDecoder().decode(ContinentRoot.self, from: data) else { fatalError("DecodingError \(error!)") // REMEMBER: the highest struct
}
self?.continentsArray.append(continent)
}
DispatchQueue.main.async {
self?.tableView.reloadData()
}
}.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return continentsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ContinentsTableViewController", for: indexPath)
let model = continentsArray[indexPath.row].links.continentItems[indexPath.row].name
cell.textLabel?.text = model
return cell
}
In viewDidLoad() I call my methods:
getContinentList()
tableView.delegate = self
tableView.dataSource = self
tableView.register(ContinentsTableCell.self, forCellReuseIdentifier: "ContinentsTableViewController")
setupLayout()
Thank you so much for for attention!
According to your attachment design:
if continentsArray is an array of "ContinentRoot" s.
and you want to show the links in the selected ContinentRoot you must first select it, and use it like below:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return selectedContinent.links.continentItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ContinentsTableViewController", for: indexPath)
let model = selectedContinent.links.continentItems[indexPath.row].name
cell.textLabel?.text = model
return cell
}
if Not you must use your code and change this line:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ContinentsTableViewController", for: indexPath)
let selectedIndex = .zero // or every index you want
let model = continentsArray[indexPath.row].links.continentItems[selectedIndex].name
cell.textLabel?.text = model
return cell
}

How to use searchBar in tableView to search cells with data and filter [duplicate]

This question already has an answer here:
How to make search results are not showing in my table view from my search bar?
(1 answer)
Closed last year.
I have a table view with cells. Here is how it looks like:
The main question is how it possible to make, that when user write "Test", so it should show all what match for this word. Here is some method, which I've tried to use, but it's not working:
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchedArray = activeStates.filter({ titleClass -> Bool in
titleClass.title!.contains(searchText)
})
searching = true
tableView.reloadData()
}
Now, I will show all data which I have. var activeStates: [ActiveState] = []. Here is my tableView methods:
extension HomeViewController: UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 118
}
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let classCell = cell as! ClassTableViewCell
classCell.sd_cancelCurrentImageLoad()
classCell.imgClass.image = nil
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let classCell = cell as! ClassTableViewCell
let asCell = self.activeStates[indexPath.row]
classCell.lblTitle.text = asCell.title
if let imageUrl: String = asCell.thumbnailUrl {
if imageUrl != "" {
classCell.imgClass.sd_setImage(with: URL(string: imageUrl), placeholderImage: placeholderImage)
}
} else {
classCell.imgClass.image = UIImage(named: "classImagePlaceholder")
}
classCell.unreadClassImage.isHidden = unreadManager.unreadTable[asCell.idKey] == nil
classCell.lblTitle.font = unreadManager.unreadTable[asCell.idKey] == nil ? Constants.CustomFont.customFontSemiBold : Constants.CustomFont.customFontBold
classCell.classId = asCell.idKey
classCell.selectionStyle = .none
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
store.dispatch(NavigationAction(destination: .paymentScreen, direction: .forward))
print("SelecterRow")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return activeStates.count
}
//swiftlint:disable force_cast
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = UITableViewCell()
switch tableView {
case tableView:
cell = tableView.dequeueReusableCell(withIdentifier: "classCell", for: indexPath)
case secondTableView:
cell = tableView.dequeueReusableCell(withIdentifier: "classCell", for: indexPath)
default:
print("Something goes wrong")
}
// let cell = tableView.dequeueReusableCell(withIdentifier: "classCell", for: indexPath) as! ClassTableViewCell
return cell
}
I think, maybe need to filter titleName of the cell or something else.
when you are in "searching state" you should get your data from the filtered array (searchedArray) and not from the original activeState array, in your case try something like this:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let classCell = cell as! ClassTableViewCell
let asCell = searching ? self.searchedArray[indexPath.row] :self.activeStates[indexPath.row]
classCell.lblTitle.text = asCell.title
if let imageUrl: String = asCell.thumbnailUrl {
if imageUrl != "" {
classCell.imgClass.sd_setImage(with: URL(string: imageUrl), placeholderImage: placeholderImage)
}
} else {
classCell.imgClass.image = UIImage(named: "classImagePlaceholder")
}
classCell.unreadClassImage.isHidden = unreadManager.unreadTable[asCell.idKey] == nil
classCell.lblTitle.font = unreadManager.unreadTable[asCell.idKey] == nil ? Constants.CustomFont.customFontSemiBold : Constants.CustomFont.customFontBold
classCell.classId = asCell.idKey
classCell.selectionStyle = .none
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
store.dispatch(NavigationAction(destination: .paymentScreen, direction: .forward))
print("SelecterRow")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching {
return searchedArray.count
}
return activeStates.count
}
EDIT:
set the searching variable using the UISearchBarDelegate:
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
searching = true
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
searching = false
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchedArray = activeStates.filter({ titleClass -> Bool in
titleClass.title!.contains(searchText)
})
tableView.reloadData()
}

Having two tableview in a view Swift

I am trying to have two tableviews in a single view, i have given a unique identifier for each cell and each tableview has their own cell class. The codes are
class ReportsViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var tableView2: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
extension ReportsViewController : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! BreakingNewsTableViewCell
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 145
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Breaking News"
}
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .medium
let date = Date()
let dateString = dateFormatter.string(from: date)
return "Updated on " + dateString
}
private func tableView2(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
private func tableView2(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell2", for: indexPath) as! TopStoriesTableViewCell
return cell
}
private func tableView2(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 145
}
private func tableView2(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
private func tableView2(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return " Top Stories"
}
private func tableView2(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .medium
let date = Date()
let dateString = dateFormatter.string(from: date)
return "Updated on " + dateString
}
}
However, it keeps crashing as says my cell isnt registered? May i know what mistake i did here? I have also linked the tableview delegates to self
You need to switch all methodfs like this
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == self.tableView {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! BreakingNewsTableViewCell
return cell
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell2", for: indexPath) as! cell2
return cell
}
}

Core data query returns correct values but table cell is empty

I am fetching data from Core Data, the returned data is correct when printed in the console. But the tableview always returns empty cells. The object exists, numberOfRowsinSection is returned with the proper count as well.
Been looking for hours, I hope its not a typo. Any help is appreciated, the code is below. I tried both valueForKey and valueForKeypath with no success
Thanks!
import UIKit
import CoreData
class HistoryViewController: UITableViewController{
#IBOutlet var historyTableView: UITableView!
var activitiesHistory: [NSManagedObject] = []
override func viewDidLoad() {
super.viewDidLoad()
title = "Past Workouts"
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "historyCell")
let entity = NSEntityDescription.entity(forEntityName: "Activity", in: CoreDataStack.context)
let fetchRequest: NSFetchRequest<Activity> = Activity.fetchRequest()
fetchRequest.entity = entity
//let sortDescriptor = NSSortDescriptor(key: #keyPath(Activity.timestamp), ascending: true)
//fetchRequest.sortDescriptors = [sortDescriptor]
do {
activitiesHistory = try CoreDataStack.context.fetch(fetchRequest)
//print(activitiesHistory)
} catch let error{
//handle error
print(error)
}
}
override func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return activitiesHistory.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let workout = activitiesHistory[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "historyCell", for: indexPath)
print(workout.value(forKey: "timestamp"))
cell.textLabel?.text = workout.value(forKey: "timestamp")as? String
//cell.textLabel?.text = "test"
return cell
}
}
This is worked for me.
var activities: [Activity] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
addData()
let app = UIApplication.shared.delegate as! AppDelegate
let context = app.persistentContainer.viewContext
do {
activities = try context.fetch(Activity.fetchRequest())
}
catch {
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.activities.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = self.activities[indexPath.row].timestamp
return cell
}
Result
Thanks for all your help! I found the solution:
The problem was that the data returned from the DB as of type "Date". The cell would simply show blank space and Xcode would show a warning or error. When I used DateFormatter() to convert the type to a formatted string, the cells displayed the data. Code below:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let workout = activities[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "historyCell", for: indexPath)
let time = workout.value(forKey: "timestamp")
let formatter = DateFormatter()
formatter.dateFormat = "dd-MM"
let formatteddate = formatter.string(from: time as! Date)
cell.textLabel?.text = formatteddate
return cell
}

swift 3 - how to swipe remove table view cell?

import UIKit
class PartiesTableViewController: UITableViewController
{
let cellIdentifier = "partyCell"
var parties: [Party]?
let persistence = Persistence()
let dateFormatter = DateFormatter()
override func viewDidLoad()
{
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellIdentifier)
dateFormatter.dateStyle = DateFormatter.Style.short
dateFormatter.timeStyle = DateFormatter.Style.short
print("PartiesTableViewController.viewDidLoad() begins")
}
override func viewDidAppear(_ animated: Bool)
{
parties = persistence.fetchParties()
tableView.reloadData()
}
//UITableViewDataSource
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return parties?.count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
print("PartiesTableViewController.tableView() begins")
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
if let parties = parties
{
let party = parties[indexPath.row]
print("partyname = \(party.name) \(party.startDate)")
let strDate = dateFormatter.string(from: party.startDate)
cell.textLabel?.text = "\(party.name) - \(strDate)"
}
return cell
}
//UITableViewDelegate - delete
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath)
{
if(editingStyle == UITableViewCellEditingStyle.delete)
{
parties.remove(at: indexPath.row)
}
}
}
I am very new to iOS development and this is my first todo list project. I was trying to swipe delete table view cell data from the screen. But the way I did seems incorrect way. Can anyone help me to implement deleting cell method please? last line parties.remove(at: indexPath.row) gives the error:
"Value of type '[Party]?' has no member 'remove'
You must unwrap the parties optional. You'll also have to reload the table after modifying the parties array.
parties?.remove(at: indexPath.row)
tableView.reloadData()
For more info about optionals: https://developer.apple.com/reference/swift/optional
Change
parties.remove(at: indexPath.row)
to
parties?.remove(at: indexPath.row)

Resources