UITableView sectionForSectionIndexTitle never called in swift 3 - ios

As I am new in Swift I got stuck in a situation. I am working on a app in which I want show side index section like in a contact app. I have used this code in my app
func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int{
print(sections.index(of:title)!)
return sections.index(of:title)!
}
But this method never get called, And the TableView is not showing the side index section. I am using Swift 3. Any help will be appreciated.
I have a tableview in which country list is there now i want to focus particular section with selection of section index (A..Z). I have implemented the above code but section index is not showing.

After a long research got the solution i was doing a silly mistake. To focus on selected section we need to scroll the tableView at selected section with this code.
You can see complete code here :https://github.com/ipraba/EPContactsPicker/blob/master/Pods/EPContactsPicker.swift
override open func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
// if resultSearchController.isActive { return 0 }
tableView.scrollToRow(at: IndexPath(row: 0, section: index), at: UITableViewScrollPosition.top , animated: false)
return sections.index(of: title)!
}

Related

Why headerView is always nil in numberOfRowsInSection?

I have to make a dynamic datasource for a table, that satisfies the criteria:
we do not know exact number of sections
we do not know exact title for header in section
we do not now how many rows it will be in each section each time
To detect number of rows per section I have the following code:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let header = tableView.headerView(forSection: section)
return self.servicesData?.filter({ $0.clubName == header?.textLabel?.text }).count ?? 0
}
where servicesData is the object array that is to be filtered by the header title aka clubName. Titles are gathered from the object array.
For some reason I always get nil when try to access the header. So far I tried calling tableView(_:titleForHeaderInSection:) directly in numberOfRowsInSection but obviously with no luck.
Feedback much appreciated.
It is nil because it is not created at that moment. You should not use tableView.headerView(forSection: section) in numberOfRows. You should use the model from where you get the title to use it to filter in the array.
You should fetch all of your data before populating the table view and first. Then you should implement:
func numberOfSections(in tableView: UITableView) -> Int {
return headerModels.count
}
where headerModels it's an array of objects with your title and it's items like:
class HeaderModel {
let title: String
let items: [YourItem]
}
and then:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return headerModels[section].items.count
}
So, as per #shurtugal 's solution it is possible to pass-in an array of objects to populate the table. I thought that creating a utility class for this very case may be too much. So here's the slightly altered way:
Consider a dictionary var servicesData = [String: [CustomObject]]() that is populated each time the ViewController shows up. The dictionary can have indefinite number of keys and indefinite number of values inside each key. Algorithm of populating the dictionary is taken from this answer.
Thereby, UITableViewDatasource methods can be defined like this:
func numberOfSections(in tableView: UITableView) -> Int {
self.servicesData.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Array(self.servicesData.values)[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
/* cell creation code code */
/// sample line
cell?.someLabel.text = Array(self.servicesData.values)[indexPath.section][indexPath.row]
return cell
}

How to display title for header text for header in first section iOS

Using method below of viewForHeaderInSection i try and display an image in table header section one , but i also want a heading text, using the method of heading title text i am able to display the heading for all sections but not section 1 , do i need to make some changes to heading view for section one to be able to display the heading , if i were to remove the viewForHeaderInSection then the title for first header section also shows up
let sectionTitle = ["Feedback", "Follow Us"]
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 0 {
headerTop.headerLogo.image = UIImage(named: "foodpin-logo")
return headerTop.topView
}
return nil
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sectionTitle[section]
}
You need to create a text label in your headerTop view and remove this function override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int). So your code will be like this:
let sectionTitle = ["Feedback", "Follow Us"]
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
headerTop.headerLogo.image = (section == 0) ? UIImage(named: "foodpin-logo") : nil
headerTop.yourHeaderTextLabel.text = sectionTitle[section]
return headerTop.topView
}
It seems like you are looking for tableHeaderView! The issue is you are overriding the first section's header.
You may consider setting the tableView's tableHeaderView for the headerTop instead. So all sections will have their titles as you expected.
tableView. tableHeaderView = headerTop
Also, if you want yout table's header to be sticky, there are bunch of tutorials for that.

UITableview Sections Indexing Click Event

Is there any way to get click event of the table view section indexing?
I have researched lots but not got any appropriate suggestion.
does anyone know how to get click event action of indexing?
I want to get click event of this blue marked indexing in the below image.
You can use the tableView(_:sectionForSectionIndexTitle:at:) method to return the appropriate section index when a index title is clicked.
let sectionArr = ["a","a","b","c","d","d"]
func numberOfSections(in tableView: UITableView) -> Int {
return sectionArr.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sectionArr[section]
}
func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return Array(Set(sectionArr))//["a","b","c","d"]
}
func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
if let index = sectionArr.firstIndex(where: { $0 == title }) {
return index//0 for a, 2 for b, 3 for c, 4 for d
}
return 0
}
You can use a custom view as the index view of the UITableView as shown in below reference :
Custom UITableView section index
This is in Objective-c but I think it is a great reference as that has the touchesBegan, touchesMoved, touchesEnded and touchesCancelled events and you can set the user experience with these as you would like. Also it would be very easy to add customised behaviours in this custom indexing view.
Hope this helps.

TitleForHeaderInSection not displaying correctly

This app is just for research and learning purposes. I'm new to developing, so there is nothing fancy about that, really.
It should display a TableView with some contacts, which are split into two different categories: Recent & Friends
However, when I try to return those to be displayed as a header (everything else worked just as I intended it to) with the TitleForHeaderInSection function, only one gets displayed eventually. (I used a switch function, so for that matter only case 0 is displaying).
override func numberOfSections(in tableView: UITableView) -> Int {
return 2 // Recent & Friends
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch section {
case 0: return "RECENT"
case 1: return "FRIENDS"
default: break
}
return String(section)
}
As seen in the screenshot above, in the app only one of the two cases gets returned and I am genuinely struggling to figure out why.
is it showing sections or sections are missing, not just heading ?if sections are missing then you need to select tableview style grouped in storyboard
Its because you dint implement these delegates :(1) viewforHeaderInSection (2)heightForHeaderInSection
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
//Create label which you want to show on each section header and return it
}
Also use heightForHeaderInsection method .
Hope this help to solve your issue

How can I add/remove cells in a table view more elegantly?

I think this is probably an X Y problem, so I'll give some background info first.
I am building an app that can show a "form". The user can fill in the form with some stuff and create some custom things.
I think the most suitable thing to do is to use a table view for the form. And I can display all the text boxes that need to be fill in, in each of the table cells.
Here's a screenshot:
The "Add New Input" button will insert a new cell on the bottom when it is tapped. And if you swipe one of the inputs to the left, you get a "Delete" button. You can use that to delete the input.
As you can see, this table view needs to add and delete rows.
Originally, I was using a cocoapod called "TableViewModel" which makes this super easy. But then I found a really severe bug in the library so I don't want to use it anymore.
I tried using the table view's deleteRowsAtIndexPaths and insertRowsAtIndexPaths methods. But if I do it like this:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = "Hello"
return cell
}
// I use motionEnded here because I don't want to add a button or anything just to test this
override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent?) {
tableView.deleteRowsAtIndexPaths([NSIndexPath(forRow: 0, inSection: 0)], withRowAnimation: .Fade)
}
It will result in an exception saying that the table view is inconsistent with the data source after I deleted one row. This means I have to have code to handle this inconsistency. This will definitely make my code harder to read.
The same thing goes with the insert method.
Also, I need to keep track of how many rows there are in each section. And my code just becomes really messy and unmaintainable with all that.
I have also searched for other libraries but they are all really weird and not as straightforward as "tableViewModel". They all seem to require me to create a model for the table view. I don't understand how to do that in my case. I just want to display a bunch of text fields!
How can I insert or delete rows more elegantly? I think I either need to write an extension of the table view or learn to write a model for my table view. But I am able to do neither of these methods.
Because you don't have any data source. Also see logically: suppose you add a new row with insertRowsAtIndexPaths . So now you have 2 rows. However your numberOfRowsInSection always returning 1. so it will crash and vice versa for delete. Table view are supposed to work with a collection (NSArray, NSDictionary, NSSet etc.).
For your help:
Already they have nade a form as yours
obj-c easy to understand how table view with data source work
Adding, Updating, Deleting and Moving records using Swift.
You may use a TableView to create the form but you must design an appropriate dataSource to keep track of these data. Now the dataSource method must be modified to
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return yourDataSourceForSection.count
}
You may edit this dataSource when a delete operation is performed as follows
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
//Delete the row from the data source to ensure consistency
self.array.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
}
You may use a model class as the dataSource.
class NewCustomOperation: NSObject {
var name: String?
var rejectFloatingPoints: Bool?
var inputs: AvailableInputs?
var results: Results?
}
class AvailableInputs: NSObject {
var name: [String]?
var description: [String]?
}
class Results: NSObject {
var name: [String]?
var formula: [String]?
}
I found a solution!
I basically keep an array of an array of cells for the table view to display:
var cells: [[UITableViewCell]] = [[], [], []] // I have 3 sections, so 3 empty arrays
And then I added these data source methods:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return cells.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cells[section].count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return cells[indexPath.section][indexPath.row]
}
Now, I can add and remove cells super easily:
func addCellToSection(section: Int, index: Int, cell: UITableViewCell) {
cells[section].insert(cell, atIndex: index)
tableView.insertRowsAtIndexPaths([NSIndexPath(forRow: index, inSection: section)], withRowAnimation: .Left)
}
func removeCellFromSection(section: Int, index: Int) {
cells[section].removeAtIndex(index)
tableView.deleteRowsAtIndexPaths([NSIndexPath(forRow: index, inSection: section)], withRowAnimation: .Left)
}
With just two lines, I can add/remove cells with animation!

Resources