Add an Index to a searchBar - uitableview

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)
}

Related

showing an error as has need to conform the protocol

This is my code:-
Model:-
class QuestionListModel: NSObject {
var optionsModelArray:[OptionsModel] = []
var question:String!
init(dictionary :JSONDictionary) {
guard let question = dictionary["question"] as? String
else {
return
}
if let options = dictionary["options"] as? [String]{
print(options)
print(options)
for values in options{
print(values)
let optionmodel = NH_OptionsModel(values: values)
self.optionsModelArray.append(optionmodel)
}
}
self.question = question
// print(self.dataListArray33)
}
}
optionModel:-
class OptionsModel: NSObject {
var values:String?
init(values:String) {
self.values = values
print( self.values)
}
}
in viewmodel:-
var questionsModelArray:Array<NH_QuestionListModel>? = []
init(withdatasource newDatasourceModel:NH_QuestionDataSourceModel) {
datasourceModel = newDatasourceModel
print(datasourceModel.dataListArray?.count)
self.questionsModelArray = datasourceModel.dataListArray
print(self.questionsModelArray)
print(datasourceModel.dataListArray)
}
func numberOfSections() -> Int{
return (self.questionsModelArray?.count)!
}
func titleForHeaderInSection(atindexPath indexPath: IndexPath) -> QuestionListModel {
return self.questionsModelArray![indexPath.row]
}
func numberOfRowsInSection(indexPath:IndexPath) -> Int {
if let questionModel = self.questionsModelArray?[indexPath.section]{
return questionModel.optionsModelArray.count
}
else{
return 0
}
}
func datafordisplay(atindex indexPath: IndexPath) -> OptionsModel{
let questionModel = self.questionsModelArray?[indexPath.section]
return questionModel!.optionsModelArray[indexPath.row]
}
And in ViewController:-
func numberOfSections(in tableView: UITableView) -> Int {
return questionViewModel.numberOfSections()
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: IndexPath) -> UIView? {
// let headercell = Bundle.main.loadNibNamed("HeaderCell", owner: self, options: nil)?.first as! NH_questionheader
let identifier = "HeaderCell"
var headercell: NH_questionheader! = tableView.dequeueReusableCell(withIdentifier: identifier) as? NH_questionheader
if headercell == nil {
tableView.register(UINib(nibName: "NH_questionheader", bundle: nil), forCellReuseIdentifier: identifier)
headercell = tableView.dequeueReusableCell(withIdentifier: identifier) as? NH_questionheader
}
headercell.setReviewData(reviews:questionViewModel.titleForHeaderInSection(atindexPath:section))
return headercell
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 150
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: IndexPath) -> Int {
return questionViewModel.numberOfRowsInSection(indexPath: section)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "Cell"
var cell: CellTableViewCell! = tableView.dequeueReusableCell(withIdentifier: identifier) as? CellTableViewCell
if cell == nil {
tableView.register(UINib(nibName: "CellTableViewCell", bundle: nil), forCellReuseIdentifier: identifier)
cell = tableView.dequeueReusableCell(withIdentifier: identifier) as? CellTableViewCell
}
cell.contentView.backgroundColor = UIColor.clear
cell.setOptions(Options1: questionViewModel.datafordisplay(atindex: indexPath))
print("Section \(indexPath.section), Row : \(indexPath.row)")
return cell
}
my json file:-
{
"data":[
{
"question": "Gender",
"options": ["Male","Female"]
},
{
"question": "How old are you",
"options": ["Under 18","Age 18 to 24","Age 25 to 40","Age 41 to 60","Above 60"]
}, {
"question": "I am filling the Questionnaire for?",
"options": ["Myself","Mychild","Partner","Others"]
}
]
}
This is my data .So i need to display the questions in header and options in the cell for index .But showing as error as UITableview has need to conform the protocol UITableviewDataSource.
Also showing error as Index out of range.
How to do.....
I think you are not assign a datasource to your view controller. So please assign it in your ViewDidLoad of your view controller
override func viewDidLoad() {
super.viewDidLoad()
self.yourtableview.delegate = self
self.yourtableview.dataSource = self
// Do any additional setup after loading the view.
}
This error usually occurs when you fail to implement the required methods of a protocol. In this case the methods would be :
cellForRowAt
numberOfRowsInSection
Since you already have them implemented in your view controller chances are that you might have failed to set the datasource for the table view.
Refer to this
https://developer.apple.com/documentation/uikit/uitableviewdatasource
your view controller cannot find the data source and delegate for the table view. make sure you have assigned the data source and delegate
self.yourtableview.delegate = self
self.yourtableview.dataSource = self
and also make sure that your controller also inherit the UITableViewDelegate and UITableViewDataSource like this
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource
To achieve what you want, you should set your VC as the delegate and datasource of your table.
Option 1, do it dynamically:
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
}
Option 2, from your storyboard (example below):
After this, you should use the following datasource functions of UITableView:
// return number of questions
func numberOfSections(in tableView: UITableView) -> Int
// return number of options per question (indicated by section)
func tableView(UITableView, numberOfRowsInSection: Int) -> Int
You haven't correctly declared the numberOfRowsInSection function; section is an Int, not an IndexPath. As a result you have not implemented the mandatory functions of UITableViewDataSource.
You want:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return questionViewModel.numberOfRowsIn(section: section)
}
With an appropriate change in your view model:
func numberOfRowsIn(section:Int) -> Int {
return self.questionsModelArray?[section].optionsModelArray.count ?? 0
}
I would also suggest that you review your use of implicitly unwrapped optionals and force unwrapping; this is just asking for crashes.
For example, there is no reason for the question property of QuestionListModel to be String!; just declare it as String and make your initialiser failable. Better yet, use Codable to create your model from JSON and get rid of all of that code.
You can eliminate the force unwrapping in numberOfSections too:
func numberOfSections() -> Int {
return self.questionsModelArray?.count ?? 0
}
I would also suggest you make QuestionListModel a struct rather than an NSObject subclass.
If I were you I would re-factor to remove the view model, it is adding unnecessary complexity in this case, and use Codable for your JSON deserialisation:
struct Questions: Codable {
enum CodingKeys: String, CodingKey {
case questions = "data"
}
var questions: [Question]
}
struct Question: Codable {
var question: String
var options: [String]
}
Your view controller then becomes much simpler:
class ViewController: UIViewController, UITableViewDatasource {
var questionData: Questions?
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "NH_questionheader", bundle: nil), forCellReuseIdentifier: "HeaderCell")
tableView.register(UINib(nibName: "CellTableViewCell", bundle: nil), forCellReuseIdentifier: "Cell")
// You don't show how you load your JSON, but assuming you have it in an instance of `Data` called `jsonData`:
do {
self.questionData = try JSONDecoder().decode(Questions.self, from: jsonData)
} catch {
print("Error decoding JSON: \(error.localizedDescription)")
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: IndexPath) -> UIView? {
let identifier = "HeaderCell"
guard let questionData = self.questionData,
let headercell = tableView.dequeueReusableCell(withIdentifier: identifier) as? NH_questionheader else {
return nil
}
headercell.label.text = questionData.questions[section].question
return headercell
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 150
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.questionData?.questions[section].options.count ?? 0
}
func numberOfSections(in tableView: UITableView) -> Int {
return self.questionData?.questions.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "Cell"
// Note, I have used force unwrapping and a forced downcast here as if either of these lines fail you have a serious problem and crashing is the simplest way of finding it during development
let option = self.questionData!.questions[indexPath.section].options[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath ) as! CellTableViewCell
cell.contentView.backgroundColor = .clear
cell.label.text = option
return cell
}
}
Once you have this basic approach working you can try and add a view model if you like.

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
}

Can't present data in table view cell

I am trying to retrive each row from database and keep value in dictionary. After that I have created a array of dictionary. But when i am trying to show each value of dictionary in table cell, i can't . Here is my code
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
var borrowerInformation:NSMutableDictionary? = NSMutableDictionary();
var borrowerName:String = ""
var givenDate:String = ""
var borrower:NSMutableArray = []
override func viewDidLoad() {
super.viewDidLoad()
dataQuery()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return self.borrower.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! BorrowerLanderTableViewCell
cell.borrowerName.text = self.borrower[indexPath.row] as? String
return cell
}
func dataQuery(){
let queryBorrower = PFQuery(className:"BorrowerLander")
queryBorrower.whereKey("borrowerLander", equalTo: 0)
queryBorrower.whereKey("userId", equalTo: (PFUser.currentUser()?.objectId)!)
queryBorrower.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
self.borrower = NSMutableArray()
if error == nil {
if let borrowerData = objects {
for object in borrowerData {
self.borrowerName = object["name"] as! String
self.givenDate = object["givenDate"] as! String
self.borrowerInformation = [ "borrowerName": self.borrowerName,"givenDate":"self.givenDate"]
self.borrower.addObject(self.borrowerInformation!)
}
}
}
self.tableview.reloadData()
}
}
}
Here, i have just added main part of the code. Please tell me how can i show value of array of the dictionary in table view cell?
I don't know how you did the setup but an issue could be that you have not set your tableviews delegate and datasource to the controller you are working with.
self.tableView.dataSource = self
self.tableView.delegate = self
I'm also wondering if you did implement the numberOfRows and numberOfSections delegate methods.
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return borrower.count
}
and
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
Also when setting the text you need to actually use the name string you added to the dictionary
let borrower = self.borrower[indexPath.row]
if let name = borrower["borrowerName"] as? String{
cell.borrowerName.text = name
} esle {
cell.borrowerName.text = "error"
}
Also declare your data array as
var borrower:[[String: AnyObject]] = []
Extra edit:
I do want to say that I really think you should use a custom object here instead of an dictionary
class Borrower {
var name: String
var givenDate: String
//etc.
init(name: String, givenDate: String) {
self.name = name
self.givenDate = givenDate
}
}
enabling the following
var borrower:[Borrower] = []
and in the query:
for object in borrowerData {
if let name = object["name"] as? String, givenDate = object["givenDate"] as? String {
self.borrower.append(Borrower(name: name, givenDate: giverDate))
}
}
First, make sure you've set the dataSource and delegate for your table view to your view controller. Then, the function tableView:cellForRowAtIndexPath: will never get called unless you implement tableView:numberOfRowsInSection. The default implementation of that function returns 0. Try implementing this:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return borrower.count
}
Also, if the code inside of queryBorrower.findObjectsInBackgroundWithBlock { is actually in the background, you need to dispatch your table view reload to the main thread. Like this:
dispatch_async(dispatch_get_main_queue(), { [weak self] () -> Void in
self?.tableView.reloadData()
})
You need to implement numberOf... dataSource delegate first:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int

Slow/Stuttered scroll in tableview

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.

TableView error: titleForHeaderInSection/Swift

I am working on a table view project I've seen in a tutorial, then I came across this piece of code that gives me the **error: Definition conflicts with previous value.**
The piece of code is:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int, titleForHeaderInSection section:Int) -> String? {
// Return the number of rows in the section.
return animalSelectionTitles[section]
}
I have tried to change the String? into String or Int, but String gives me the same error and Int gives me an error on the return line.
Here's my complete code:
import UIKit
class AnimalTableViewController: UITableViewController {
var animalsDict = [String: [String]] ()
var animalSelectionTitles = [String] ()
let animals = ["Bear", "Black Swan", "Buffalo", "Camel", "Cockatoo", "Dog", "Donkey", "Emu", "Giraffe", "Greater Rhea", "Hippopotamus", "Horse", "Koala", "Lion", "Llama", "Manatus", "Meerkat", "Panda", "Peacock", "Pig", "Platypus", "Polar Bear", "Rhinoceros", "Seagull", "Tasmania Devil", "Whale", "Whale Shark", "Wombat"]
func createAnimalDict() {
for animal in animals {
let animalKey = animal.substringFromIndex(advance(animal.startIndex, 1))
if var animalValues = animalsDict[animalKey] {
animalValues.append(animal)
animalsDict[animalKey] = animalValues
} else {
animalsDict[animalKey] = [animal]
}
}
animalSelectionTitles = [String] (animalsDict.keys)
animalSelectionTitles.sort({ $0 < $1})
animalSelectionTitles.sort( { (s1:String, s2:String) -> Bool in
return s1 < s2
})
}
override func viewDidLoad() {
super.viewDidLoad()
createAnimalDict()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return animalSelectionTitles.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int, titleForHeaderInSection section:Int) -> String? {
// Return the number of rows in the section.
return animalSelectionTitles[section]
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
// Configure the cell...
cell.textLabel?.text = animals[indexPath.row]
// Convert the animal name to lower case and
// then replace all occurences of a space with an underscore
let imageFilename = animals[indexPath.row].lowercaseString.stringByReplacingOccurrencesOfString(" ", withString: "_", options: nil, range: nil)
cell.imageView?.image = UIImage(named: imageFilename)
return cell
}
Two things:
1)
You should change your line
let animalKey = animal.substringFromIndex(advance(animal.startIndex, 1))
Currently it substrings from the 2nd character, which means that for the input Black Swan then animalKey would be equal to lack Swan. Instead you should use the following line:
let animalKey = animal.substringToIndex(advance(animal.startIndex, 1))
2)
There is no method in the UITableViewDataSource Protocol which is called tableView:numberOfRowsInSection:titleForHeaderInSection. Instead you need to split it into the following two methods:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let title = animalSelectionTitles[section]
return animalsDict[title]!.count
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return animalSelectionTitles[section]
}
UPDATE 1:
In your tableView:cellForRowAtIndexPath, you should also update the retrieving of the animal name to reflect what is stored in the dictionary like so:
// Configure the cell...
let secTitle = animalSelectionTitles[indexPath.section]
let animalName = animalsDict[secTitle]![indexPath.row]
cell.textLabel?.text = animalName

Resources