Why does overriding tableView(:viewForHeaderInSection:) change the results - ios

I have a simple implementation of a table view with no header. This is the result that I'm going for.
class NavigationController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
self.pushViewController(MyTable(style: .grouped), animated: false)
}
}
class MyTable: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return CGFloat.leastNormalMagnitude
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return CGFloat.leastNormalMagnitude
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
assert(false)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return UITableViewCell()
}
}
Notice that I have an override for viewForHeaderInSection which never gets called which is expected since the height I give is essentially zero.
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
assert(false)
}
When I remove the definition of the above function, then I get a different result. Why does this happen, and is there a way to get the same result without overriding this method?

If a UITableView's delegate implements tableView(_:viewForHeaderInSection:), the calling UITableView switches from assuming you want titles in your section headers to thinking that you want views in your section headers. Once that switch has been made, it checks the delegate's tableView(_:heightForHeaderInSection:) — and since you return (effectively) zero, I'd guess that the table view can lay out without querying the delegate for an actual view.
Keep in mind that making this switch never has to involve calling the view-for-header method. UITableView can check whether its delegate implements the ObjC selector for the method without needing to actually call it, and change its behavior accordingly. That's most likely why your assertion never fires.

The compiler actually throws an error if you don't have the override keyword along with the function.
Overriding declaration requires an 'override' keyword

Related

How to remove the separator between the header and the first row in a grouped tableView

I am creating a static tableView. My goal is to have a single view that displays a tableView with 3 rows (get support, send feedback, participation terms), and a header + footer.
Right now, it all works fine EXCEPT the fact that there are two extra separators (one between the header and the first cell and the other between the last cell and the footer) that I cannot seem to get rid of.
Here is my code:
final class viewController: UITableViewController {
private let headerContainer = UIView()
private let footerContainer = UIView()
private let tableData = ["Get Support", "Send Feedback", "Participation Terms"]
override func viewDidLoad() {
super.viewDidLoad()
setupHeaderAndFooter()
setupTableView()
}
func setupHeaderAndFooter() {
/* setup code here (not relevant to this question) */
func setupTableView() {
// reinitializing tableView so that we can change its style to grouped
tableView = UITableView(frame: CGRect.zero, style: .grouped)
tableView.delegate = self
tableView.separatorStyle = .singleLine
}
//MARK: UITableView Methods
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return headerContainer
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 150
}
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
return footerContainer
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 200
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableData.count
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 70
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cell")
cell.textLabel!.text = tableData[indexPath.row]
return cell
}
}
One way to achieve this is don't use default separator provided by UITableView. and place one UILabel or UIView and set its background color to light gray and set its height=1 (As per your requirement), width (full tableview width or as per your requirement) and position at bottom of all you content inside cell, header or footer.
Ok here you go.
The easy trick is actually a one-liner and a self-explanatory:
self.tableView.separatorColor = self.tableView.backgroundColor
AFTER:
I hope this helps!
You have to set clipsToBounds to true on your header view.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = //Get your header here
header.clipsToBounds = true
return header
}
Before:
After:

How do I add spacing between two cells in UITableView? There's nothing like separator height or something

I need a layout something like in attached image. I have tried adding a subView at the bottom of UICell but it actually distorts the other items UI. Can someone please help? I am working in Xamarin.iOS
One option is to design the cell as the xib.
One option is to use sections and section footers:
override func numberOfSections(in tableView: UITableView) -> Int {
return 5
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
// your spacing
return 20
}
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footer = UIView()
footer.backgroundColor = .clear
footer.isOpaque = false
return footer
}

Strange issue with displaying cells in UITableView

I have UITableView with methods:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
override func numberOfSections(in tableView: UITableView) -> Int
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
So, the problem is that Xcode runs at first
override func numberOfSections(in tableView: UITableView) -> Int
which returns me 1.
Later it runs override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int which also returns me 1.
BUT I do not know why after these 2 methods it does not run cellForRowAt method and does not display any row on my UITableView.
I do not know what happens there and why this happens.
Have you met such problem and could you please help me to fix it?
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let fetchedResultsController = self.fetchedResultsController, let fetchedObjects = fetchedResultsController.fetchedObjects {
if !searchController.isActive {
print("numberOfRowsInSection fetchedObjects.count - \(fetchedObjects.count)")
return fetchedObjects.count
} else {
if let results = filteredResult[searchController.searchBar.selectedScopeButtonIndex] {
print("numberOfRowsInSection results.count - \(results.count)")
return results.count
}
}
}
print("numberOfRowsInSection - 0")
return 0
}
override func numberOfSections(in tableView: UITableView) -> Int {
if let frc = fetchedResultsController, frc.fetchedObjects?.count != 0 {
print("numberOfSections - 1")
return 1
} else {
print("numberOfSections - 0")
return 0
}
}
What is probably happening is that your cell has height 0. When this happens, cellForRowAt won't be called at all, even if the other methods return a non zero section/row count.
My guess as to why this is happening is that you may be using auto layout for your cell, but your constraints don't allow the cell to figure out its own height. You may be using auto layout unknowingly, since on iOS 11, it's now the default. If you are using storyboards you can set the height for the cell prototype on the attributes inspector, instead of checking the automatic box. Alternatively, you can set it in code with tableView.rowHeight or by implementing func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat

Compile & Build error related to "Type 'ViewController' does not conform to protocol 'UITableViewDataSource'"

I am not understanding why my app is not compiling. This is the output currently:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var IndexArray = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]
override func viewDidLoad() {
super.viewDidLoad()
// 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.
}
func numberOfSectionsinTableView(tableView: UITableView) -> Int {
return IndexArray.count
}
func tableView(tableView: UITableView, tiltleForHeaderInSection section: Int) -> String? {
return IndexArray[section]
}
func sectionIndexTitlesfortableView (tableView: UITableView) -> [String]? {
return IndexArray
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableCell", for: indexPath as IndexPath) as! TableCell
cell.imgPhoto.image = UIImage(named: "charity")
cell.lblUserName.text! = "User Name"
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
}
You are missing to specify a few methods declared in the protocols your class is deriving from.
func tableView(UITableView, cellForRowAt: IndexPath)
Required. Asks the data source for a cell to insert in a particular location of the table view.
func tableView(UITableView, numberOfRowsInSection: Int)
Required. Tells the data source to return the number of rows in a given section of a table view.
At least the two methods above must be declared in your class, otherwise you get the error.
These are just the required methods, but for functioning in the correct way you need to define others. See Apple documentation on UITableViewDataSource protocol
In Swift 3 all method signatures have been changed to:
func numberOfSections(in tableView: UITableView) -> Int { }
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { }
func sectionIndexTitles(for tableView: UITableView) -> [String]? { }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { }
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {}

why titleForheaderInsection method in different table view controller not work?

why titleForheaderInsection method in different table view controller not work?
I create two table view controllers in one storyboard for my app, with different function, one is SettingsTableViewControlelr, one is CitylistTableViewControlelr, and they should have different section titles.
tableView:titleForHeaderInSection method is used to name section title, but unfortunately only the method in SettingsTableViewControlelr has been appeared correctly in iOS simulator, but the method in CitylistTableViewControlelr is not work, i put a breakpoint on tableView:titleForHeaderInSection method, and find the method even not be called in CitylistTableViewControlelr. Here is my code below:
SettingsTableViewController
import UIKit
class SettingsTableViewController: UITableViewController, UITableViewDataSource, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, titleForHeaderInSection section:Int) -> String? {
switch section{
case 0:
return"Settings"
default:
return nil
}
}
//the "tableView:titleForHeaderInSection" method in class SettingsTableViewController
//is called and section title appears on simulator.
override func tableView(tableView: UITableView,heightForHeaderInSection section:Int) -> CGFloat {
return 44
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("settingsIdentifier", forIndexPath: indexPath) as! UITableViewCell
return cell
}
}
CitylistTableViewControlelr
import UIKit
class CityListTableViewController: UITableViewController, UITableViewDataSource, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
func tableView(tableView: UITableView, titleForheaderInsection section: Int) -> String? {
switch section {
case 0:
return "Top Cities"
case 1:
return "Other Cities"
default:
return nil
}
}
//Putting a breakpoint here, and find "tableView:titleForHeaderInSection" method in
//CityListTableViewController is not even been called, thus the section titles, "Top Cities" & "Other Cities",
//do not appear in simulator.I have tried to add "override" keyword before the method, but the
//complier report error says "Method does not override any method from its superclass".
override func tableView(tableView: UITableView,heightForHeaderInSection section:Int) -> CGFloat {
return 44
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section{
case 0:
return 12
case 1:
return 15
default:
return 0
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cityListIdentifier", forIndexPath: indexPath) as! UITableViewCell
switch indexPath.section{
case 0:
cell.textLabel!.text=topCitiesList[indexPath.row]
case 1:
cell.textLabel!.text=otherCitiesList[indexPath.row]
default:
cell.textLabel!.text="unknown"
}
return cell
}
}
My questions are:
Why tableView:titleForHeaderInSection method not be called in CityListTableViewController?
How can I correct my code to make the section title appear on simulator/iPhone respectively?
Have a look at the differences between the two:
In the first view controller where it is being called, the function is being declared like this:
override func tableView(tableView: UITableView, titleForHeaderInSection section:Int) -> String? {
switch section{
Whereas in the one it isn't called it is being declared like this:
func tableView(tableView: UITableView, titleForheaderInsection section: Int) -> String? {
switch section {
You are missing the override declaration. Also, you didn't capitalize the "header" in the second declaration (Thanks for pointing that out Jesper).

Resources