Why does UITableView footer appear over the top of the last cell? - ios

I have a UITableView which contains a bunch of cells (dynamically generated) and it displays a footer message showing the last updated time. The issue I'm having is that on the first loading of the UITableView it seems to display the footer over the top of the last cell. Once you reload the data it displays it correctly though.
Does anybody know why this is happening?
Initial loading:
After reloading:
The following is the logic I use for establishing the footer message to display:
override func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
var title = ""
if (section == kSectionA) {
if (!self.hasInternet) {
title = "No Internet Connection"
} else {
if (self.dataToDisplay) {
title = "Last Updated: xx:xx AM"
} else {
title = "No results found."
}
}
}
return title
}

You need to tell the Tableview how big the footer is from the beginning.Try using this code but with your values.
Tells the Tableview how big the footer is:
override func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footerView = UIView(frame: CGRectMake(0, 0, tableView.frame.size.width, 40))
footerView.backgroundColor = UIColor.grayColor()
return footerView
}
Tells the footer how big the footer is:
override func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 40.0
}
That should solve you problem.

Related

willDisplayHeaderView being called but text attributes in titleForHeaderInSection remain unchanged

I have a custom tableView with the willDisplayHeaderView below.
override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
let tableViewHeaderFooterview = UITableViewHeaderFooterView()
tableViewHeaderFooterview.textLabel?.font = UIFont.hmFont(13)
tableViewHeaderFooterview.textLabel?.textColor = UIColor.hmDarkGrey()
}
I have another tableViewController that subclasses from the custom tableView with the titleForHeaderInSection below.
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "YOUR SUBSCRIPTION"
} else if section == 1 {
return "SIGN OUT"
}
return nil
}
My willDisplayHeaderView is called but I am not seeing the changes in the header view titles when I run my app on simulator. I have been searching for answers the past couple hours on here but so far no solution has worked. I thought at first I might be forgetting to set my delegate/datasource but that didn't work.
Let's take a look at your code
override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
let tableViewHeaderFooterview = UITableViewHeaderFooterView()
tableViewHeaderFooterview.textLabel?.font = UIFont.hmFont(13)
tableViewHeaderFooterview.textLabel?.textColor = UIColor.hmDarkGrey()
}
In the function declaration you've got a view called view that your function is being called with. In other words, iOS is looking up this function on your class and saying, "I'm about to show this view, just so you know." In the first line of your implementation though, you're creating a new view called tableViewHeaderFooterview and setting some properties on it. Those changes are not going to be reflected in the view that iOS is telling you about.
At this point you have a few choices: 1) muck around with view's subviews looking for labels (messy and bad), 2) try to cast it to UITableHeaderFooterView (if let header = view as? UITableViewHeaderFooterView { header.textLabel.font = whatever }), or 3) register your own UIView subclass and configure it how you want ahead of time:
class MyHeader: UITableViewHeaderFooterView {
...
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerClass(MyHeader.self, forHeaderFooterViewReuseIdentifier: "myHeader")
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "myHeader") as? MyHeader {
myHeader.label.text = "YOUR SUBSCRIPTION";
return myHeader
}
return nil
}

Set margin on top of a tableview in IOS

I'm new to IOS development with swift and I'm having a problem. I need to create a tableview and it looks almost the way I wanted, except for the space at the top of the first section of the table. It has no name but I would like to reduce the space between the top and the first item. What I was able to do is according to the code and image below:
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch (section) {
case 0:
return ""
default:
return self.nameSection2
}
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
headerView.backgroundColor = UIColor.white
let headerLabel = UILabel(frame: CGRect(x: 15, y: 8, width:
tableView.bounds.size.width, height: tableView.bounds.size.height))
headerLabel.font = UIFont(name: "Verdana", size: 16)
headerLabel.textColor = UIColor.lightGray
headerLabel.text = self.tableView(self.tableView, titleForHeaderInSection: section)
headerLabel.sizeToFit()
headerView.addSubview(headerLabel)
return headerView
}
The 'margin' you see is because the height for both the section headers is the same. The second one looks less-empty as it actually has a title.
You can modify the height for the headers to reduce the space:
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
switch section {
case 0:
return 0
case 1:
return 44 //Required height value here
default:
return defaultValue //Any default value
}
}
You need to implement the heightForHeaderInSection so you can collapse that header. See below:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 0 {
return 1.0
} else {
return 32.0
}
}
You can set the appropriate value for the else condition for your needs but this gives you the idea.
Update #1: I found this link in searching that may help as well: http://stackoverflow.com/a/23955420/3965
It recommends using GLFloat's minimum value instead:
if section == 0 {
return CGFloat.leastNormalMagnitude
}
return tableView.sectionHeaderHeight
Implement heightForHeaderInSection and return the height you want for the first section.
Also, you wouldn't normally implement titleForHeaderInSection and viewForHeaderInSection. Just put your switch statement in viewForHeaderInSection to set the text for your label.
And you don't need to put your UILabel into headerView, just return the label. Or instead of UIView, use UITableViewHeaderFooterView.

Why do collapsing/expandable iOS UITableView rows disappear on interaction?

I am new to iOS Development and I just implemented a simple expandable sections UITableView. I am not able to understand why some rows disappear and sometimes change position when the row heights are recalculated on tapping the section header. I went through all the already answered questions on this topic and have not been able to find the right solution.
Following is a scenario:
Launch the app:
Tap on the section header:
Section expands
All other headers disappear
Tap again
Section collapses
The headers continue to be blank
Scrolled to the bottom and back to the top
The positions of headers changed
Scrolled to the bottom and back to the top again
The positions of headers changed again with some cells still blank
Things I have already tried:
Wrapping reloadRowsAtIndexPaths in updates block (beginUpdates() and endUpdates())
Using reloadRowsAtIndexPaths with animation set to .none
Removing reloadRowsAtIndexPaths at all while keeping the updates block
Using reloadData() instead which actually works but I lose animation
Code:
Here is the link to the project repository.
You're using cells for the header. You shouldn't do that, you need a regular UIView there, or at least a cell that's not being dequeued like that. There's a few warnings when you run it that give that away. Usually just make a standalone xib with the view and then have a static method like this in your header class. Make sure you tie your outlets to the view itself, and NOT the owner:
static func view() -> HeaderView {
return Bundle.main.loadNibNamed("HeaderView", owner: nil, options: nil)![0] as! HeaderView
}
You're reloading the cells in the section that grows, but when you change the section that's grown you'd need to at least reload the former section for it to take the changes to it's cell's height. You can reload the section by index instead of individual rows in both cases
Ok as you ask, I am changing my answer according to you.
import UIKit
class MyTableViewController: UITableViewController {
let rows = 2
var categories = [Int](repeating: 0, count: 10)
struct Constants {
static let noSelectedSection = -1
}
var selectedSection: Int = Constants.noSelectedSection
func selectedChanged(to selected: Int?) {
let oldIndex = selectedSection;
if let s = selected {
if selectedSection != s {
selectedSection = s
} else {
selectedSection = Constants.noSelectedSection
}
tableView.beginUpdates()
if(oldIndex != -1){
tableView.reloadSections([oldIndex,s], with: .automatic)
}else{
tableView.reloadSections([s], with: .automatic)
}
tableView.endUpdates()
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return categories.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("reloading section \(section)")
return (selectedSection == section) ? rows : 0;//rows
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return tableView.rowHeight
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return tableView.rowHeight
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "Header")
if let categoryCell = cell as? MyTableViewCell {
categoryCell.category = section + 1
let recognizer = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture))
recognizer.numberOfTapsRequired = 1
recognizer.numberOfTouchesRequired = 1
categoryCell.contentView.tag = section;
categoryCell.contentView.addGestureRecognizer(recognizer)
}
return cell?.contentView
}
func handleTapGesture(recognizer: UITapGestureRecognizer) {
if let sindex = recognizer.view?.tag {
selectedChanged(to: sindex)
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Body", for: indexPath)
if let label = cell.viewWithTag(1) as? UILabel {
label.text = "Body \(indexPath.section + 1) - \(indexPath.row + 1)"
}
return cell
}
}
As you can see now I am just reloading a particular section instead of reloading the whole table.
also, I have removed gesture recognizer from the cell & put this into the main controller.

Too long text for titleForHeaderInSection

So what my problem is that my text above my section is too long and gets cut off.
Any way to solve this like making it two rows long?
Any help is appreciated
You need to define the heightForHeaderInSection and customize viewForHeaderInSection. You can either fix all header heights at a value big enough for all lines, or calculate the required height for the specific header (as below).
let headerFont:UIFont = UIFont.systemFontOfSize(14);
let headerTexts = ["one line", "two line test123 sadfjklsadf asdjfklasjdflk asdfjklasdjfl asdfjklsadf"];
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2;
}
override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return heightOfHeaderText(headerTexts[section]);
}
func heightOfHeaderText(text:String) -> CGFloat{
return NSString(string: text).boundingRectWithSize(
CGSizeMake(self.tableView.frame.size.width, 999),
options: NSStringDrawingOptions.UsesLineFragmentOrigin,
attributes: [NSFontAttributeName : headerFont],
context: nil).size.height;
}
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerLabel:UILabel = UILabel.init(frame: CGRectMake(0, 0, tableView.frame.size.width, self.tableView(tableView, heightForHeaderInSection: section)));
headerLabel.numberOfLines = 0;
headerLabel.lineBreakMode = NSLineBreakMode.ByWordWrapping;
headerLabel.font = headerFont;
headerLabel.text = headerTexts[section];
return headerLabel;
}
Make a custom view with a label in it. And use viewForHeaderInSection delegate method to assign text to that label and return this view.
EDIT:
See this link
Customize UITableView header section

Return two different sections in my UITableView

I'm trying to return two seperate .counts in my UITableView. I kind of know the logic behind it but not using the syntax right. How can I do this? What I want it to do is fill my tableview up with the both .counts. I would also like to put in a label on top of the first section and second section. Anything would help!
Here is what I have so far but I'm only getting the first section of cells. Why isnt it displaying both .counts?
func tableView(tableView: UITableView, numberOfSectionsInTableView: Int) -> Int{
return 2;
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (section == 0){
return featSubNames.count
}
else{
return subCatNames.count
}
}
func tableView(tableView: UITableView!, viewForHeaderInSection section: Int) -> UIView! {
if (section == 0){
????
}
if (section == 1){
????
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell
// Configure the cell
switch (indexPath.section) {
case 0:
cell.textLabel?.text = featSubName[indexPath.row]
cell.textLabel?.textColor = UIColor.whiteColor()
case 1:
cell.textLabel?.text = subCatNames[indexPath.row]
cell.textLabel?.textColor = UIColor.whiteColor()
default:
cell.textLabel?.text = "Other"
}
return cell
}
Your code seems to be right. To get the UIViews on the header of each section, you can return any object that inherits from a UIView(this doesn't mean that would be nice to). So, you can return a small container with a UIImageView and a UILabel, if you want this for example.
Your code for the viewHeader would be something like this:
func tableView(tableView: UITableView!, viewForHeaderInSection section: Int) -> UIView! {
// instantiate the view to be returned and placed correctly
let commonLabel = UILabel(frame: CGRectMake(0, 0, tableWidth, 30))
commonLabel.textColor = .blackColor()
commonLabel.textAlignment = .Center
// ... other settings in the properties
if section == 0 {
commonLabel.text = "section1"
// other settings for the first section
}
if section == 1 {
commonLabel.text = "section2"
// other settings ...
}
return commonLabel
}
Notes:
Be sure to set the container view's frame,for sectionHeaders the top view in the hierarchy don't accept auto layout.
Implementing the viewForHeaderInSection method is more accessible to custom UI's, you can use the TitleForHeaderInSection if you wish, but it's limited for more complex stuffs.
EDIT:
If you're using storyboards, you still can use this code, although it's not so elegant in terms of apps that use IB for the UI. For this, you might take a look at this link: How to Implement Custom Table View Section Headers and Footers with Storyboard
. This implements only the view using storyboards, but the delegate part must to be wrote.

Resources