So I have a tableview controller which has headers and cells which are supposed to be present under those headers. I have already done the job of correctly creating the headers. I currently have prepared the datasource for the cells which takes its content from the a list of currentEvents that a user is currently attending. The issue isn't the pulling of the data it seems to be the completion block. It takes so long to come back that the currentEvents array never gets appended when it is supposed to. It also keeps appending events onto the original array even after it should reset upon entering the new section. I have tried many things and moved code around many places but nothing seems to be having any effect
The key function in all this is
self.fetchEventsFromServer()
specifically the part where the EventService.show takes place
import UIKit
import Firebase
class FriendsEventsView: UITableViewController{
var cellID = "cellID"
var friends = [Friend]()
var attendingEvents = [Event]()
//label that will be displayed if there are no events
var currentUserName: String?
var currentUserPic: String?
var currentEventKey: String?
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Friends Events"
view.backgroundColor = .white
// Auto resizing the height of the cell
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "close_black").withRenderingMode(.alwaysOriginal), style: .done, target: self, action: #selector(self.goBack))
tableView.register(EventDetailsCell.self, forCellReuseIdentifier: cellID)
self.tableView.tableFooterView = UIView(frame: CGRect.zero)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.global(qos: .background).async {
print("This is run on the background queue")
self.fetchEventsFromServer { (error) in
if error != nil {
print(error)
return
} else {
DispatchQueue.main.async {
self.tableView.reloadData()
print("This is run on the main queue, after the previous code in outer block")
}
}
}
}
}
#objc func goBack(){
dismiss(animated: true)
}
override func numberOfSections(in tableView: UITableView) -> Int {
// print(friends.count)
return friends.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// print(friends[section].events.count)
return friends[section].collapsed ? 0 : friends[section].events.count
}
func tableView(_ tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID) as! EventDetailsCell? ?? EventDetailsCell(style: .default, reuseIdentifier: cellID)
// print(indexPath.row)
cell.details = friends[indexPath.section].events[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "header") as? CollapsibleTableViewHeader ?? CollapsibleTableViewHeader(reuseIdentifier: "header")
// print(section)
header.arrowLabel.text = ">"
header.setCollapsed(friends[section].collapsed)
print(friends[section].collapsed)
header.section = section
// header.delegate = self
header.friendDetails = friends[section]
return header
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
func fetchEventsFromServer(_ completion: #escaping (_ error: Error?) -> Void ){
//will grab the uid of the current user
guard let myUserId = Auth.auth().currentUser?.uid else {
return
}
let ref = Database.database().reference()
//checking database for users that the current user is following
ref.child("following").child(myUserId).observeSingleEvent(of: .value, with: { (followingSnapshot) in
//handling potentail nil or error cases
guard let following = followingSnapshot.children.allObjects as? [DataSnapshot]
else {return}
//validating if proper data was pulled
let group = DispatchGroup()
for followingId in following {
group.enter()
print(followingId.key)
ref.child("users").child(followingId.key).observeSingleEvent(of: .value, with: { (userInfoSnapShot) in
guard let followingUserInfo = userInfoSnapShot.children.allObjects as? [DataSnapshot] else {
return
}
//validating if proper data was pulled for each follower
for currentUserInfo in followingUserInfo {
//will add this back when I want to event implementation
if currentUserInfo.key == "Attending" {
guard let eventKeys = currentUserInfo.children.allObjects as? [DataSnapshot] else{return}
for event in eventKeys {
print(event.key)
EventService.show(forEventKey: event.key, completion: { (event) in
guard let currentEvent = event else{
return
}
self.attendingEvents.append(currentEvent)
print(self.attendingEvents.count)
})
}
}
if currentUserInfo.key == "profilePic"{
self.currentUserPic = currentUserInfo.value as! String
//print(self.currentUserPic)
}
if currentUserInfo.key == "username"{
self.currentUserName = currentUserInfo.value as! String
//print(self.currentUserName)
var friend = Friend(friendName: self.currentUserName!, events: self.attendingEvents, imageUrl: self.currentUserPic!)
print(friend.events.count)
self.friends.append(friend)
//print(self.friends.count)
}
}
group.leave()
let result = group.wait(timeout: .now() + 0.01)
completion(nil)
}, withCancel: { (err) in
completion(err)
print("Couldn't grab info for the current list of users: \(err)")
})
}
completion(nil)
}) { (err) in
completion(err)
print("Couldn't grab people that you are currently following: \(err)")
}
}
}
If anyone sees something I don't please dont hesitate to say something
How would I alter my dispatch group implementation to accomplish this goal
EventService Function
struct EventService {
static func show(forEventKey eventKey: String, completion: #escaping (Event?) -> Void) {
// print(eventKey)
let ref = Database.database().reference().child("events").child(eventKey)
// print(eventKey)
//pull everything
ref.observeSingleEvent(of: .value, andPreviousSiblingKeyWith: { (snapshot,eventKey) in
// print(snapshot.value ?? "")
guard let event = Event(snapshot: snapshot) else {
return completion(nil)
}
completion(event)
})
}
}
Related
I have a simple tableView with saved data. I created a delete button that lets me multi-delete from realm. That part works, it is when the tableview is suppose to reload that it seems to not work. I have seen a lot of answers that say you should reload it on the main thread, or view or whatever, using dispatchQueue.main.async
using just normal tableView.reloadData() didn't reload the tableview but when I use the dispatchQueue version it does delete a value but usually the last value in the tableView.
For example my tableView has the strings Uno and Un in that descending order. If I chose to delete Uno when I press the delete button the tableview does reload leaving only one value but that value is Uno, but realm Database tells me I deleted Uno and when I go back to that view it shows Un. It just isn't reloading correctly.
I have tried to place the reloadData in the dispatch at many different locations, but it still doesn't reload correctly. I am curious what I am doing wrong.
this is the viewController with the tableview where I delete the data in the tableView:
import UIKit
import Realm
import RealmSwift
class OtherViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var otherTableView: UITableView!
var realm: Realm!
var realmedData = ""
var realmList: Results<Realmed> {
get {
return realm.objects(Realmed.self)
}
}
let deleteBtn = UIBarButtonItem()
var testingBool = false
var realmArr = [String]()
var idValue = [Int]()
var idArr = [Int]()
var spanArrValue: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
otherTableView.reloadData()
realm = try! Realm()
self.otherTableView.delegate = self
self.otherTableView.dataSource = self
self.otherTableView.reloadData()
deleteBtnInfo(btn: deleteBtn)
self.navigationItem.rightBarButtonItem = deleteBtn
}
func deleteBtnInfo(btn: UIBarButtonItem) {
btn.title = "Delete"
btn.style = .plain
btn.target = self
btn.action = #selector(didTapDeleteBtn(sender:))
testingBool = false
}
#objc func didTapDeleteBtn(sender: AnyObject) {
testingBool = !testingBool
if testingBool == true {
deleteBtn.title = "Remove"
otherTableView.allowsMultipleSelection = true
otherTableView.allowsMultipleSelectionDuringEditing = true
} else if testingBool == false {
deleteBtn.title = "Delete"
didPressRemove()
DispatchQueue.main.async {
self.otherTableView.reloadData()
}
otherTableView.allowsMultipleSelection = false
otherTableView.allowsMultipleSelectionDuringEditing = false
}
}
func didPressRemove() {
if idValue.count == 0 {
print("Please Select what to Delete")
} else {
deleteRealm(idInt: idValue)
}
}
func deleteRealm(idInt: [Int]) {
do {
try realm.write {
for deleteIndex in idInt {
let deleteValue = realm.objects(RealmTwo.self).filter("id == %#", deleteIndex as Any)
print(deleteIndex)
realm.delete(deleteValue)
}
}
} catch {
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var counted = realm.objects(RealmTwo.self).filter("realmLbl == %#", realmedData)
return counted.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "otherCell", for: indexPath) as! OtherTableViewCell
var celledItem = realm.objects(Realmed.self)
for item in celledItem {
for items in item.realmTwo {
self.idArr.append(items.id)
self.realmArr.append(items.spanish)
}
}
cell.otherLbl.text = "\(realmArr[indexPath.row])"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if testingBool == false {
print(realmArr[indexPath.row])
} else {
self.idValue.append(idArr[indexPath.row])
print(spanArrValue)
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if testingBool == true {
if let index = idValue.index(of: idArr[indexPath.row]) {
idValue.remove(at: index)
print(spanArrValue)
}
}
}
}
this is the realm class for the data that I am trying to delete.
import Foundation
import UIKit
import Realm
import RealmSwift
class RealmTwo: Object {
#objc dynamic var id = Int()
#objc dynamic var realmLbl = String()
#objc dynamic var spanish = String()
#objc dynamic var french = String()
let realmed = LinkingObjects(fromType: Realmed.self, property: "realmTwo")
convenience init(id: Int, realmLbl: String, spanish: String, french: String) {
self.init()
self.id = id
self.realmLbl = realmLbl
self.spanish = spanish
self.french = french
}
}
As I said above, I placed reloadData() in different places and these are where I placed them, just in case you want to know:
func didPressRemove() {
if idValue.count == 0 {
print("Please Select what to Delete")
} else {
deleteRealm(idInt: idValue)
DispatchQueue.main.async {
self.otherTableView.reloadData()
}
}
}
func deleteRealm(idInt: [Int]) {
do {
try realm.write {
for deleteIndex in idInt {
let deleteValue = realm.objects(RealmTwo.self).filter("id == %#", deleteIndex as Any)
print(deleteIndex)
realm.delete(deleteValue)
DispatchQueue.main.async {
self.otherTableView.reloadData()
}
}
}
} catch {
}
}
I am just not sure where the reloadData is suppose to go, or if that is the real problem. Thank you for the help, and ask if there is anything else I can do.
There are a couple of issues but the main issue is that you're deleting the object from realm but that object is still hanging around in your dataSource tableView array, realmArr.
There are a whole bunch of solutions but the simplest is to add an observer to the realm results and when an item is added, changed or removed, have that update your dataSource array and then reload the tableview. One option also here is to use those results as the dataSource instead of a separate array. Realm Results objects behave very similar to an array and are great a a dataSource.
Conceptually the realm code is similar to
notificationToken = results.observe { [weak self] (changes: RealmCollectionChange) in
guard let tableView = self?.tableView else { return }
switch changes {
case .initial:
tableView.reloadData() //this is when the realm data is intially loaded.
case .update(_, let deletions, let insertions, let modifications):
//handle add, edit and modify per event.
// with an add, add the provided object to your dataSource
// same thing for remove and modify
case .error(let error):
// An error occurred while opening the Realm file on the background worker thread
fatalError("\(error)")
}
//reload the tableView now the dataSource has been updated
}
There are several options of handling those events and they are all covered in the Realm documentation. See Realm Notifications for further details about setting up the notifications.
A second option is to manually keep things in sync; e.g. when deleting the item from Realm, also delete the item from your dataSource array
This is how I managed to solve this problem.
import UIKit
import Realm
import RealmSwift
class OtherViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var notificationToken: NotificationToken? = nil
#IBOutlet weak var otherTableView: UITableView!
var realm: Realm!
var realmedData = ""
var realmList: Results<RealmTwo> {
get {
return realm.objects(RealmTwo.self).filter("%# == realmLbl", realmedData)
}
}
var realmingList: Results<RealmTwo> {
get {
return realm.objects(RealmTwo.self)
}
}
let deleteBtn = UIBarButtonItem()
var testingBool = false
var realmArr = [String]()
var idValue = [Int]()
var idArr = [Int]()
var spanArrValue: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
otherTableView.allowsMultipleSelectionDuringEditing = true
realm = try! Realm()
notificationToken = realmList.observe { [weak self] (changes: RealmCollectionChange) in
guard let tableView = self?.otherTableView else {return}
switch changes {
case .initial:
tableView.reloadData()
case .update(_, let deletions, let insertions, let modifications):
tableView.beginUpdates()
tableView.insertRows(at: insertions.map({ IndexPath(row: $0, section: 0) }),
with: .automatic)
tableView.deleteRows(at: deletions.map({ IndexPath(row: $0, section: 0)}),
with: .automatic)
tableView.reloadRows(at: modifications.map({ IndexPath(row: $0, section: 0) }),
with: .automatic)
tableView.endUpdates()
case .error(let error):
fatalError("\(error)")
}
}
self.otherTableView.delegate = self
self.otherTableView.dataSource = self
self.otherTableView.reloadData()
deleteBtnInfo(btn: deleteBtn)
self.navigationItem.rightBarButtonItem = deleteBtn
}
func deleteBtnInfo(btn: UIBarButtonItem) {
btn.title = "Delete"
btn.style = .plain
btn.target = self
btn.action = #selector(didTapDeleteBtn(sender:))
testingBool = false
}
#objc func didTapDeleteBtn(sender: AnyObject) {
testingBool = !testingBool
if testingBool == true {
deleteBtn.title = "Remove"
} else if testingBool == false {
deleteBtn.title = "Delete"
}
}
func didPressRemove() {
if testingBool == false {
print("Select what to Delete")
} else {
deleteRealm(idInt: idValue)
otherTableView.isEditing = false
}
}
#IBAction func pressEdit(_ sender: Any) {
testingBool = !testingBool
if testingBool == true {
otherTableView.isEditing = true
} else if testingBool == false {
otherTableView.isEditing = false
}
}
#IBAction func pressDelete(_ sender: Any) {
deleteRealm(idInt: idValue)
}
func deleteRealm(idInt: [Int]) {
do {
try realm.write {
for deleteIndex in idInt {
let deletingValue = realmList.filter("id == %#", deleteIndex as Any)
print("DeleteValue: \(deletingValue)")
realm.delete(deletingValue)
}
}
} catch {
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return realmList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "otherCell", for: indexPath) as! OtherTableViewCell
cell.otherLbl.text = realmList.filter("%# == realmLbl", realmedData)[indexPath.row].spanish
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if otherTableView.isEditing == false {
} else {
let idArr = realmList.filter("%# == realmLbl", realmedData)[indexPath.row].id
self.idValue.append(idArr)
print("ID: \(idValue)")
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if otherTableView.isEditing == true {
let idArr = realmList.filter("%# == realmLbl", realmedData)[indexPath.row].id
if let index = idValue.index(of: idArr) {
idValue.remove(at: index)
print("ID: \(idValue)")
}
}
}
deinit {
notificationToken?.invalidate()
}
}
Thank you
people, I have this issue when I try back image from different cell
(Thread 1: Fatal error: Index out of range)
what I'm doing here ?
I'm trying to build an Instagram clone and in my home view controller that what should posts show up. I make navigation with a table view and that table view has 2 cell with the different identifier. cell number 1 it's a header that brings data from users table to my username label and profile image. and cell number 2 its for posts its should bring post data like image and caption. I use firebase database.
my code :
import UIKit
import FirebaseAuth
import FirebaseDatabase
class HomeViewController: UIViewController ,UITableViewDelegate {
#IBOutlet weak var tableview: UITableView!
var posts = [Post]()
var users = [UserD]()
override func viewDidLoad() {
super.viewDidLoad()
tableview.dataSource = self
loadposts()
userDetal()
// var post = Post(captiontxt: "test", photoUrlString: "urll")
// print(post.caption)
// print(post.photoUrl)
}
func loadposts() {
Database.database().reference().child("posts").observe(.childAdded){ (snapshot: DataSnapshot)in
print(Thread.isMainThread)
if let dict = snapshot.value as? [String: Any]{
let captiontxt = dict["caption"] as! String
let photoUrlString = dict["photoUrl"] as! String
let post = Post(captiontxt: captiontxt, photoUrlString: photoUrlString)
self.posts.append(post)
print(self.posts)
self.tableview.reloadData()
}
}
}
func userDetal() {
Database.database().reference().child("users").observe(.childAdded){ (snapshot: DataSnapshot)in
print(Thread.isMainThread)
if let dict = snapshot.value as? [String: Any]{
let usernametxt = dict["username"] as! String
let profileImageUrlString = dict["profileImageUrl"] as! String
let user = UserD(usernametxt: usernametxt, profileImageUrlString: profileImageUrlString)
self.users.append(user)
print(self.users)
self.tableview.reloadData()
}
}
}
#IBAction func logout(_ sender: Any) {
do {
try Auth.auth().signOut()
}catch let logoutErrorr{
print(logoutErrorr)
}
let storyboard = UIStoryboard(name: "Start", bundle: nil)
let signinVC = storyboard.instantiateViewController(withIdentifier: "SigninViewController")
self.present(signinVC, animated: true, completion: nil)
}
}
extension HomeViewController: UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return users.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0{
let cell = tableview.dequeueReusableCell(withIdentifier: "imagecell", for: indexPath) as! PostCellTableViewCell
cell.postimage.image = nil
cell.tag += 1
let tag = cell.tag
cell.captionLabel.text = posts[indexPath.row].caption
let photoUrl = posts[indexPath.row].photoUrl
getImage(url: photoUrl) { photo in
if photo != nil {
if cell.tag == tag {
DispatchQueue.main.async {
cell.postimage.image = photo
}
}
}
}
return cell
} else if indexPath.row == 1 {
let cell = tableview.dequeueReusableCell(withIdentifier: "postcell", for: indexPath) as! HeaderTableViewCell
cell.userimage.image = nil
cell.tag += 1
let tag = cell.tag
cell.usernamelabel.text = users[indexPath.row].username
//Error showing here????????????????????????????????????
let profileImageUrl = users[indexPath.row].profileImageUrl
getImage(url: profileImageUrl) { photo in
if photo != nil {
if cell.tag == tag {
DispatchQueue.main.async {
cell.userimage.image = photo
}
}
}
}
return cell
}
return UITableViewCell()
}
func getImage(url: String, completion: #escaping (UIImage?) -> ()) {
URLSession.shared.dataTask(with: URL(string: url)!) { data, response, error in
if error == nil {
completion(UIImage(data: data!))
} else {
completion(nil)
}
}.resume()
}
}
try this one.
cell.tag = indexpath.row
What is the content of users array ?
Are you sure you want to define as many sections as users or as many rows ?
In this case use
func numberOfRows(in tableView: NSTableView) -> Int {
return users.count
}
As explained, you need to rewrite completely cellForRowAt
It should look like this :
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
if row < users.count {
let user = users[row]
if let cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "CellID"), owner: self) {
(cellView as! NSTableCellView).textField?.stringValue = user.name
// do the same for all the fields you need to set
return cellView
} else {
return nil
}
}
return nil
}
thanx, my friend, I found a good way to contain my cell. for post cell, i just use cellForRowAt and but the post data. for header cell i use viewForHeaderInSection
and but my user data with heightForHeaderInSection. to make the high for a view
I am stuck on this minor issue, I have a tableviewcontroller which is also searchresultcontroller. I am getting correct data against each api call but tableview is not reloading. I have no clue why its not working. Any help or lead will be very much appreciated.
class MasterViewController: UITableViewController,UISearchResultsUpdating {
var request:DataRequest?
var peopleArr:[Peoples] = []
// MARK: - View Setup
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Search"
definesPresentationContext = true
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50.0
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 1 {
// if searchController.searchBar.selectedScopeButtonIndex == 0 {
let profileVc = self.storyboard?.instantiateViewController(withIdentifier: "profileVc") as! ProfileController
profileVc.profileData = (peopleArr[indexPath.row].user_id, peopleArr[indexPath.row].user_id)
self.navigationController?.pushViewController(profileVc, animated: true)
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return peopleArr.count
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return tableView.dequeueReusableCell(withIdentifier: "headerPeopleSec")
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "People"
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FollowingsCell", for: indexPath) as! FollowingsCell
cell.textLabel?.text = "\(indexPath.row)"
let people: Peoples
people = peopleArr[indexPath.row]
if people.following == "1" {
cell.followBtn.isHidden = true
}
else{
cell.followBtn.isHidden = false
}
cell.profile_thumb!.showImageWithURL(urlString: people.photo_url)
cell.addAction = { cell in
self.addFriendAction(indexPath: indexPath , user:people)
}
cell.profile_thumb.motionIdentifier = people.user_id
cell.username.text = people.user_name
return cell
}
func getPeopleList(searchString:String?) {
if let req = self.request {
req.cancel()
}
let peopleBag = [
"auth_token": (MemberProfile.loggedUser?._auth_token())!,
"per_page": 30,
"page": 1,
"search_key": searchString ?? ""
] as [String : Any]
NVActivityIndicatorPresenter.sharedInstance.startAnimating(activityData)
self.request = HelperClass().doGetRequestCustom(url: BASE_URL + SEARCH_PEOPLE, param:peopleBag, header: [:], completion: {(response,responseObject, error) in
if let resMsg = (responseObject?.message.resp_status) {
NVActivityIndicatorPresenter.sharedInstance.stopAnimating()
// if let hasNext = responseObject?.message.paging_data.next_page_exist as? Bool {
// self.hasNextPage = hasNext
// }
let dictionary:[String: AnyObject]? = responseObject?.message.data as? [String:AnyObject] //["member_followings"]
if let dict:Array = dictionary?["member_profiles"] as? Array<[String:AnyObject]>{
for dic in dict {
let friend = Peoples()
friend.photo_url = (dic["photo"] as? String) ?? ""
friend.user_name = ((dic["user"]?["username"])! as String)
friend.user_id = (dic["id"])! as! String
friend.following = (dic["is_following"])! as! String
self.peopleArr.append(friend)
}
self.tableView.reloadData()
}
else{
}
}
else{
NVActivityIndicatorPresenter.sharedInstance.stopAnimating()
}
NVActivityIndicatorPresenter.sharedInstance.stopAnimating()
})
}
func addFriendAction(indexPath:IndexPath , user:Peoples) {
let followBag = [
"auth_token": (MemberProfile.loggedUser?.auth_token)!,
"following_profile_id": user.user_id
] as [String : Any]
NVActivityIndicatorPresenter.sharedInstance.startAnimating(activityData)
HelperClass().doPostRequest(url: BASE_URL+FOLLOW_MEMBER , param: followBag, completion: { (dataResponse,response,error) in
if (response != nil) && (response?.message.resp_status)!
{
NVActivityIndicatorPresenter.sharedInstance.stopAnimating()
let cell = self.tableView.cellForRow(at: indexPath) as! FollowingsCell
cell.followBtn.isHidden = true
user.following = "1"
}
else
{
if (response != nil){
NVActivityIndicatorPresenter.sharedInstance.stopAnimating()
HelperClass.showAlertViewWithTitle(title: "Error", Text: (response?.message.message)!, controllerToShowOn: self)
}
else{
NVActivityIndicatorPresenter.sharedInstance.stopAnimating()
HelperClass.showAlertViewWithTitle(title: "Error", Text: "Something went wrong. Please check your internet connection & try again later.", controllerToShowOn: self)
}
return
}
})
}
func updateSearchResults(for searchController: UISearchController) {
if !(searchController.searchBar.text! == "") {
self.peopleArr.removeAll()
self.tableView.reloadData()
let searchBar = searchController.searchBar
self.getPeopleList(searchString: searchBar.text!)
}
}
}
You need to make your reload call on the main thread:
...
for dic in dict {
let friend = Peoples()
friend.photo_url = (dic["photo"] as? String) ?? ""
friend.user_name = ((dic["user"]?["username"])! as String)
friend.user_id = (dic["id"])! as! String
friend.following = (dic["is_following"])! as! String
self.peopleArr.append(friend)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
...
All UI modification always has to take place on the main thread. Most of the time you're in a completion handler you'll have to dispatch to main to modify the UI.
Seems something wrong in func updateSearchResults(for searchController: UISearchController).
Can you try moving self.tableView.reloadData() at the end of this function ?
It seems when reloadData is called, the array as cleared, and not yet populated with new values.
I have data coming from database in my UITableView, when I try to pull down to make refresh all data in my tableview repeat itself and when I am trying to put a new data in database and making refresh nothing happens.
This is my code
class MyTable: UITableViewController {
var refresh = UIRefreshControl()
#IBAction func SharaOn(_ sender: UIButton) {
}
var mydata = [TestTable]()
let backendless = Backendless()
override func viewDidLoad() {
super.viewDidLoad()
refresh = UIRefreshControl()
refresh.addTarget(self, action: #selector (MyTable.pullDown), for: .valueChanged)
tableView.addSubview(refresh)
}
override func viewDidAppear(_ animated: Bool) {
loaddatawithquery()
}
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 mydata.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "mycell") as? MyTableCell{
let ImgURL = URL(string : self.mydata[(indexPath as NSIndexPath).row].ImgURL!)
let Desc = self.mydata[(indexPath as NSIndexPath).row].Desc!
cell.MyText.text = Desc
cell.mainImage.sd_setImage(with: ImgURL)
return cell
}else{
let cell = MyTableCell()
let ImgURL = URL(string : self.mydata [(indexPath as NSIndexPath).row].ImgURL)
let Desc = self.mydata[(indexPath as NSIndexPath).row].Desc
cell.mainImage.sd_setImage(with: ImgURL)
cell.MyText.text = Desc!
return cell
}
}
func updateLikes() {
let LikeBu = TestTable()
let updaterecord = Backendless.sharedInstance().data.of(TestTable.ofClass())
LikeBu.LikeNo = 1
updaterecord?.save(LikeBu, response: { (result) in
print("udateed successfully \(result)")
}, error: { (Fault) in
print("EROrrrrrrrrrr")
})
}
func loaddatawithquery(){
_ = "ImgURL"
_ = "Desc"
let dataQuery = BackendlessDataQuery()
dataQuery.queryOptions.pageSize=50
// dataQuery.whereClause = whereClause
backendless.data.of(TestTable.ofClass()).find(dataQuery,response: {(result: BackendlessCollection?) -> Void in
let data = result?.getCurrentPage()
for obj in data! as! [TestTable] {
self.mydata.append(obj)
//print("&&&&&&&hhhjjjhhvkhkh \(obj.Desc!) &&&&&&&&&&&&&&&&&&")
self.tableView.reloadData()
}
},
error: { (fault: Fault?) -> Void in
let alert = UIAlertController(title: "info", message:"يرجى الاتصال بالانترنيت", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in
})
self.present(alert, animated: true){}
})
}
#IBAction func likebutton(_ sender: AnyObject) {
// updateLikes()
}
func pullDown() {
for data in mydata {
self.mydata.append(data)
}
self.refresh.endRefreshing()
}
}
As far as i can understand, its because of this code here
for data in mydata {
self.mydata.append(data)
}
Here actually you are iterating the data inside the tableview, and also adding the data already there, and also fetching new data, hence in the actual array self.mydata, there are 2 sets of similar objects.
I think a better approach would be if you can just append the new data after the query or you can empty the array and then load the whole data from your database.
Let me know.
You need to update your code like this:-
func pullDown() {
var temp = [TestTable]()
for data in mydata {
temp.append(data)
}
mydata.removeAll()
mydata = temp
self.tableView.reloadData()
self.refresh.endRefreshing()
}
First I get these values into my Firebase Database, which works successfully.
func handleSale() {
let ref = FIRDatabase.database().reference().child("tickets")
let childRef = ref.childByAutoId()
guard let Price = emailTextField.text, ticketName = passwordTextField.text else {
print("Form is not valid")
return
}
let values: [String: AnyObject] = ["Price": Price, "ticketName": ticketName]
ref.observeEventType(.ChildAdded, withBlock: { (snapshot) in
let ticketId = snapshot.key
let ticksRef = FIRDatabase.database().reference().child("tickets").child(ticketId)
ticksRef.observeSingleEventOfType(.Value, withBlock: { (snapshot) in
guard let dictionary = snapshot.value as? [String: AnyObject] else {
return
}
self.messages.append(Ticket(dictionary: dictionary))
childRef.updateChildValues(values) { (error, ref) in
if error != nil {
print(error)
return
}
print(snapshot)
print(dictionary)
print(ticketId)
}
}, withCancelBlock: nil)
})
}
I then tried to setup a tableview with a custom cell class and fetch these values into the tableview, however being fairly new to swift, I know I haven't done this correctly. In the end I would like that my values "price" and "ticketName" shows up in my table's..
here is my tableview:
import UIKit
import Firebase
class Sales: UITableViewController {
let cellId = "cellId"
var messages = [Ticket]()
var messagesDictionary = [String: Ticket]()
override func viewDidLoad() {
super.viewDidLoad()
var messages = [Ticket]()
var messagesDictionary = [String: Ticket]()
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Logout", style: .Plain, target: self, action: #selector(heya))
tableView.registerClass(FredericCell.self, forCellReuseIdentifier: cellId)
func fetchTicket() {
FIRDatabase.database().reference().child("tickets").observeEventType(.ChildAdded, withBlock: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let ticketId = snapshot.key
Ticket.setValuesForKeysWithDictionary(dictionary)
return
}
}, withCancelBlock: nil)
}
print("test to see if it works")
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellId, forIndexPath: indexPath) as! FredericCell
return cell
}
func heya() {
print ("working")
}
I have edited in hope of filling out my cells, but without luck..:
override func tableView(tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return tickets
.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellId, forIndexPath: indexPath) as! FredericCell
let ticket = tickets[indexPath.row]
cell.textLabel?.text = ticket.ticketName
cell.detailTextLabel?.text = ticket.Price
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
return cell
}