Swift Table View Cell Changing Data - ios

I have table view controller. My problem; The cell in my table view changes every time I enter the view controller. For example; There are 2 data (address-1 and address-2). The first row is address-1, the second row is address-2, when I re-enter the page, the cells change. How can I fix this problem. Thanks
import UIKit
import Firebase
import MapKit
class DenemeTableViewController: UITableViewController {
var ref: DatabaseReference!
let user: User = Auth.auth().currentUser!
var adresListesi = [Adresler]()
var adres: Adresler!
#IBOutlet var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.table.delegate = self
self.table.dataSource = self
Adresdefteri()
}
override func viewWillAppear(_ animated: Bool) {
Adresdefteri()
table.reloadData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return adresListesi.count
}
//prob.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! DenemeTableViewCell
let adresList = self.adresListesi[indexPath.row]
cell.textLabel?.text = adresList.adresname
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath:IndexPath) {
self.performSegue(withIdentifier: "DENEMEHARİTAGO", sender: indexPath.row)
}
func Adresdefteri() {
ref = Database.database().reference()
let adreslerim = ref
adreslerim?.child("locations").child(user.emailWithoutSpecialCharacters).child("Adresler").observe( .value) { [self] (snapshot) in
if let gelenVeributunu = snapshot.value as? [String:AnyObject]{
self.adresListesi.removeAll()
for gelenSatirVerisi in gelenVeributunu {
if let sozluk = gelenSatirVerisi.value as? NSDictionary {
let key = gelenSatirVerisi.key
let adresname = sozluk["adresname"] as? String ?? ""
let latitude = sozluk["latitude"] as? Double ?? 0.0
let longitude = sozluk["longitude"] as? Double ?? 0.0
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let adres = Adresler(adresname: adresname, latitude: latitude, longitude: longitude, adresid: key)
self.adresListesi.append(adres)
}
}
DispatchQueue.main.async {
self.table.reloadData()
}
}
}
tableview

Sort the data source array after creating it and reloading the table view, that way it stays consistent. Cheers!
self.arrayName = arrayName.sorted(by: { $0.valueToSortByInArray > $1.valueToSortByInArray })

You just have to sort the adresListesti to make sure the order is consistent.
An example would be:
adresListesi.sort { $0.adresname < $02.adresname }
Put that right before DispatchQueue.main.async and it should work.

Related

How to Sort TableViewCells by date string

As shown below I want to sort my TableViewCells by the date. For this I have the time which is also called timestampName.
Right before I reload the data, I tried to sort it, but somehow this has no effect. It also throws me a warning, that I dont use the result of the sorted by. I understand this, but I dont know how to fix that.
import UIKit
import Firebase
class popularViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet var table: UITableView!
// var models = [PhotoPost]()
var texttt = [TextPost]()
override func viewDidLoad() {
super.viewDidLoad()
gettingPosts()
table.register(popularTableViewCell.nib(), forCellReuseIdentifier: popularTableViewCell.identifier)
table.register(featuredTableViewCell.nib(), forCellReuseIdentifier: featuredTableViewCell.identifier)
table.register(textTableViewCell.nib(), forCellReuseIdentifier: textTableViewCell.identifier)
table.delegate = self
table.dataSource = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return texttt.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: textTableViewCell.identifier, for: indexPath) as! textTableViewCell
cell.configure(with: self.texttt[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 300
}
func gettingPosts(){
let db = Firestore.firestore()
let postsRef = db.collection("posts")
postsRef.addSnapshotListener { (querySnapshot, error) in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added){
let data = diff.document.data()
let Name = data["username"] as! String
let text = data["description"] as! String
let likes = data["likes"] as! Int
let typ = data["postType"] as! Int
let pfp = data["profileImage"] as! String
let uid = data["uid"] as! String
let pic = data["picture"]
let time = data["time"] as! String
if typ == 0{ // Text post
let dasDing = TextPost(numberOfComments: 0, username: Name, timestampName: time, userImageName: pfp, textName: text)
self.texttt.append(dasDing)
self.texttt.sorted(by: { $0.timestampName < $1.timestampName }) //WARNING: Result of call to 'sorted(by:)' is unused
self.table.reloadData()
}
struct TextPost {
let numberOfComments: Int
let username: String
let timestampName: String
let userImageName: String
let textName: String
}
Use sort instead of sorted. The sorted method returns a new sorted array, on the other hand, the sort method sorts the array on which it was called.
self.texttt.sort(by: { $0.timestampName < $1.timestampName })
This should also work, using sorted:
self.texttt = self.texttt.sorted(by: { $0.timestampName < $1.timestampName })

Hide Selected Cell from the Table - Swift4

I have a list with 4 objects of places that I query from my Realm database.
Optional(Results<Place> <0x7feaaea447c0> (
[0] Place {
name = Daniel Webster Highway;
country = United States;
lat = 42.72073329999999;
lon = -71.44301460000001;
},
[1] Place {
name = District Avenue;
country = United States;
lat = 42.48354969999999;
lon = -71.2102486;
},
[2] Place {
name = Gorham Street;
country = United States;
lat = 42.62137479999999;
lon = -71.30538779999999;
},
[3] Place {
name = Route de HHF;
country = Haiti;
lat = 18.6401311;
lon = -74.1203939;
}
))
I'm trying to hide the selected one.
Ex. When I click on Daniel Webster Highway, I don't want that to show on my list.
How would one go above and do that in Swift 4 ?
Code
//
// PlaceDetailVC.swift
// Memorable Places
//
//
import UIKit
import CoreLocation
import RealmSwift
class PlaceDetailVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var address: UILabel!
#IBOutlet weak var placesTable: UITableView!
var selectedPlace : Place = Place()
var selectedTrip : Trip = Trip()
var distances = [ String ]()
var places : Results<Place>?
override func viewDidLoad() {
super.viewDidLoad()
address.text = selectedPlace.name
//register xib file
placesTable.register(UINib(nibName: "PlaceDetailCell", bundle: nil), forCellReuseIdentifier: "customPlaceDetailCell")
}
override func viewDidAppear(_ animated: Bool) {
load()
if selectedPlace != nil && places != nil {
for i in 0..<places!.count {
let latitude = Double(places![i].lat)
let longitude = Double(places![i].lon)
let currentLatitude = Double(selectedPlace.lat)
let currentLongitude = Double(selectedPlace.lon)
//print(latitude,longitude,currentLatitude,currentLongitude)
let coordinate = CLLocation(latitude: latitude, longitude: longitude)
let currentCoordinate = CLLocation(latitude: currentLatitude, longitude: currentLongitude)
let distanceInMeters = coordinate.distance(from: currentCoordinate) // result is in meters
let distanceInMiles = distanceInMeters/1609.344
distances.append(String(format: "%.2f", distanceInMiles))
}
}
}
// ---------------------------------------------------------------------------------------------------------
//MARK - CRUD functions
//Read
func load() {
places = selectedTrip.places.sorted(byKeyPath: "name", ascending: true)
//print(places,"<<<")
placesTable.reloadData()
}
// ---------------------------------------------------------------------------------------------------------
//MARK - Table View Datasource
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return places?.count ?? 0
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 70
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customPlaceDetailCell", for: indexPath)
as! CustomPlaceDetailCell
if selectedPlace.name != nil {
cell.address.text = (places![indexPath.row]["name"] as! String)
cell.distance.text = distances[indexPath.row]
}
return cell
}
// ---------------------------------------------------------------------------------------------------------
//MARK - Table View Delegate
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
activePlace = indexPath.row
}
}
var distances = [ String ]()
var places : Results<Place>?
Then in tableView(_:cellForRow:)
cell.address.text = (places![indexPath.row]["name"] as! String)
cell.distance.text = distances[indexPath.row]
Just don't do that. These info need to be synchronized.
Instead, use another class/struct, or an extension which will hold the distance and the place.
var array: [PlaceModel]
struct PlaceModel {
let place: Place
let distance: Double //You can use String, but that's bad habit
//Might want to add the "image link" also?
}
In load():
array.removeAll()
let tempPlaces = selectedTrip.places.sorted(byKeyPath: "name", ascending: true)
for aPlace in tempPlaces {
let distance = //Calculate distance for aPlace
array.append(PlaceModel(place: aPlace, distance: distance)
}
Now, in tableView(_:cellForRow:):
let aPlaceModel = array[indexPath.row]
if activePlace == indexPath {
let cell = tableView.dequeue...
//Use cellWithImage for that place
return cell
} else {
let cell = tableView.dequeue...
cell.address.text = aPlaceModel.place.name
cell.distance.text = aPlaceModel.distance
return cell
}
And keep that logic wherever you want, if the heightForRow if needed (if you want for instance that all your images be at 80pt but the rest at 44pts, etc.
In tableView(_:didSelectRowAt:), add tableView.reloadData(), or better tableView.reloadRows(at: [indexPath] with: .automatic)
NB: Code not tested, might not compile, but you should get the idea.
You could pass the index of the selected row from placesVC to your PlaceDetailVC and in
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == passedIndex {
return 0
}
return 70
}
set the cell height to 0 to hide the cell.

Swift: numberOfRowsInSection and cellForRowAt not being invoked

I have been trying to display data from Firebase and display in the tableView. The data is fetched properly but is not displayed. Apart from the code mentioned below, I have already added the dataSource and Delegate for the table and the identifier for the cell.
This is the HomeTableViewController
import UIKit
import FirebaseDatabase
import Firebase
class HomeTableViewController: UITableViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
print(artists.count)
return artists.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! HomeTableViewCell
print("Here")
let artist = artists[indexPath.row]
cell.artist = artist
return cell
}
let artistRef = Database.database().reference().child("Fund")
var artists = [ArtistList]()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
artistRef.observe(.value, with: { (snapshot) in
self.artists.removeAll()
for child in snapshot.children {
let childSnapshot = child as! DataSnapshot
let artist = ArtistList(snapshot: childSnapshot)
print(artist)
self.artists.insert(artist, at: 0)
}
self.tableView.reloadData()
})
}
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.isHidden = false
self.navigationItem.hidesBackButton = true
title = "Artcall"
self.tableView.estimatedRowHeight = 290.0
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.reloadData()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
}
}
The main code for my HomeTableViewCell is
var artist: ArtistList!{
didSet
{
let dateString:String = String(format: "%#", artist.date as CVarArg)
labelDate.text = dateString
labelName.text = artist.name
labelLocation.text = artist.city
}
}
And the code for my model, ArtistList is here
class ArtistList
{
var name: String = ""
var city: String = ""
var date: Int64 = 0
let ref: DatabaseReference!
init(snapshot: DataSnapshot) {
ref = snapshot.ref
if let value = snapshot.value as? [String : Any] {
name = value["artist_name"] as! String
city = value["perf_location"] as! String
date = value["target_date"] as! Int64
}
}
}
UI updates need to occur on the main thread if they are being called within asynchronous functions, so try putting the tableview reload on the main queue like this:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
artistRef.observe(.value, with: { (snapshot) in
self.artists.removeAll()
for child in snapshot.children {
let childSnapshot = child as! DataSnapshot
let artist = ArtistList(snapshot: childSnapshot)
print(artist)
self.artists.insert(artist, at: 0)
}
//Print the artists array to verify it is definitely populated
print(self.artists)
//Reload tableview on the main queue
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
}

empty tableview cell for at index path row is not called

All the tableview functions are working except cell for row index path .
The problem maybe that foods array is empty so the number for rows is 0 so the cell for row at index path is not called
#IBOutlet weak var foooods: UITableView!
var databaseref = Database.database().reference()
var img : AnyObject?
var foods = [String?]()
override func viewDidLoad() {
super.viewDidLoad()
self.databaseref.child("basic food").observe(.childAdded, with: {( snap: DataSnapshot) in
let snapp = snap.value as! [String:AnyObject]
if let x = snapp["name"] as! String? {
self.foods.insert(x, at: 0)
//self.foods.append(x)
}
})
self.foooods.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.foods.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("difufuehf")
let cell : foodsTableViewCell = tableView.dequeueReusableCell(withIdentifier: "aupa", for:indexPath) as! foodsTableViewCell
print("fufvksdfvysdgfvjdsgfdsygfvds,jhvjsdvsdjvguydsfgdsylfgdsyfgsdlygfsiygf")
if let foo = foods[indexPath.row] {
print(foo)
cell.food.text = foo
}
return cell
}
This must be a duplicate but I can't find one.
Your issue is that you call reloadData in the wrong place which results in it being called far too soon. You need to call it inside the completion block, after you update your data model.
And you need to make sure it gets called on the main queue.
override func viewDidLoad() {
super.viewDidLoad()
self.databaseref.child("basic food").observe(.childAdded, with: {( snap: DataSnapshot) in
if let snapp = snap.value as? [String:Any], let x = snapp["name"] as? String {
self.foods.insert(x, at: 0)
//self.foods.append(x)
DispatchQueue.main.async {
self.foooods.reloadData()
}
}
})
}
Note that I also fixed the way the value is obtained. You really need to avoid force unwrapping and force casting.

The snapshot is not represented correctly

This is the code responsible for uploading text of a post for a blogging app the text of the post is retrieved correctly and saved in snapshot
struct postt {
let username : String!
let textofpost : String!
}
class TableViewController: UITableViewController {
var databaseref = FIRDatabase.database().reference()
var loggedinuser : AnyObject?
var posts = [postt]()
override func viewDidLoad() {
super.viewDidLoad()
self.loggedinuser = FIRAuth.auth()?.currentUser
self.databaseref.child("users").child(self.loggedinuser!.uid).observeSingle
Event(of: .value) {(snapshot:FIRDataSnapshot) in
let snapshot = snapshot.value as! [String:AnyObject]
let username = snapshot["name"] as? String
self.databaseref.child("posts").queryOrderedByKey().observe(.childAdded, with: {( snapshot: FIRDataSnapshot) in
let snapshot = snapshot.value as? NSDictionary
The next variable textofpost doesn't contain anything and i don't know what is the problem so when i represent the cell only the label appears which has a snapshot from the path name in the node users
let textofpost = snapshot?["text"] as? String
self.posts.insert(postt(username : username, textofpost : textofpost), at: 0)
// self.feeds.reloadData()
self.tableView.reloadData()
}
)}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let label = cell.viewWithTag(1) as! UILabel
label.text = posts[indexPath.row].username
let textview = cell.viewWithTag(2) as! UITextView
textview.text = posts[indexPath.row].textofpost
return cell
}
}

Resources