Displaying the count of comments in the feed - ios

I have an Instagram like feed with pictures shown one at a time in each cell. You can press on them and get directed to a commentTBV. Here is where I have all the comments underneath the post and they get counted correctly in this View.
var commentsArray = [FotoComment]()
is the array holding the comments
kommentarArray is the array i want to fill with the assignArray func so i can use it to display the amount of counts.
var kommentarArray = [FotoComment]()
[FotoComment] is my struc I use for my comments
What I want is that already in the feed the commentArray.count will show the correct number of comments.
func assignArray() {
let otherVC = CommentTableViewController()
kommentarArray = otherVC.commentsArray
print(kommentarArray.count)
}
This way I get access from my feed to the array of comments in the CommentTBVC.
My cell is:
cell.kommentarZähler.text = "Kommentare: \(kommentarArray.count)"
But it always shows 0 even though it already has 5 comments and it correctly displayed on the CommentTBV.
M complete code for MemesTableViewConbtroller (the feed)
import UIKit
import Firebase
import FirebaseAuth
import FirebaseDatabase
class MemesTableViewController: UITableViewController {
var kommentarArray = [FotoComment]()
var dataBaseRef : FIRDatabaseReference!
var storageRef : FIRStorageReference!
var posts = [PostMitBild]()
var segmentedControl : HMSegmentedControl!
override func viewDidLoad() {
super.viewDidLoad()
assignArray()
segmentedControl = HMSegmentedControl(sectionTitles: ["Top Heute", "Beliebteste", "Neue"])
segmentedControl.frame = CGRect(x: 10, y: 10, width: 300, height: 60)
segmentedControl.backgroundColor = UIColor.red
segmentedControl.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
segmentedControl.borderColor = UIColor.brown
segmentedControl.tintColor = UIColor.black
segmentedControl.selectionIndicatorColor = UIColor.gray
segmentedControl.addTarget(self, action: #selector(getter: MemesTableViewController.segmentedControl), for: UIControlEvents.valueChanged)
tableView.tableHeaderView = segmentedControl
segmentedAction()
}
func segmentedAction() {
if segmentedControl.selectedSegmentIndex == 0 {
let postRef = FIRDatabase.database().reference().child("MemesBilder")
postRef.observe(.value, with: { (snapshot) in
var newPost = [PostMitBild]()
for post in snapshot.children {
let Post = PostMitBild(snapshot: post as! FIRDataSnapshot)
newPost.insert(Post, at: 0)
}
self.posts = newPost
DispatchQueue.main.async {
self.tableView.reloadData()
}
}, withCancel: { (error) in
print(error.localizedDescription)
})
}
}
//------------------------------------------
override func viewWillAppear(_ animated: Bool) {
if FIRAuth.auth()?.currentUser == nil {
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Login")
self.present(vc, animated: true, completion: nil)
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! PostTableViewCell
if let seconds = posts[indexPath.row].postDate {
let timestamp = NSDate(timeIntervalSince1970: seconds)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy HH:mm"
cell.uploadDatum.text = dateFormatter.string(from: timestamp as Date)
}
cell.kommentarZähler.text = "Kommentare: \(kommentarArray.count)"
cell.usernameTextField.text = posts[indexPath.row].username
cell.postContent.text = posts[indexPath.row].content
storageRef = FIRStorage.storage().reference(forURL: posts[indexPath.row].userImageUrlString)
storageRef.data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) in
if error == nil {
DispatchQueue.main.async {
if let data = data {
cell.UserImageView.image = UIImage (data: data)
}
}
}else {
print(error?.localizedDescription)
}
})
let storageRef2 = FIRStorage.storage().reference(forURL: posts[indexPath.row].PostImageUrlString)
storageRef2.data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) in
if error == nil {
DispatchQueue.main.async {
if let data = data {
cell.postImageView.image = UIImage (data: data)
}
}
}else {
print(error?.localizedDescription)
}
})
return cell
}
//done!!!! ------------------------------------------
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
tableView.deleteRows(at: [indexPath], with: .fade)
let ref = posts[indexPath.row].ref
ref!.removeValue()
posts.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var numberOfRows = 0
switch segmentedControl.selectedSegmentIndex {
case 0 : numberOfRows = posts.count
case 1: numberOfRows = posts.count
default: break
}
return numberOfRows
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 420.00
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segmentedControl.selectedSegmentIndex == 0 {
if segue.identifier == "addComment" {
let vc = segue.destination as! CommentTableViewController
let indexPath = tableView.indexPathForSelectedRow!
vc.selectedPosts = posts[indexPath.row]
}
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if segmentedControl.selectedSegmentIndex == 0 {
performSegue(withIdentifier: "addComment", sender: self)
}
if segmentedControl.selectedSegmentIndex == 1 {
performSegue(withIdentifier: "addComment", sender: self)
}
if segmentedControl.selectedSegmentIndex == 2 {
performSegue(withIdentifier: "addComment", sender: self)
}
}
func assignArray() {
let otherVC = CommentTableViewController()
kommentarArray = otherVC.commentsArray
print(kommentarArray.count)
}
}
The code for the CommentTableViewController ( where i want to get the count of comments from the array var commentsArray = FotoComment which is already working on this TableView)
import UIKit
import Firebase
import FirebaseAuth
import FirebaseDatabase
import FirebaseStorage
class CommentTableViewController: UITableViewController {
#IBOutlet weak var komentarZähler: UILabel!
#IBOutlet weak var UserImageView: UIImageView!
#IBOutlet weak var usernameTextField: UILabel!
#IBOutlet weak var postImageView: UIImageView!
#IBOutlet weak var postContent: UITextView!
var dataBaseRef : FIRDatabaseReference!
var storageRef : FIRStorageReference!
var commentsArray = [FotoComment]()
var selectedPosts:PostMitBild!
override func viewDidLoad() {
super.viewDidLoad()
configurePost()
let commentRef = selectedPosts.ref!.child("Kommentare")
commentRef.observe(.value, with: { (snapshot) in
var newComments = [FotoComment]()
for item in snapshot.children {
let neuerKommentar = FotoComment(snapshot: item as! FIRDataSnapshot)
newComments.insert(neuerKommentar, at: 0)
}
self.commentsArray = newComments
self.tableView.reloadData()
}, withCancel: { (error) in
print(error.localizedDescription)
})
}
#IBAction func addComment(_ sender: UIBarButtonItem) {
let alertView = UIAlertController(title: "Kommentar", message: "Füge einen Kommentar hinzu", preferredStyle: UIAlertControllerStyle.alert)
alertView.addTextField { (textfield) in
textfield.placeholder = "Einen neuen Kommentar hinzufügen"
}
let sendCommentAction = UIAlertAction(title: "Kommentieren", style: .default) { (action) in
let textfield = alertView.textFields!.first!
let comment = FotoComment(content: textfield.text! , postId: self.selectedPosts.postId , username: (FIRAuth.auth()!.currentUser!.displayName!) , userImageUrlString: String(describing: FIRAuth.auth()!.currentUser!.photoURL!), postDate: (NSDate().timeIntervalSince1970))
let commentRef = self.selectedPosts.ref!.child("Kommentare").childByAutoId()
commentRef.setValue(comment.toAnyObject())
}
let cancelAction = UIAlertAction(title: "Abbrechen", style: .cancel, handler: nil)
alertView.addAction(sendCommentAction)
alertView.addAction(cancelAction)
self.present(alertView, animated: true, completion: nil)
}
// 2----------------------------------------------
func configurePost() {
usernameTextField.text = selectedPosts.username
postContent.text = selectedPosts.content
storageRef = FIRStorage.storage().reference(forURL: selectedPosts.userImageUrlString)
storageRef.data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) in
if error == nil {
DispatchQueue.main.async {
if let data = data {
self.UserImageView.image = UIImage (data: data)
}
}
}else {
print(error?.localizedDescription)
}
})
let storageRef2 = FIRStorage.storage().reference(forURL: selectedPosts.PostImageUrlString)
storageRef2.data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) in
if error == nil {
DispatchQueue.main.async {
if let data = data {
self.postImageView.image = UIImage (data: data)
}
}
}else {
print(error?.localizedDescription)
}
})
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
komentarZähler.text = "Kommentare: \(commentsArray.count)"
return commentsArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "commentCell", for: indexPath) as! CommentTableViewCell
if let seconds = commentsArray[indexPath.row].postDate {
let timestamp = NSDate(timeIntervalSince1970: seconds)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy HH:mm:ss"
cell.uploadDatum.text = dateFormatter.string(from: timestamp as Date)
}
cell.usernameTextField.text = commentsArray[indexPath.row].username
cell.postContent.text = commentsArray[indexPath.row].content
storageRef = FIRStorage.storage().reference(forURL: commentsArray[indexPath.row].userImageUrlString!)
storageRef.data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) in
if error == nil {
DispatchQueue.main.async {
if let data = data {
cell.UserImageView.image = UIImage (data: data)
}
self.tableView.reloadData()
}
}else {
print(error?.localizedDescription)
}
})
return cell
}
}
this is how i safe my data in firebase. I want to update the number of comments (kommentarAnzahl which is currently 0) every time a comment is added to the post

You have to convert your Array count to string value to display in cell.
Try this Code,
cell.kommentarZähler.text = String("Kommentare:",kommentarArray.count)
I hope this will work for You.

So you have two separate view controllers namely CommentTableViewController and MemesTableViewController consisting of commentsArray and kommentarArray resepctively. These arrays are of class-scope meaning that once you get outside of your class; for this case it would be leaving the view, then they would be deallocated. Once you enter the view, they'll be re-created again and filled with values.
Since you want to get the number of elements in commentsArray, I'd recommend you create a static variable which would keep track of this. When you make something static you're pretty much making it accessible throughout your entire app/program and changes made to it are reflected across the entire app. In other words, a memory block is reserved to store your variable which will only be de-allocated once you quit the app entirely. You may think of this as an app-scope variable.
Two ways
Lousy Way
Change your commentsArray definition from var commentsArray = [FotoComment]() to static var commentsArray = [FotoComment](). By doing this, then you may access and manipulate the contents of this array from any other class. This is great if you have few elements but what happens when you have tens of thousands or even a million comments? It'll mean that we'll be walking around with huge amounts of data everywhere even when we really don't need it.
Recommended Way
Keep your current commentsArray definition and add this static var numberOfComments: Int = 0 inside your CommentTableViewController. Right after adding your elements to commentsArray, update the element tracker as shown below
CommentTableViewController.numberOfComments = commentsArray.count
Then when you go back to your MemesTableViewController, you can simply delete assignArray(); since we have a global element counter now, and simply amend your cell to this
cell.kommentarZähler.text = String(CommentTableViewController.numberOfComments)
With this, even if you create say another class called FriendsVC, you can still access numberOfComments and even change it as well.
PS: Since numberOfComments is static, whenever and wherever you want to access or amend it, you MUST always first call the class or struct in which it was defined in. For this case, it is inside CommentTableViewController so you need to always do CommentTableViewController.numberOfComments to access it; even when you are inside CommentTableViewController itself. Always remember that.

Related

Implementing a UISearchController on top of UITableView displaying Core Data in swift 5

I have a core data Swift master/detail view application (code is located here) where the core data object, called sweetnote, is basically this:
class sweetnote: NSObject {
private(set) var noteId : UUID
private(set) var noteTitle : String
private(set) var noteText : NSAttributedString
private(set) var noteCreated : Int64
private(set) var noteModified : Int64
private(set) var noteCategory : String
}
I have a Master view controller which renders these notes in a UITableView with the help of a function in Helpers/sweetnoteCoreDataHelper.swift called readNotesFromCoreData:
static func readNotesFromCoreData(fromManagedObjectContext: NSManagedObjectContext) -> [sweetnote] {
var returnedNotes = [sweetnote]()
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Note")
fetchRequest.predicate = nil
do {
let fetchedNotesFromCoreData = try fromManagedObjectContext.fetch(fetchRequest)
fetchedNotesFromCoreData.forEach { (fetchRequestResult) in
let noteManagedObjectRead = fetchRequestResult as! NSManagedObject
returnedNotes.append(sweetnote.init(
noteId: noteManagedObjectRead.value(forKey: "noteId") as! UUID,
noteTitle: noteManagedObjectRead.value(forKey: "noteTitle") as! String,
noteText: noteManagedObjectRead.value(forKey: "noteText") as! NSAttributedString,
noteCreated: noteManagedObjectRead.value(forKey: "noteCreated") as! Int64,
noteModified: noteManagedObjectRead.value(forKey: "noteModified") as! Int64,
noteCategory: noteManagedObjectRead.value(forKey: "noteCategory")
as! String))
}
} catch let error as NSError {
// TODO error handling
print("Could not read notes from core data. \(error), \(error.userInfo)")
}
// Set note count
self.count = returnedNotes.count
// Sort by modified date
return returnedNotes.sorted() {
$0.noteModified > $1.noteModified
}
}
And then in my Master view controller (at UI/MasterViewController.swift) I have a UISearchController configured at the top of the UITableView which is currently not applying any filtering logic based on the text and I'm having trouble determining where I need to update:
import UIKit
import Foundation
import CoreData
class MasterViewController: UITableViewController, NSFetchedResultsControllerDelegate, UISearchBarDelegate, UISearchResultsUpdating {
var detailViewController: DetailViewController? = nil
var searchtemplate: String? {didSet {print (searchtemplate as Any)}}
// Search results controller
let resultSearchController = UISearchController(searchResultsController: nil)
var sweetnotes: [sweetnote] = []
var searchResults: [sweetnote] = []
override func viewDidLoad() {
super.viewDidLoad()
// Core data initialization
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
// Create alert controller
let alert = UIAlertController(
title: "Could note get app delegate",
message: "Could note get app delegate, unexpected error occurred. Try again later.",
preferredStyle: .alert)
// Add OK action
alert.addAction(UIAlertAction(title: "OK",
style: .default))
// Show alert
self.present(alert, animated: true)
return
}
// Pass the context forward from the app delegate
let managedContext = appDelegate.persistentContainer.viewContext
// Set context in the storage
sweetnoteStorage.storage.setManagedContext(managedObjectContext: managedContext)
// Set the search controller programmatically
resultSearchController.searchResultsUpdater = self
resultSearchController.hidesNavigationBarDuringPresentation = false
resultSearchController.obscuresBackgroundDuringPresentation = false
self.definesPresentationContext = true
// Scope bar
//resultSearchController.searchBar.scopeButtonTitles = ["All", "Ideas", "Information", "Lifestyle", "Lists", "Recipes", "Other"]
//searchView.addSubview(resultSearchController.searchBar)
tableView.tableHeaderView = resultSearchController.searchBar
resultSearchController.searchBar.tintColor = UIColor.darkGray
resultSearchController.searchBar.barTintColor = UIColor(red: 0.9, green: 0.9, blue: 0.9, alpha: 0.8)
resultSearchController.searchBar.placeholder = "Search sweetnotes"
// Prefer large titles
self.navigationController?.navigationBar.prefersLargeTitles = true
// Edit-note button
navigationItem.leftBarButtonItem = editButtonItem
// Add-note button
let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(insertNewObject(_:)))
navigationItem.rightBarButtonItem = addButton
if let split = splitViewController {
let controllers = split.viewControllers
detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
}
self.tableView.delegate = self
}
...
override func viewWillAppear(_ animated: Bool) {
clearsSelectionOnViewWillAppear = splitViewController!.isCollapsed
super.viewWillAppear(animated)
}
// Dismiss search bar on scroll-down
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
resultSearchController.dismiss(animated: false, completion: nil)
}
#objc
func insertNewObject(_ sender: Any) {
performSegue(withIdentifier: "showCreateNoteSegue", sender: self)
}
// MARK: - Segues
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
if let indexPath = tableView.indexPathForSelectedRow {
//let object = objects[indexPath.row]
let object = sweetnoteStorage.storage.readNote(at: indexPath.row)
let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
controller.navigationItem.leftItemsSupplementBackButton = true
//controller.detailItem = resultSearchController.isActive ? searchResults[indexPath.row] : sweetnotes[indexPath.row]
}
}
}
// MARK: - Table View
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
filterContentForSearchText(searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope])
}
func updateSearchResults(for searchController: UISearchController) {
guard let text = searchController.searchBar.text else { return }
print(text)
}
func filterContentForSearchText(_ searchText: String, scope: String = "All") {
searchtemplate = searchText
tableView.reloadData()
}
func updateSearchResultsForSearchController(searchController: UISearchController, fromManagedObjectContext: NSManagedObjectContext) {
let searchText = searchController.searchBar.text!
let predicate = NSPredicate(format: "%K CONTAINS[c] %#", argumentArray: ["noteText", searchText])
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Note")
fetchRequest.predicate = predicate
do {
let fetchedNotesFromCoreData = try fromManagedObjectContext.fetch(fetchRequest)
fetchedNotesFromCoreData.forEach { (fetchRequestResult) in
let noteManagedObjectRead = fetchRequestResult as! NSManagedObject
searchResults.append(sweetnote.init(
noteId: noteManagedObjectRead.value(forKey: "noteId") as! UUID,
noteTitle: noteManagedObjectRead.value(forKey: "noteTitle") as! String,
noteText: noteManagedObjectRead.value(forKey: "noteText") as! NSAttributedString,
noteCreated: noteManagedObjectRead.value(forKey: "noteCreated") as! Int64,
noteModified: noteManagedObjectRead.value(forKey: "noteModified") as! Int64,
noteCategory: noteManagedObjectRead.value(forKey: "noteCategory")
as! String))
}
} catch let error as NSError {
// TODO error handling
print("Could not read notes from core data. \(error), \(error.userInfo)")
}
tableView.reloadData()
}
...
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if resultSearchController.isActive && resultSearchController.searchBar.text != "" {
return searchResults.count
} else {
return sweetnoteStorage.storage.count()
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! sweetnoteUITableViewCell
//let note = fetchedResultsController.object(at: indexPath)
//configureCell(cell, withEvent: note)
if let object = sweetnoteStorage.storage.readNote(at: indexPath.row) {
cell.noteTitleLabel!.text = object.noteTitle
cell.noteTextLabel!.attributedText = object.noteText
cell.noteCategoryLabel!.text = object.noteCategory
cell.noteDateLabel!.text = sweetnoteDateHelper.convertDate(date: Date.init(minutes: object.noteModified))
}
//cell.contentView.layer.cornerRadius = 6.0
//cell.contentView.layer.borderColor = UIColor.gray.withAlphaComponent(0.5).cgColor
//cell.contentView.layer.borderWidth = 1.0
//cell.contentView.layer.masksToBounds = true
//cell.contentView.clipsToBounds = true
cell.backgroundColor = UIColor.white
cell.selectionStyle = UITableViewCell.SelectionStyle.none
return cell
}
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable
if resultSearchController.isActive {
return false
} else {
return true
}
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
presentDeletionFailsafe(indexPath: indexPath)
//objects.remove(at: indexPath.row)
//sweetnoteStorage.storage.removeNote(at: indexPath.row)
//tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
// Deletion failsafe function
func presentDeletionFailsafe(indexPath: IndexPath) {
let alert = UIAlertController(title: nil, message: "Are you sure you would like to delete this note?", preferredStyle: .alert)
// Delete the note
let yesAction = UIAlertAction(title: "Yes", style: .default) { _ in
// replace data variable with your own data array
sweetnoteStorage.storage.removeNote(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath], with: .fade)
}
alert.addAction(yesAction)
// Cancel and don't delete note
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(alert, animated: true, completion: nil)
}
func configureCell(_ cell: sweetnoteUITableViewCell, withEvent note: Note) {
cell.noteTitleLabel!.text = note.noteTitle
cell.noteTextLabel!.attributedText = note.noteText
cell.noteCategoryLabel!.text = note.noteCategory
cell.noteDateLabel!.text = sweetnoteDateHelper.convertDate(date: Date.init(minutes: note.noteModified))
}
}
The function updateSearchResultsForSearchController is where I tried to establish a predicate and execute the search result fetch request, based on some similar questions out there where people implemented search controllers on top of core data projects, but I am definitely still missing something or am in the wrong place because when text is entered to the search bar, all notes remain in the table view.
Any answers/suggestions are massively appreciated as I've spent a good deal of time on this. I am happy to provide more information and context if that helps you out. And again the code is located in my 'searchbar' branch of my project at here. Thank you very much in advance.

Modifying Realm data crashes on tableView.reloadData()

You get one thing figured out and something else comes along and that's why I love this stuff.
My workout app uses realm for data persistence and when I modify some of the data elements of the file, it crashes when I reload the tableView. Here is a layout of the tableview in WorkoutViewController.
I select an item to edit it's three properties (Name, Date and average time). When selecting Edit the following view controller is presented. This is WorkoutAlertViewController.
Here is the relevant code:
class WorkoutViewController: UIViewController {
let realm = try! Realm()
var workoutItems: Results<Workout>?
var passedWorkout = Workout()
#IBOutlet weak var tableView: UITableView!
func editWorkout(workout: Workout, name: String, time: String, date: Date) {
do {
try self.realm.write {
passedWorkout = workout
print("passedWorkout \(workout)")
print("changes to be made \(name), \(time), \(date)")
passedWorkout.name = name
passedWorkout.time = time
passedWorkout.dateCreated = date
print("workout changed \(passedWorkout)")
}
} catch {
print("Error editing workout \(error)")
}
// loadWorkout()
}
func loadWorkout() {
workoutItems = realm.objects(Workout.self).sorted(byKeyPath: "name", ascending: false)
print("workoutItems \(String(describing: workoutItems))")
tableView.reloadData()
}
}
extension WorkoutViewController: PassNewWorkout {
func didTapAdd(name: String, time: String, date: Date) {
workoutName = name
averageTime = time
creationDate = date
let newWorkout = Workout()
newWorkout.name = workoutName
newWorkout.dateCreated = creationDate
newWorkout.time = averageTime
saveWorkout(workout: newWorkout)
}
}
extension WorkoutViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return workoutItems?.count ?? 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! WorkoutCell
if let workout = workoutItems?[indexPath.row] {
cell.workoutLabel.text = workout.name
cell.dateLabel.text = String("Created: \(functions.convertTime(timeToConvert: workout.dateCreated!))")
cell.timeLabel.text = String("Avg Time: \(workout.time)")
} else {
cell.textLabel?.text = "No Workouts Added"
}
return cell
}
}
extension WorkoutViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "workoutListSegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "workoutListSegue" {
if let destinationVC = segue.destination as? WorkoutListViewController, let indexPath = tableView.indexPathForSelectedRow {
if let workout = workoutItems?[indexPath.row] {
destinationVC.selectedWorkout = workout
destinationVC.workoutName = workout.name
}
}
}
}
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(style: .destructive, title: "Delete") { (action, view, actionPerformed) in
if let _ = tableView.cellForRow(at: indexPath){
self.passedWorkout = self.workoutItems![indexPath.row]
self.deleteWorkout(workout: self.passedWorkout)
}
tableView.reloadData()
}
deleteAction.backgroundColor = .red
let editAction = UIContextualAction(style: .normal, title: "Edit") { (action, view, actionPerformed) in
if let _ = tableView.cellForRow(at: indexPath){
let alertVC = self.storyboard!.instantiateViewController(identifier: "WorkoutVC") as! WorkoutAlertViewController
self.passedWorkout = self.workoutItems![indexPath.row]
print("editAction \(self.passedWorkout)")
alertVC.didTapEdit(workout: self.passedWorkout)
self.present(alertVC, animated: true, completion: nil)
}
}
editAction.backgroundColor = .gray
return UISwipeActionsConfiguration(actions: [deleteAction, editAction])
}
}
The app crashes on loadWorkout() with:
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file /Users/kevin/Desktop/FitTrax/FitTrax/Controller/Fitness/WorkoutViewController.swift, line 88
Line 88 is tableView.reloadData()
Here is the WorkoutAlertViewController file.
protocol PassNewWorkout {
func didTapAdd(name: String, time: String, date: Date)
}
class WorkoutAlertViewController: UIViewController {
var workoutName = String()
var averageTime = String()
var creationDate = Date()
var receivedWorkout = Workout()
#IBAction func addButtonPressed(_ sender: UIButton) {
dismiss(animated: true, completion: nil)
if workoutName == "" {
workoutName = workoutTextField.text!
creationDate = datePicker.date
averageTime = timeTextField.text!
alertDelegate.didTapAdd(name: workoutName, time: averageTime, date: creationDate)
} else {
workoutName = workoutTextField.text!
creationDate = datePicker.date
averageTime = timeTextField.text!
let workoutVC = storyboard!.instantiateViewController(withIdentifier: "Main") as! WorkoutViewController
workoutVC.editWorkout(workout: receivedWorkout, name: workoutName, time: averageTime, date: creationDate)
}
}
func didTapEdit(workout: Workout) {
workoutName = workout.name
averageTime = workout.time
creationDate = workout.dateCreated!
receivedWorkout = workout
}
}
The edit does work without the reload but does not immediately show the changes. If I go back to the Fitness Menu and back to the Workout Menu, it displays the changes then. It's something with that reload that I can't figure out.
Thank you for any suggestions.

TableView stop reuse

I am trying to display users for a messaging app in a TableView however when I scroll it updates the cells and therefore pulls the data again thus lagging. I am using a prototype cell in a storyboard.
import UIKit
import FirebaseAuth
import FirebaseDatabase
class TableViewCell: UITableViewCell {
#IBOutlet weak var profilePicture: UIImageView!
#IBOutlet weak var statusImage: UIImageView!
#IBOutlet weak var nameLabel: UILabel!
override func prepareForReuse() -> Void {
profilePicture.image = nil
statusImage.backgroundColor = UIColor.systemGreen
nameLabel.text = nil
}
}
class MainController: UITableViewController {
#IBOutlet weak var tableViews: UITableView!
var users = [String]()
var decryptedUsers = [String]()
#IBAction func signOut(_ sender: Any) {
do {
let userid = Auth.auth().currentUser?.uid
let dbReference = Database.database().reference().child("Users").child(userid!).child("status")
dbReference.observeSingleEvent(of: .value, andPreviousSiblingKeyWith: { snapshot,error in
if (snapshot.value as? String == "online") {
dbReference.setValue("offline")
}
})
try Auth.auth().signOut()
self.performSegue(withIdentifier: "backToSignIn", sender: nil)
} catch {
let alert = UIAlertController(title: "Error", message: "There was an error signing out. Please ensure that you are connected to the internet.", preferredStyle: UIAlertController.Style.alert)
let okButton = UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil)
alert.addAction(okButton)
self.present(alert, animated: true, completion: nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
setOnline()
fetchUsers()
}
static var userslist = [String]()
func fetchUsers() {
let userid = (Auth.auth().currentUser?.uid)!
let dbRef = Database.database().reference().child("Chatlist").child(userid)
dbRef.observe(.value) { (snapshot, error) in
self.users.removeAll()
for yeet in snapshot.children {
let yeetsnap = yeet as! DataSnapshot
self.users.append(yeetsnap.childSnapshot(forPath: "id").value as! String)
print(self.users)
self.tableViews.reloadData()
}
}
}
func setOnline() {
let userid = Auth.auth().currentUser?.uid
let dbReference = Database.database().reference().child("Users").child(userid!).child("status")
dbReference.observeSingleEvent(of: .value, andPreviousSiblingKeyWith: { snapshot,error in
if (snapshot.value as? String == "offline") {
dbReference.setValue("online")
}
})
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: TableViewCell?
cell = nil
if (cell == nil) {
cell = (tableView.dequeueReusableCell(withIdentifier: "CellProto") as? TableViewCell)!
DispatchQueue.main.async {
cell!.profilePicture.layer.masksToBounds = true
cell!.profilePicture.layer.cornerRadius = (cell!.profilePicture.bounds.width) / 2
cell!.statusImage.layer.masksToBounds = true
cell!.statusImage.layer.cornerRadius = (cell!.statusImage.bounds.width) / 2
let dbRef = Database.database().reference().child("Users").child(self.users[indexPath.row])
dbRef.observeSingleEvent(of: .value) { (snapshot) in
cell!.nameLabel?.text = snapshot.childSnapshot(forPath: "username").value as? String
let urlstring = snapshot.childSnapshot(forPath: "imageURL").value as? String
if (urlstring != "default") {
let url = URL(string: urlstring!)
let data = try? Data(contentsOf: url!)
cell!.profilePicture.image = UIImage(data: data!)
}
let statusstring = snapshot.childSnapshot(forPath: "status").value as? String
if (statusstring == "online") {
cell!.statusImage.backgroundColor = UIColor.systemGreen
} else if (statusstring == "offline") {
cell!.statusImage.backgroundColor = UIColor.systemGray
} else if (statusstring == "appearoffline") {
cell!.statusImage.backgroundColor = UIColor.systemGray
} else if (statusstring == "dnd") {
cell!.statusImage.backgroundColor = UIColor.systemRed
}
}
}
}
print("test")
return cell!
}
}
Do not load the data every time within the cellForRow. This reloads the data every time you scroll, causing the table to lag.
A better approach would be to load the data once by putting the database retrieving code in here:
func viewWillAppear(_ animated: Bool){
super.viewWillAppear(animated)
// retrieve the data here by calling a separate function
}
Whatever you do, it is not a good coding approach to load data within the cellForRow method.
You can also retrieve the data by calling the function whenever the user swipes down from the top of the table to reload the table.
One more option would be to only load a number of rows at once (10 max for example), then when the user scroll to the bottom of the 10 rows and keeps scrolling it will then retrieve the next 10 datapoints for the next 10 rows in the table.

Deleting item from tableview from filtered data crashing

I'm trying to delete a reminder from my tableview but when its been sourced from filtering through the searchbar, the regular delete works perfect when a filter isnt taking place, but when a filter is in the search bar it crashes the app.
Heres the code;
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
var updatedArray = [Reminder]()
if editingStyle == .delete {
if filtered == false {
reminders.remove(at: indexPath.row)
tableReminders.deleteRows(at: [indexPath], with: .fade)
convertAndSaveInDDPath(array: reminders)
}
if filtered == true {
updatedArray = reminders.filter{ $0.reminderName != filterData[indexPath.row].reminderName}
print(updatedArray)
reminders = updatedArray
tableReminders.deleteRows(at: [indexPath], with: .fade)
tableReminders.reloadData()
//convertAndSaveInDDPath(array: reminders)
}
}
}
Any help would be appreciated, thank you.
EDIT (NEW CODE):
public struct Reminder {
var reminderName : String
var reminderPriority : String
var reminderDate : Date
var reminderStatus : String
var reminderSavedTime : Date
}
var reminders : [Reminder] = []
var filtered : Bool = false
public func getFilePath(fileName:String) -> String {
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = NSURL(fileURLWithPath: path)
let filePath = url.appendingPathComponent(fileName)?.path
return filePath!
}
public func convertAndSaveInDDPath (array:[Reminder]) {
let objCArray = NSMutableArray()
for obj in array {
// we have to do something like this as we can't store struct objects directly in NSMutableArray
let dict = NSDictionary(objects: [obj.reminderName ,obj.reminderPriority, obj.reminderDate, obj.reminderStatus, obj.reminderSavedTime ], forKeys: ["reminderName" as NSCopying,"reminderPriority" as NSCopying, "reminderDate" as NSCopying, "reminderStatus" as NSCopying, "reminderSavedTime" as NSCopying])
objCArray.add(dict)
}
// this line will save the array in document directory path.
objCArray.write(toFile: getFilePath(fileName: "remindersArray"), atomically: true)
}
public func getArray() -> [Reminder]? {
var remindersArray = [Reminder]()
if let _ = FileManager.default.contents(atPath: getFilePath(fileName: "remindersArray")) {
let array = NSArray(contentsOfFile: getFilePath(fileName: "remindersArray"))
for (_,userObj) in array!.enumerated() {
let reminderDict = userObj as! NSDictionary
let reminder = Reminder(reminderName: (reminderDict.value(forKey: "reminderName") as? String)!, reminderPriority: (reminderDict.value(forKey: "reminderPriority") as? String)!, reminderDate: (reminderDict.value(forKey: "reminderDate") as? Date)!, reminderStatus: (reminderDict.value(forKey: "reminderStatus") as? String)!, reminderSavedTime: (reminderDict.value(forKey: "reminderSavedTime") as? Date)!)
remindersArray.append(reminder)
}
return remindersArray
}
return nil
}
class ViewController: UIViewController, UITableViewDataSource, UITextFieldDelegate, UITableViewDelegate, UISearchBarDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Reminder Section
return filteredReminder.count
}
#IBOutlet var searchBar: UISearchBar!
var filterData = [Reminder]()
var originalReminder = [Reminder]() // original data array.
var filteredReminder = [Reminder]() // data that used to show in tableview.
var sortedAZState : Bool = false
var sortedTimeState : Bool = false
var sortedPrioState : Bool = false
#IBOutlet weak var tableReminders: UITableView!
#IBOutlet weak var timeSortBtn: UIButton!
#IBOutlet weak var sortBtn: UIButton!
#IBOutlet weak var prioritySortBtn: UIButton!
#IBAction func BtnSort(_ sender: Any) {
sortList(sender: sortBtn) // sorts by a-z through the sort function
}
#IBAction func btnSortTime(_ sender: Any) {
sortList(sender: timeSortBtn)
}
#IBAction func btnSortPriority(_ sender: Any) {
sortList(sender: prioritySortBtn)
}
func sortList(sender: UIButton) { // should probably be called sort and not filter
if sender.tag == 1 && sortedAZState == false {
reminders.sort() { $0.reminderName < $1.reminderName } // sort the reminder by name
tableReminders.reloadData(); // notify the table view the data has changed
print("sender.tag 1")
sortedAZState = true
}
else if sender.tag == 1 && sortedAZState == true {
reminders.sort() { $0.reminderName > $1.reminderName } // sort the reminder by name
tableReminders.reloadData(); // notify the table view the data has changed
print("sender.tag 1")
sortedAZState = false
}
else if sender.tag == 2 && sortedTimeState == false {
reminders.sort { $0.reminderSavedTime.compare($1.reminderSavedTime) == .orderedAscending }
tableReminders.reloadData();
print("sender.tag 2")
sortedTimeState = true
}
else if sender.tag == 2 && sortedTimeState == true {
reminders.sort { $0.reminderSavedTime.compare($1.reminderSavedTime) == .orderedDescending }
tableReminders.reloadData();
print("sender.tag 2")
sortedTimeState = false
}
else if sender.tag == 3 && sortedPrioState == false {
reminders.sort() { $0.reminderPriority.count < $1.reminderPriority.count } // sort the reminder by priority
tableReminders.reloadData(); // notify the table view the data has changed
print("sender.tag 3")
sortedPrioState = true
}
else if sender.tag == 3 && sortedPrioState == true {
reminders.sort() { $0.reminderPriority.count > $1.reminderPriority.count } // sort the reminder by priority
tableReminders.reloadData(); // notify the table view the data has changed
print("sender.tag 3")
sortedPrioState = false
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Create an object of the dynamic cell "plainCell"
let cell = tableView.dequeueReusableCell(withIdentifier: "ReminderTableViewCell", for: indexPath) as! ReminderTableViewCell
// Depending on the section, fill the textLabel with the relevant text
// Reminder Section
cell.reminderLabel.text = filteredReminder[indexPath.row].reminderName
if filteredReminder[indexPath.row].reminderPriority == "!" {
let yourImage: UIImage = UIImage(named: "lowpriority")!
cell.priorityImage.image = yourImage
}
else if filteredReminder[indexPath.row].reminderPriority == "!!" {
let yourImage: UIImage = UIImage(named: "mediumpriority")!
cell.priorityImage.image = yourImage
}
else if filteredReminder[indexPath.row].reminderPriority == "!!!" {
let yourImage: UIImage = UIImage(named: "highpriority")!
cell.priorityImage.image = yourImage
}
/* I DON'T KNOW WHAT THIS COMPLETION FOR, HOPE IT IS WORKING A/C TO YOUR NEED. */
// cell.completeButtonAction = { [unowned self] in
// let reminderCall = reminders[indexPath.row].reminderName
// let alert = UIAlertController(title: "Complete!", message: "You have completed \(reminderCall).", preferredStyle: .alert)
// let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
// alert.addAction(okAction)
//
// reminders.remove(at: indexPath.row)
// self.tableReminders.deleteRows(at: [indexPath], with: .fade)
// convertAndSaveInDDPath(array: reminders)
//
// print("reminder deleted")
//
// self.present(alert, animated: true, completion: nil)
// }
//
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
print(reminders[indexPath.row].reminderName)
let selectedReminder = reminders[indexPath.row].reminderName
let destinationVC = EditReminderViewController()
destinationVC.reminderPassed = selectedReminder
performSegue(withIdentifier: "editSegue", sender: indexPath)
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
filteredReminder.remove(at: indexPath.row)
tableReminders.deleteRows(at: [indexPath], with: .fade)
print(filteredReminder)
convertAndSaveInDDPath(array: filteredReminder) // UNCOMMENT THIS
}
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
searchBar.resignFirstResponder()
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
// var decapValue = searchBar.text?.lowercased()
// print(decapValue!)
// print(LinearSearch(searchText: decapValue!, array: reminders))
if searchBar.text != "" {
self.filteredReminder = originalReminder.filter({ reminder -> Bool in
return reminder.reminderName.lowercased().contains(searchText.lowercased())
})
}
else {
self.filteredReminder = self.originalReminder
}
tableReminders.reloadData()
// print(filterData)
}
/*
func LinearSearch(searchText: String, array: [Reminder]) -> Bool { // search function to return a true or a false bool (contains two parameneters, search value and array
for i in reminders { // cycles through each element in the array
if i.reminderName.lowercased().contains(searchText) { // if element = search (return true)
filterData.append(i)
print(filterData)
return true
}
}
return false // returns false if no element comes back to equal the searchValue
}
*/
#IBAction func btnAdd(_ sender: Any) {
performSegue(withIdentifier: "addSegue", sender: (Any).self)
}
override func viewDidLoad() {
super.viewDidLoad() // example cell
// reminders.append(Reminder(reminderName: "HOMEWORK", reminderPriority: "LOW", reminderDate: "4324", reminderStatus: "INCOMPLETE"))
tableReminders.dataSource = self
tableReminders.delegate = self
searchBar.delegate = self
tableReminders.reloadData()
// print file path of array saved
// print(getFilePath(fileName: "remindersArray"))
let reminderRetrievedArray = getArray()
reminders = reminderRetrievedArray!
originalReminder = reminders
gfilteredReminder = reminders
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I didn't know which code you were after so there is all of it. But yes you are correct, it deletes one at a time, but it is saving an empty array to be loaded for next open.
Here are the changes to make your code work perfect, clean, understandable and working. If you have any doubt then write comment and if there is any wrong in answer then update the answer.
You don not need to maintain flag filtered. I am assuming you have two array
var originalReminder = [Reminder]() // original data array.
var filteredReminder = [Reminder]() // data that used to show in tableview.
In ViewDidLoad , set right data in originalReminder and if you wanted to show all the original data in table then assign same data in filteredReminder also.
Now we will manage the tableview with 1 array i.e. filteredReminder and if searchbar text is empty the then we will assign orignal array to filtered array. So your searchbar textDidChange look like this.
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text != "" {
self.filteredReminder = originalReminder.filter({ reminder -> Bool in
return reminder.reminderName.lowercased().contains(searchText.lowercased())
})
}
else {
self.filteredReminder = self.originalReminder
}
tableReminders.reloadData()
}
You can remove redundant code from cellForRow like below
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredReminder.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Create an object of the dynamic cell "plainCell"
let cell = tableView.dequeueReusableCell(withIdentifier: "ReminderTableViewCell", for: indexPath) as! ReminderTableViewCell
// Depending on the section, fill the textLabel with the relevant text
// Reminder Section
cell.reminderLabel.text = filteredReminder[indexPath.row].reminderName
if filteredReminder[indexPath.row].reminderPriority == "!" {
let yourImage: UIImage = UIImage(named: "lowpriority")!
cell.priorityImage.image = yourImage
}
else if filteredReminder[indexPath.row].reminderPriority == "!!" {
let yourImage: UIImage = UIImage(named: "mediumpriority")!
cell.priorityImage.image = yourImage
}
else if filteredReminder[indexPath.row].reminderPriority == "!!!" {
let yourImage: UIImage = UIImage(named: "highpriority")!
cell.priorityImage.image = yourImage
}
/* I DON'T KNOW WHAT THIS COMPLETION FOR, HOPE IT IS WORKING A/C TO YOUR NEED. */
// cell.completeButtonAction = { [unowned self] in
// let reminderCall = reminders[indexPath.row].reminderName
// let alert = UIAlertController(title: "Complete!", message: "You have completed \(reminderCall).", preferredStyle: .alert)
// let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
// alert.addAction(okAction)
//
// reminders.remove(at: indexPath.row)
// self.tableReminders.deleteRows(at: [indexPath], with: .fade)
// convertAndSaveInDDPath(array: reminders)
//
// print("reminder deleted")
//
// self.present(alert, animated: true, completion: nil)
// }
//
return cell
}
To delete the cell, now need to do this only.
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
filteredReminder.remove(at: indexPath.row)
tableReminders.deleteRows(at: [indexPath], with: .fade)
// convertAndSaveInDDPath(array: filteredReminder) // UNCOMMENT THIS
}
}

How to insert the first new row in TableViewCell(swift)?

People tell me how to get out of my position and do so that when a new event is added it becomes the beginning, not the end.I will be very grateful for the help.
I have three arrays and I had to sort them out by the parent cells, and in this line I very much played func tableView (_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
import UIKit
import Parse
class PrognozSegmentViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
var soccerString = [Soccer]()
var basketString = [Basketball]()
var tennisString = [Tennis]()
var refresh : UIRefreshControl!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var segmentControl: UISegmentedControl!
#IBAction func btnSegment(_ sender: Any) {
tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
refresh = UIRefreshControl()
refresh.attributedTitle = NSAttributedString(string: "Потяните вниз чтоб обновить ресурс")
refresh.tintColor = UIColor.green
refresh.addTarget(self, action: #selector(PrognozSegmentViewController.refreshing), for: UIControlEvents.valueChanged)
tableView.addSubview(refresh)
setupSeg()
tableView.tableFooterView = UIView()
tableView.estimatedRowHeight = 88
tableView.rowHeight = UITableViewAutomaticDimension
tableView.separatorStyle = UITableViewCellSeparatorStyle.none
tableView.register(UINib(nibName:"TableViewCell",bundle:nil), forCellReuseIdentifier: "sportCell")
loadObjects1()
loadObjects2()
loadObjects3()
}
func setupSeg() {
let attributes = [ NSAttributedStringKey.foregroundColor : UIColor.yellow,
NSAttributedStringKey.font : UIFont.systemFont(ofSize: 10, weight: UIFont.Weight.bold)];
let attributesSelected = [ NSAttributedStringKey.foregroundColor : UIColor.black,
NSAttributedStringKey.font : UIFont.systemFont(ofSize: 12, weight: UIFont.Weight.bold)];
segmentControl.setTitleTextAttributes(attributes, for: UIControlState.normal)
segmentControl.setTitleTextAttributes(attributesSelected, for: UIControlState.selected)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var returnSport = 0
switch (segmentControl.selectedSegmentIndex) {
case 0 :
returnSport = soccerString.count
break
case 1 :
returnSport = basketString.count
break
case 2 :
returnSport = tennisString.count
break
default :
break
}
return returnSport
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let sportCell = tableView.dequeueReusableCell(withIdentifier: "sportCell", for: indexPath) as! TableViewCell
switch (segmentControl.selectedSegmentIndex) {
case 0 :
let soccer = soccerString[indexPath.row]
sportCell.matchLabel.text = soccer.matchS
soccerString[indexPath.row].imagePrS.getDataInBackground {(data, error) in
sportCell.imageMatch.image = error == nil ? UIImage(data: data!) : nil
}
break
case 1 :
let basket = basketString[indexPath.row]
sportCell.matchLabel.text = basket.matchB
basketString[indexPath.row].imagePrB.getDataInBackground {(data, error) in
sportCell.imageMatch.image = error == nil ? UIImage(data: data!) : nil
}
break
case 2:
sportCell.matchLabel.text = tennisString[indexPath.row].matchT
tennisString[indexPath.row].imagePrT.getDataInBackground {(data, error) in
sportCell.imageMatch.image = error == nil ? UIImage(data: data!) : nil
}
break
default:
break
}
sportCell.selectionStyle = UITableViewCellSelectionStyle.none
let date = Date()
let calendar = Calendar.current
let yesterday = calendar.date(byAdding: .day, value: -1, to: date)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE, MMM:d, yyyy"
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .short
dateFormatter.locale = Locale(identifier: "ru_UA")
dateFormatter.doesRelativeDateFormatting = true
let dataFormater2 = DateFormatter()
dataFormater2.dateFormat = "EEEE, MMM:d, yyyy"
dataFormater2.dateStyle = .short
dataFormater2.timeStyle = .short
dataFormater2.locale = Locale(identifier: "ru_UA")
dataFormater2.doesRelativeDateFormatting = true
sportCell.dataSave.text! = dateFormatter.string(from: date)
sportCell.dataSave.text = dateFormatter.string(from: yesterday!)
return sportCell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func loadObjects1() {
let query = Soccer.query() as! PFQuery<Soccer>
query.findObjectsInBackground { (objects, error) in
if error == nil {
self.soccerString.removeAll()
self.soccerString = objects!
self.tableView.reloadData()
} else {
print(error!)
}
}
}
func loadObjects2() {
let query = Basketball.query() as! PFQuery<Basketball>
query.findObjectsInBackground { (objects, error) in
if error == nil {
self.basketString.removeAll()
self.basketString = objects!
self.tableView.reloadData()
} else {
print(error!)
}
}
}
func loadObjects3() {
let query = Tennis.query() as! PFQuery<Tennis>
query.findObjectsInBackground { (objects, error) in
if error == nil {
self.tennisString.removeAll()
self.tennisString = objects!
self.tableView.reloadData()
} else {
print(error!)
}
}
}
#objc func refreshing() {
loadObjects1()
loadObjects2()
loadObjects3()
tableView.reloadData()
refresh.endRefreshing()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch (segmentControl.selectedSegmentIndex) {
case 0 :
if segue.identifier == "showSoccer" {
if let indexPath = tableView.indexPathForSelectedRow {
let destationViewController = segue.destination as! DetailSoccerTableViewController
destationViewController.detailSoccer = self.soccerString[indexPath.row]
}
}
case 1 :
if segue.identifier == "showBasket" {
let dvc = segue.destination as! DetailBasketViewController
if let indexPath = tableView.indexPathForSelectedRow {
let row = Int(indexPath.row)
dvc.baskets = basketString[row]
}
}
case 2 :
let destationVC: DetailTennisViewController = segue.destination as! DetailTennisViewController
if let indexPath = self.tableView.indexPathForSelectedRow {
let row = Int(indexPath.row)
destationVC.tenises = tennisString[row]
}
self.present(destationVC, animated: true, completion: nil)
default :
break
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch ( segmentControl.selectedSegmentIndex) {
case 0 :
let soccer = soccerString[indexPath.row]
performSegue(withIdentifier: "showSoccer", sender: soccer)
break
case 1 :
let basket = basketString[indexPath.row]
let destinationVC = DetailBasketViewController()
destinationVC.baskets = basket
self.performSegue(withIdentifier: "showBasket", sender: self)
break
case 2 :
let tennis = tennisString[indexPath.row]
let destTenVC = DetailTennisViewController()
destTenVC.tenises = tennis
self.performSegue(withIdentifier: "showTennis", sender: self)
break
default :
break
}
}
}
If your problem is the new object becomes the last
I assume you're talking about an array and you're doing
array.append(item)
If you want to have the item at the beginning do:
array.insert(item, at: 0)
And then
tableView.reloadData()

Resources