Slow/Stuttered scroll in tableview - ios

I'm loading about 150 elements from an array of arrays of dictionaries (tasks) and I can get all of the data into my tableview but when I scroll its stupid slow. When I print out the information of one of my functions to the console, it looks like I am getting all of the data back every time I scroll. Is this something I am not loading well (i.e. asynchronously) or do I need to change my functions?
func querySections() -> [String] {
var sectionsArray = [String]()
for task in tasks {
let dueTimes = task.dueTime
sectionsArray.append(dueTimes)
}
let uniqueSectionsArray = Array(Set(sectionsArray.sort()))
// print(uniqueSectionsArray)
return uniqueSectionsArray
}
func queryDueTimes(section:Int) -> [Task] {
var sectionItems = [Task]()
for task in tasks {
let dueTimes = task.dueTime
if dueTimes == querySections()[section] {
sectionItems.append(task)
}
}
print(sectionItems)
return sectionItems
}
// MARK: - Table view data source
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return querySections()[section]
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return querySections().count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return queryDueTimes(section).count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("TaskCell", forIndexPath: indexPath) as! TaskCell
// Configure the cell...
cell.selectionStyle = .None
let times = queryDueTimes(indexPath.section)
let task = times[indexPath.row]
cell.label.text = task.title
if task.done == true {
cell.checkBox.image = UIImage(named: "checkedbox")
cell.detailLabel.text = "Completed By: \(task.completedBy)"
}
else {
cell.checkBox.image = UIImage(named: "uncheckedbox")
cell.detailLabel.text = ""
}
cell.delegate = self
return cell
}
Basically, in querySections, I'm iterating through all of the dueTimes for each task and then changing them into an array of a set so I can filter out all of the duplicates. This is giving me all of my sections. For queryDueTimes, I'm iterating through the tasks and matching them to a section.
I had a thought about calling the functions in viewDidLoad but that isn't working (it keeps giving me an empty array when I try to pass it to another empty array thats more accessible outside of the function) and I can't access section (for queryDueTimes) in viewDidLoad (as far as what I know how to do).
Update 1:
I think the mistake is on my end. I said that I tasks is an array of arrays when its just an array of Tasks (a struct with all of the properties of each task). When I load the app, I append all of the tasks from my backend to a local array ("tasks"). Should I have an array of arrays for this to work or can I amend my code somehow and get it to work?
Update 2:
I'm getting sectionTimes and tasksInSectionArray as empty arrays when I print them.
var sectionTimes = [String]()
var tasksInSectionArray = [[Task]]()
var tasks = [Task]() {
didSet {
tableView?.reloadData()
}
}
func updateTableView() {
sectionTimes = Set(tasks.map{$0.dueTime}).sort()
tasksInSectionArray = sectionTimes.map{section in tasks.filter{$0.dueTime == section}}
print(sectionTimes)
print(tasksInSectionArray)
tableView.reloadData()
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sectionTimes[section]
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sectionTimes.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tasksInSectionArray[section].count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("TaskCell", forIndexPath: indexPath) as! TaskCell
// Configure the cell...
cell.selectionStyle = .None
let task = tasksInSectionArray[indexPath.section][indexPath.row]

Like you guessed, the data is being loaded and sorted over and over again, instead of only once. Save the results of querySelections and queryDueTimes and use that inside the table view data source methods.
You can do this in viewDidLoad - call both functions once and assign the results to a variable at the class level, and then call tableView.reloadData() (assuming you have a reference to the table view).

var sections: [String] = []
var data: [[Tasks]] = []
func updateTableView() {
sections = Set(tasks.map { $0.dueTime }).sort()
data = sections.map { section in tasks.filter { $0.dueTime == section } }
tableView.reloadData()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sections.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data[section].count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let task = data[indexPath.section][indexPath.row]
// Cell configuration
}
This is basically what DMan said, but I've made an example for you.

Related

Filtering query for Realm

I have a function which prints all the objects in my realm table to a table view. I would like to be able to filter these objects by their "muscle" property.
Here's my DB helper functions:
func getMusclesCount()-> Int {
let storedExercise = realm.objects(StoredExercise.self)
return storedExercise.count
}
//MARK:- getAllMuscelsNames
func getAllMusclesNames()-> [String] {
var musclesName = [String]()
let storedExercise = realm.objects(StoredExercise.self)
for exercise in storedExercise {
print("Muscle = \(exercise.muscle)")
musclesName.append(exercise.name)
}
return musclesName
}
Here's my Table View Controller class :
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return DBHelper.shared.getAllMusclesNames().count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
}
let muscle = DBHelper.shared.getAllMusclesNames()[indexPath.row]
cell.textLabel?.text = muscle
return cell
}
I've tried adding .Filter to 'let storedExercise' but I'm not sure how to set it up correctly. Any assitance would be greatly appreciated, thanks.
If your StoredExercise model looks like this
class StoredExercise: Object {
#objc dynamic var muscle = ""
}
then to get all of the exercises that are for the biceps, it's this
let bicepResults = realm.objects(StoredExercise.self).filter("muscle == 'biceps'")

How to split text separated with "," on new UITableView custom cell in UITableView

Problem Statement:
I want to display text separated by "," on new custom UITableViewCell.
Problem: It displays all data in single custom cell only with multi-line property, as shown in below.
I want to display tableView like this way.
Now I'm trying to display above data separated by "," on each new custom cell, as shown in above screenshot, but it displays only 1st data and skip remaining data, as per my code.
let Meaning :String = "Aback,Abacus,Abandon,Able,Aboard"
let Smeaning :String = "Fabric,Habit,keen,Pace"
func tableView(tableViewData: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableViewData.dequeueReusableCellWithIdentifier("cell")! as! StudentCell
let fmeaning = Mmeaning.characters.split{$0 == ","}.map(String.init)
let smeaning = Hmeaning.characters.split{$0 == ","}.map(String.init)
for var i = 0; i < fmeaning.count; i += 1{
print(fmeaning[i])
//Here it prints all values perfectly
}
for var i = 0; i < smeaning.count; i += 1{
print(smeaning[i])
//Here it prints all values perfectly
}
Problem occurs here below two statements: display only 1st value in UITableView
cell.lblMeaning1.text = fmeaning[indexPath.row]
cell.lblMeaning2.text = smeaning[indexPath.row]
return cell;
}
How should I assign an Array to these two custom cells, so that it will display data on separate new custom cell?
For that you need to use section table with your tableView, also you need to make this calculation in viewDidLoad and then reload the tableView, For that declare your two instance of array like this in your viewController.
var languageDic = [String: [String]]()
var allLanguage = [String]()
override func viewDidLoad() {
super.viewDidLoad()
let mMeaningArray = Mmeaning.characters.split{$0 == ","}.map(String.init)
let hMeaningArray = Hmeaning.characters.split{$0 == ","}.map(String.init)
self.languageDic = ["Marathi Meaning": mMeaningArray,"Hindi Meaning": hMeaningArray] // You can add other langaue in the dic sam way I have added this two
self.allLanguage = Array(self.languageDic.keys) as [String]
self.tableView.reloadData()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return self.allLanguage.count
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String {
return self.allLanguage[indexPath.section]
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.languageDic[self.allLanguage[section]].count
}
func tableView(tableViewData: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableViewData.dequeueReusableCellWithIdentifier("cell")! as! StudentCell
let str = self.languageDic[self.allLanguage[indexPath.section]][indexPath.row]
cell.lblMeaning1.text = str
return cell
}
Note: In cellForRowAtIndexPath you need only one label.
Edit: As of you want return the count of array that have more element you can try like this. F
var mMeaningArray = [String]()
var hMeaningArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.mMeaningArray = Mmeaning.characters.split{$0 == ","}.map(String.init)
self.hMeaningArray = Hmeaning.characters.split{$0 == ","}.map(String.init)
self.tableView.reloadData()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (self.mMeaningArray.count > self.hMeaningArray.count)? self.mMeaningArray.count : self.hMeaningArray.count
}
func tableView(tableViewData: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableViewData.dequeueReusableCellWithIdentifier("cell")! as! StudentCell
if indexPath.row < self.mMeaningArray.count {
cell.lblMeaning1.text = self.mMeaningArray[indexPath.row]
}
else {
cell.lblMeaning1.text = ""
}
if indexPath.row < self.hMeaningArray.count {
cell.lblMeaning2.text = self.hMeaningArray[indexPath.row]
}
else {
cell.lblMeaning2.text = ""
}
return cell
}
the numberOfRowsInSection() function should return fmeaning.count and smeaning.count... put an if to determine which table you want to have the correct row number.
also you cand display the fmeaning as a section in your tableview, and the smeaning as cells.

Group and sort Backendless data in UITableview with Swift

I'm looking to group and sort a list of users from backendless, similar to iPhone contacts. I want to add sectionIndexTitlesForTableView(_:), titleForHeaderInSection(_:), and sectionForSectionIndexTitle(_:). I haven't found a tutorial on how to do this, and I have been stuck for weeks.
So far, I'm able to retrieve users and populate the table view. I also implemented UISearchBarDelegate.
var users: [BackendlessUser] = []
var filteredUsers : [BackendlessUser] = []
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == self.tableView {
return users.count
} else {
return self.filteredUsers.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
if tableView == self.tableView {
let user = users[indexPath.row]
cell.textLabel?.text = user.name
} else {
let filteredUser = filteredUsers[indexPath.row]
cell.textLabel?.text = filteredUser.name
}
return cell
}
You must have a dictionary of array (name 'data' for example)
data["A"] = ["Ananas", "Anaconda", "Apple"]
data["B"] = ["Banana", "Baby"]
...
data["Z"] = ["Zoro"]
begin:
let letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
var headers: [String] = []
var data : [String: [String]] = [:] // Choose your type
override func viewDidLoad(){
// Do your stuff...
headers = letters.keys.sort()
// init your data var
data = ...
tableView.reloadData()
}
for header:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return headers.count
}
func sectionHeaderTitlesForTableView(tableView: UITableView) -> [String]?{
return headers
}
func tableView: UITableView, titleForHeaderInSection section: Int) -> String?{
return headers[section];
}
cell
func tableView(tableView: UITableView, numberOfRowInSection section: Int) -> Int {
// Exemple
return data[section].count
}

Add an Index to a searchBar

Need help to correct this code. I have a coredata app with restaurant names and address, have a search Bar and it is working. What I want to add is a Index and a IndexTitle as showed in the picture below (arrows). Any help is more than welcome. Thanks in advance.
import UIKit
import CoreData
class DictionaryTableViewController: UITableViewController, NSFetchedResultsControllerDelegate, UISearchResultsUpdating
{
var searchController:UISearchController!
var searchResults:[Dictionary] = []
private var dictionaryItems:[Dictionary] = []
var fetchResultController:NSFetchedResultsController!
override func viewDidLoad() {
super.viewDidLoad()
if let managedObjectContext = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext {
let fetchRequest = NSFetchRequest(entityName: "DictionaryEntity")
do {
dictionaryItems = try managedObjectContext.executeFetchRequest(fetchRequest) as! [Dictionary]
} catch {
print("Failed to retrieve record")
print(error)
}
}
searchController = UISearchController(searchResultsController: nil)
tableView.tableHeaderView = searchController.searchBar
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
tableView.estimatedRowHeight = 30.0
tableView.rowHeight = UITableViewAutomaticDimension
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 26
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.active {
return searchResults.count
} else {
return dictionaryItems.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! DictionaryTableViewCell
// Configure the cell...
cell.wordLabel.text = dictionaryItems[indexPath.row].word
cell.definitionSmallLabel.text = dictionaryItems[indexPath.row].definition
let dictionary = (searchController.active) ? searchResults[indexPath.row]: dictionaryItems[indexPath.row]
// Configure the cell...
cell.wordLabel.text = dictionary.word
cell.definitionSmallLabel.text = dictionary.definition
return cell
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String?
{
return "dictionaryItems\(section)"
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
if searchController.active{
return false
}else{
return true
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDictionaryDetail" {
if let indexPath = tableView.indexPathForSelectedRow {
let destinationController = segue.destinationViewController as! DictionaryDetailViewController
destinationController.dictionary = (searchController.active) ? searchResults[indexPath.row] : dictionaryItems[indexPath.row]
searchController.active = false
}
}
}
func updateSearchResultsForSearchController(searchController:
UISearchController) {
if let searchText = searchController.searchBar.text {
filterContentForSearchText(searchText)
tableView.reloadData()
}
}
func filterContentForSearchText(searchText: String) {
searchResults = dictionaryItems.filter({ (dictionary:Dictionary) -> Bool in
let wordMatch = dictionary.word!.rangeOfString(searchText, options:
NSStringCompareOptions.CaseInsensitiveSearch)
return wordMatch != nil
})
}
}
What I am trying to have in my table View is the Index (Arrows in the left) and the Index Title (Arrows in the right side).
You have typos in your override function for titleForHeaderInSection
Here is the corrected one, note that v in tableView is capital: (copy paste my code)
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
}
I'd recommend using the auto-complete feature of Xcode. that way, you don't get stuck in insidious errors like this.
UPDATE:
You also need to provide both of these methods to see section header and section index titles.
override func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
return ["A", "B", "C"]
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "A title for section: \(section)"
}
Try the following code:
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 9
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 9
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
// Configure the cell...
cell.textLabel?.text = "\(indexPath.section+1)\(indexPath.row+1)"
return cell
}
override func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
return ["A", "C", "B"]
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "SECTION \(section+1)"
}
}
One way to do this is using built-in properties on the NSFetchedResultsController. The most important part of this is setting up the sectionNameKeyPath (which should be the managed object attribute holding the title).
private var frc: NSFetchedResultsController? = nil
private lazy var fetchedResultsController:NSFetchedResultsController = {
if self.frc != nil {
return self.frc!
}
//grab your managed object context
let moc = dbStore.sharedInstance.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Transaction")
fetchRequest.fetchBatchSize = 30
fetchRequest.resultType = NSFetchRequestResultType.ManagedObjectResultType
let sortDescriptorByTimeStamp = NSSortDescriptor(key: "timeStamp", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptorByTimeStamp]
//in-memory cache when cachename is nil, delegate is non-nil
//note the sectionNameKeyPath requires that I have a related category object with the categoryName attribute on my Transaction managed object
let nfrc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: moc, sectionNameKeyPath: "category.categoryName", cacheName: nil)
self.frc = nfrc
do
{
try self.frc!.performFetch()
} catch let e as NSError {
fatalError(e.localizedDescription)
}
return self.frc!
}()
Section Titles
To get the section titles you implement this method.
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let sectionTitle = self.fetchedResultsController.sections?[section].name ?? "No Category Titles"
return sectionTitle
}
Table Index
To setup the index you implement the following method.
func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
return self.fetchedResultsController.sectionIndexTitles ?? [""]
}
Note the Quick Help Documentation when you click on
sectionIndexTitles. The array of section index titles. The default
implementation returns the array created by calling
sectionIndexTitleForSectionName: on all the known sections. You should
override this method if you want to return a different array for the
section index. You only need this method if you use a section index.
Source
If you want to see this in context, to get the gist of how to use the NSFetchedResultsController in this way you can checkout my sample at this location.
Alternates (Arrays/Dictionaries)
There are a few other tutorials about how to build an index from arrays and dictionaries (but I recommend the method above as simplest way to proceed).
Alternate Tutorial #1
Alternate Tutorial #2
Alternate Implementation
Note: Code in this section is from Alternate Tutorial #1
// `UIKit` convenience class for sectioning a table
let collation = UILocalizedIndexedCollation.currentCollation()
as UILocalizedIndexedCollation
....
/* section headers
appear above each `UITableView` section */
override func tableView(tableView: UITableView,
titleForHeaderInSection section: Int)
-> String {
// do not display empty `Section`s
if !self.sections[section].users.isEmpty {
return self.collation.sectionTitles[section] as String
}
return ""
}
/* section index titles
displayed to the right of the `UITableView` */
override func sectionIndexTitlesForTableView(tableView: UITableView)
-> [AnyObject] {
return self.collation.sectionIndexTitles
}
override func tableView(tableView: UITableView,
sectionForSectionIndexTitle title: String,
atIndex index: Int)
-> Int {
return self.collation.sectionForSectionIndexTitleAtIndex(index)
}

Adding sections, separated by dates, to UITableView in Swift

I'm a complete rookie at Swift and iOS programming so you'll have to forgive the perhaps simple question.
I've created a tableView which displays the contents of an array (strings) at the press of a button.
Now, I'd like to "group" these strings in tableView sections, sorted by date.
In more detail: When the user taps the button, the string should be inserted at index 0 of the array and be displayed in a section with a header of todays date. If there's values older than today's date in the array, these should be displayed in a separate section for that date. Each section should correspond to a 24 hour day and display all the strings added during that day.
Here's some sample code of what I've achieved so far:
var testArray[String]()
var sectionsInTable[String]()
#IBOutlet weak var testTable: UITableView!
#IBAction func saveButton(sender: AnyObject) {
testArray.insert("\(strTest)", atIndex: 0)
testTable.reloaddata()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sectionsInTable.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return testArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
cell.textLabel.text = String(testArray[indexPath.row])
return cell
}
I really don't know how to manage the sections part. Hopefully someone can point me in the right direction. Thanks!
I was in need for something similar, and while Ron Fessler's solution works, when there's a lot of sections/rows, it took a very long time for table to load data, and even after that it wasn't much responsive. Main issue there I think is getSectionItems function as it will always go through all of items...
My solution:
struct TableItem {
let title: String
let creationDate: NSDate
}
var sections = Dictionary<String, Array<TableItem>>()
var sortedSections = [String]()
#IBAction func saveButton(sender: AnyObject) {
let date:String = "your date in string..."
//if we don't have section for particular date, create new one, otherwise we'll just add item to existing section
if self.sections.indexForKey(date) == nil {
self.sections[date] = [TableItem(title: name, creationDate: date)]
}
else {
self.sections[date]!.append(TableItem(title: name, creationDate: date))
}
//we are storing our sections in dictionary, so we need to sort it
self.sortedSections = self.sections.keys.array.sorted(>)
self.tableView.reloadData()
}
tableView dataSource methods:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sections.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[sortedSections[section]]!.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell")
let tableSection = sections[sortedSections[indexPath.section]]
let tableItem = tableSection![indexPath.row]
cell.titleLabel?.text = tableItem.title
return cell
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sortedSections[section]
}
I would usually do this with Core Data and NSFetchedResultsController since it has built-in methods for getting sections.
However, I'll answer the question without using Core Data. The code is a little messier but here we go...
First, you have to create an object that will store both the date and the text. The testArray will be an array of these objects, instead of a String array. For example:
class DateTextItem: NSObject {
var text: String = ""
var insertDate: NSDate = NSDate()
}
var testArray = [DateTextItem]()
Then when the saveButton is hit, we'll create and add the DateTextItem object. We'll also add the date to the sectionsInTable if it doesn't already exist.
#IBAction func saveButton(sender: AnyObject) {
let newItem = DateTextItem()
newItem.text = "Test \(testArray.count)"
// this is for development only
// increment the date after 2 records so we can test grouping by date
if testArray.count >= (testArray.count/2) {
let incrementDate = NSTimeInterval(86400*(testArray.count/2))
newItem.insertDate = NSDate(timeIntervalSinceNow:incrementDate)
}
testArray.append(newItem)
// this next bit will create a date string and check if it's in the sectionInTable
let df = NSDateFormatter()
df.dateFormat = "MM/dd/yyyy"
let dateString = df.stringFromDate(newItem.insertDate)
// create sections NSSet so we can use 'containsObject'
let sections: NSSet = NSSet(array: sectionsInTable)
// if sectionsInTable doesn't contain the dateString, then add it
if !sections.containsObject(dateString) {
sectionsInTable.append(dateString)
}
self.tableView.reloadData()
}
Next, I created a function to get the items in a section since we need it in a couple places.
func getSectionItems(section: Int) -> [DateTextItem] {
var sectionItems = [DateTextItem]()
// loop through the testArray to get the items for this sections's date
for item in testArray {
let dateTextItem = item as DateTextItem
let df = NSDateFormatter()
df.dateFormat = "MM/dd/yyyy"
let dateString = df.stringFromDate(dateTextItem.insertDate)
// if the item's date equals the section's date then add it
if dateString == sectionsInTable[section] as NSString {
sectionItems.append(dateTextItem)
}
}
return sectionItems
}
Finally, here is what the Table View Data Source methods look like
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sectionsInTable.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.getSectionItems(section).count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Configure the cell...
var cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
// get the items in this section
let sectionItems = self.getSectionItems(indexPath.section)
// get the item for the row in this section
let dateTextItem = sectionItems[indexPath.row]
cell.textLabel.text = dateTextItem.text
return cell
}
// print the date as the section header title
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sectionsInTable[section]
}
You have to make an array for each day (called dayArray[] for example) and add it to sectionInTable[] and do something like that :
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sectionsInTable.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return sectionsInTable.objectAtIndex(section).count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
cell.textLabel.text = String(sectionInTable.objectAtIndex(indexPath.section).objectAtIndex(indexPath.row))
return cell
}
Sorry if I did mistakes, I'm not familiar with swift but I think the idea can help.
I implemented generic algorithm to sort out any objects which can by identified by some day. I guess would be helpful in such cases:
protocol DayCategorizable {
var identifierDate: Date { get }
}
extension Array where Element: DayCategorizable {
var daySorted: [Date: [Element]] {
var result: [Date: [Element]] = [:]
let calendar = Calendar.current
self.forEach { item in
let i = calendar.startOfDay(for: item.identifierDate)
if result.keys.contains(i) {
result[i]?.append(item)
} else {
result[i] = [item]
}
}
return result
}
}

Resources