Prevent footer overlapping tableViewCell in UITableView - Swift - ios

I have this table view which works how i want it to however i have a problem where the footer overlap the cells in the table view as seen in
How can i prevent this? This is my code for the footer
func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footerView = UIView(frame: CGRectMake(0, 0, tableView.frame.size.width, 40))
// self.myTableView.tableFooterView = footerView;
let sectionString = Array(foodArray.keys)[section]
for value in caloriesArray[sectionString]! {
calories += value
}
totalCalories += calories
print(calories)
print(totalCalories)
let label = UILabel(frame: CGRectMake(footerView.frame.origin.x - 15, footerView.frame.origin.y, footerView.frame.size.width, 20))
label.textAlignment = NSTextAlignment.Right
label.text = "Total Calories: \(calories) "
footerView.addSubview(label)
calories = 0
return footerView
}
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 20.0
}
#IBAction func addFoodTapped(sender: AnyObject) {
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let sectionString = Array(foodArray.keys)[indexPath.section]
foodArray[sectionString]?.removeAtIndex(indexPath.row)
caloriesArray[sectionString]?.removeAtIndex(indexPath.row)
print(foodArray)
viewDidAppear(true)
}

You can do that, Just make Style Grouped as shown below:

Just add some padding to the bottom of the content so that it doesn't overlap with the footer:
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, FOOTER_HEIGHT, 0)

I know this is an old thread, so this is more so for others that encountered this (like myself).
The floating section footer is default behavior from my understanding. There are a couple of options that I can think of:
Provide your footer view with a background color (footerView.backgroundColor = UIColor.white) thus cleaning up the overlap.
or
Replace the section footer with a custom "Total Calories" cell that you add to the table after the last cell in that section, effectively acting like a footer cell.

Sorry for the delay. If you have modified the standard contentInsetAdjustmentBehavior of the tableView, you must adjust the tableView contentInset property to take into account the total height of the views at the bottom of the UITableView, like the tab bar. If contentInsetAdjustmentBehavior is set to "automatic" (or you didn't change the default value), then set the clipsToBounds property of your footer view to true so that its child views cannot be painted outside the footer view layer's frame. That should solve your issue.

Try to override this method
override func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 44.0
}

Just set your label's background color to UIColor.white. and you are done !
label.backgroundColor = UIColor.white

Of course it overlaps. This is how footers and header work in UITableViews. You can set the footerView.backgroundColor to UIColor.gray, for example, to make it look better.

Related

Keyboard pushes FooterView up when its opening < TextView > Swift

I have a problem that I couldn't find any appropriate solution.
I have a tableView with custom cells which holds a textView inside.
also I have a blank grey footer for the section.
whenever I click on the textView and the keyboard pops up the footer in that section goes up and hides behind the textView field.
I don't know how to make that footer sticky so it won't move when the keyboard pops.
if anyone have a solution it will be great!
thank you!
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
if section == 0 {
let footerView = UIView()
footerView.backgroundColor = .red
return footerView
}else {
return UIView()
}
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 8
}
Set your tableView as grouped give height to the tableview footer and tableView Header in the tableview header,footer height functions.
If you are not using tableview header the set the height of the header to .leastNormalMagnitude .
This is the way to make the tableView Footer Static.

spacing between UITableViewCells swift3

I am creating a IOS app in swift and want to add spacing between cells like this
I would like to give space of each table view cell same like my attach image.
How I can do that? and Right now all cells are coming without any space.
swift3
you can try this in your class of tableView cell:
class cell: UITableViewCell{
override var frame: CGRect {
get {
return super.frame
}
set (newFrame) {
var frame = newFrame
frame.origin.y += 4
frame.size.height -= 2 * 5
super.frame = frame
}
}
}
From Storyboard, your view hierarchy should be like this. View CellContent (as highlighted) will contain all the components.
Give margin to View CellContent of 10px from top, bottom, leading & trailing from its superview.
Now, select the tblCell and change the background color.
Now run your project, make sure delegate and datasource are properly binded.
OUTPUT
NOTE: I just added 1 UILabel in View CellContent for dummy purpose.
Update: UIEdgeInsetsInsetRect method is replaced now you can do it like this
contentView.frame = contentView.frame.inset(by: margins)
Swift 4 answer:
in your custom cell class add this function
override func layoutSubviews() {
super.layoutSubviews()
//set the values for top,left,bottom,right margins
let margins = UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 0)
contentView.frame = UIEdgeInsetsInsetRect(contentView.frame, margins)
}
You can change values as per your need
***** Note *****
calling super function
super.layoutSubviews()
is very important otherwise you will get into strange issues
If you are using UITableViewCell to achieve this kind of layout, there is no provision to provide spacing between UITableViewCells.
Here are the options you can choose:
Create a custom UIView within UITableViewCell with clear background, so that it appears like the spacing between cells.
You need to set the background as clear of: cell, content view.
You can use UICollectionView instead of UITableView. It is much more flexible and you can design it the way you want.
Let me know if you want any more details regarding this.
One simple way is collection view instead of table view and give cell spacing to collection view and use
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
let widthSize = collectionView.frame.size.width / 1
return CGSize(width: widthSize-2, height: widthSize+20)
}
And if you want tableview only then add background view as container view and set background color white and cell background color clear color set backround view of cell leading, trilling, bottom to 10
backgroundView.layer.cornerRadius = 2.0
backgroundView.layer.masksToBounds = false
backgroundView.layer.shadowColor = UIColor.black.withAlphaComponent(0.2).cgColor
Please try it. It is working fine for me.
You can use section instead of row.
You return array count in numberOfSectionsInTableView method and set 1 in numberOfRowsInSection delegate method
Use [array objectAtIndex:indexPath.section] in cellForRowAtIndexPath method.
Set the heightForHeaderInSection as 40 or according to your requirement.
Thanks,Hope it will helps to you
- Statically Set UITableViewCell Spacing - Swift 4 - Not Fully Tested.
Set your tableView Row height to whatever value you prefer.
override func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = <Your preferred cell size>
tableView.rowHeight = UITableViewAutomaticDimension
// make sure to set your TableView delegates
tableView.dataSource = self
tableView.delegate = self
}
extension YourClass : UITexFieldDelegate, UITextFieldDataSource {
//Now set your cells.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "yourCell", for: indexPath) as! UITableViewCell
//to help see the spacing.
cell.backgroundColor = .red
cell.textLabel?.text = "Cell"
return cell
}
//display 3 cells
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
//now lets insert a headerView to create the spacing we want. (This will also work for viewForHeaderInSection)
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
//you can create your own custom view here
let view = UIView()
view.frame = CGRect(x: 0, y: 0, width: tableView.frame.width, height: 44) //size of a standard tableViewCell
//this will hide the headerView and match the tableView's background color.
view.backgroundColor = UIColor.clear
return view
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44
}
}

UITableView header dynamic height in run-time

I know there are a lot of posts about it, but maybe in newest iOS there are some updates on this...
I think all of us had a task to create viewController that has a lot of content at the top, most of them are self-sizing, and at the very bottom it figures out that you need to show some tableView with many items...
The first solution that can be done is to use UIScrollView, and don't care about reusableCells at all.
The second is to use UITableView's headerView and adjust its height manually (or by calling systemLayoutSizeFittingSize:) each time when it is needed.
Maybe the third solution is to use UITableView and self-sized UIView separately, with having UIEdgeInsets on tableView. And depending on what object has higher "zIndex", it can bring problems with handling interactions...
The forth solution is to use whole content above the cell, like a separate cell. Not sure this is a good idea at all...
Question: Is there any new solution to this problem? I haven't dig into it for like 2 years... Maybe in new iOS there is something like reusableViews for UIScrollView... Of course, the goal is to have reusable cells, and header with using autolayout without necessity of updating its height manually...
I am guessing you are talking about section headers of table view here. If that is so you can absolutely use auto layout for section headers.
Use the below two code in viewDidLoad:
tableView.sectionHeaderHeight = UITableViewAutomaticDimension
tableView.estimatedSectionHeaderHeight = 36;
Now in viewForHeaderInSection: try the below code just to get an idea how things are working out. Change it according to your requirement.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let label: UILabel = {
let lb = UILabel()
lb.translatesAutoresizingMaskIntoConstraints = false
lb.text = "HEADER \(section) with a loooooooooooooooonnngngngngngngngng texxxxxxxxxxxxxxxxt"
lb.textColor = .black
lb.backgroundColor = .yellow
lb.numberOfLines = 0
return lb
}()
let header: UIView = {
let hd = UIView()
hd.backgroundColor = .blue
hd.addSubview(label)
label.leadingAnchor.constraint(equalTo: hd.leadingAnchor, constant: 8).isActive = true
label.topAnchor.constraint(equalTo: hd.topAnchor, constant: 8).isActive = true
label.trailingAnchor.constraint(equalTo: hd.trailingAnchor, constant: -8).isActive = true
label.bottomAnchor.constraint(equalTo: hd.bottomAnchor, constant: -8).isActive = true
return hd
}()
return header
}
I'm using XCode 10.3 and this is my solution worked with your second solution using table header view.
First, you would create a separating view with xib file, for example with a label inside. And you apply the constraints for this label, top, left, bottom, right to the cell's container view. And set numberOfLines = 0.
Update your awakeFromNib() function inside your view class.
override func awakeFromNib() {
super.awakeFromNib()
ourLabel.translatesAutoresizingMaskIntoConstraints = false
}
Second, on your viewController, setup your tableView:
tableView.sectionHeaderHeight = UITableView.automaticDimension
tableView.estimatedSectionHeaderHeight = 64
Remember don't delegate this method:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
because we set the constraints of our view already.
Finally, you return it on the delegate method
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
the view for header.
let view = UINib(nibName: String(describing: SimpleHeaderTitleView.self), bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! SimpleHeaderTitleView
view.ourLabel.text = "Your longgggg text"
return view
Done! Check it works.
I like the way it's done here:
• If you want to set your tableview header height dynamically based on it's content, just call self.tableView.layoutTableFooterView() right after you have set your headerView as TableViewHeader (so after self.tableView.tableHeaderView = view )
• If you need to update your tableview header height on runtime, also call self.tableView.layoutTableFooterView() right after you have updated the values of your tableview header.
(This obviously also works with tableviewFooters, though, is not to be used for sectionHeaders/Footers)
extension UITableView {
//Variable-height UITableView tableHeaderView with autolayout
func layoutTableHeaderView() {
guard let headerView = self.tableHeaderView else { return }
headerView.translatesAutoresizingMaskIntoConstraints = false
let headerWidth = headerView.bounds.size.width
let temporaryWidthConstraint = headerView.widthAnchor.constraint(equalToConstant: headerWidth)
headerView.addConstraint(temporaryWidthConstraint)
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let headerSize = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
let height = headerSize.height
var frame = headerView.frame
frame.size.height = height
headerView.frame = frame
self.tableHeaderView = headerView
headerView.removeConstraint(temporaryWidthConstraint)
headerView.translatesAutoresizingMaskIntoConstraints = true
}
}
source
Copied from useyourloaf.com
Swift 5.0
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
guard let headerView = tableView.tableHeaderView else {return}
let size = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
if headerView.frame.size.height != size.height {
headerView.frame.size.height = size.height
tableView.tableHeaderView = headerView
tableView.layoutIfNeeded()
}
}
Step One :
From interface builder Drag a UIView and drop into
UItableView
This view will automatically act as a UITableView Header (Mind it not section Header)
. Suppose this the width of this view is 200 .If you run the UItableView This view will automatically appear as a Header .
Create a Outlet of this drag-drop View .
#property (weak, nonatomic) IBOutlet UIView *tableViewHeader; // height of this view is 200
Now my goal is increase the height of the table View header .
Step Two :
Add this method
- (void)viewDidLayoutSubviews
{
int increasedHeight = 100;
// set a frame of this view Like example
self.tableViewHeader.frame = CGRectMake(0, 0, self.view.frame.size.width , 200 + increasedHeight );
self.tableView.tableHeaderView = self.tableViewHeader;
}
Now you tableview header height will be 300 .
This is how i have approached it
Using tableview
I have created the UI For the header in XIB
Now in the following delegate method
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
}
I Create a UIView for the header and calculate the height based on the content and return the same.
Now i can return the same header view from the following delegate method
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
}
Based on the section i again create a view from xib and return that view from the method.
In my case i needed only one headerview for table so i kept 2 sections and returned the headerview for section one.
If you are using Interface Builder simply you check these buttons
You can do that in viewDidLayoutSubviews of the UIViewController that contains your UITableView:
#IBOutlet weak var tableView: UITableView!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Resize header view with dynamic size in UITableView
guard let headerView = tableView.tableHeaderView else {
return
}
let size = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
if headerView.frame.height != size.height {
tableView.tableHeaderView?.frame = CGRect(
origin: headerView.frame.origin,
size: size
)
tableView.layoutIfNeeded()
}
}
Xcode 14 + Swift 5
This is how I made it.
First step - configuring of table view for dynamic height headers usage:
In viewViewDidLoad you need to add:
tableView.sectionHeaderHeight = UITableView.automaticDimension
tableView.estimatedSectionHeaderHeight = 40
This also can be done in xib/storyboard where your tableView is located instead of configuring in code:
Second step - creation of header view:
You need to create custom view to use as table's header. For example view with label as a subview, pinned with constraints to superview. Any view that contains some subviews that could cause it to have different height. The main thing - you should set up content of header with constraints to make it resize itself.
Third step - configuration of tableView's delegate:
In your tableView delegate implement viewForHeaderInSection method and just return configured instance of your custom header view.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
IMPORTANT - heightForHeaderInSection method shouldn't be implemented, just don't add it.
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat

UITableView Section Header Automatic Height Not Updated Properly

I am running into an issue with automatic/dynamic UITableView section header views that contain a UILabel that wraps (numberOfLines = 0). The height is not being calculated properly, especially once you scroll the table and the views are reused. Sometimes the UILabel wraps, sometimes it is truncated, sometimes one or more of the labels are not visible, and sometimes there is extra spacing between the labels. The custom view contains a vertical UIStackView with three UILabels, once of which wraps.
A complete sample app demonstrating the issue can be found at https://github.com/outerstorm/tableviewHeaderTest.
The section header heights are set to automatic in viewDidLoad with the following:
tableView.sectionHeaderHeight = UITableViewAutomaticDimension
tableView.estimatedSectionHeaderHeight = 30.0
and also have implemented the following heightForHeaderInSection just to try to get it to work:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return UITableViewAutomaticDimension
}
I have also tried calling setNeedsLayout() and layoutIfNeeded() at various points to no avail. Any suggestions would be greatly appreciated.
Below is a screenshot of the behavior seen in the app. The first section is cutoff and the second section is too tall:
I have faced this kind of issue recently.
I solved it by setting preferredMaxLayoutWidth of multiline labels, i.e.
Before setting the value in labels, set their preferredMaxLayoutWidth as:
self.label1.preferredMaxLayoutWidth = self.label1.frame.size.width
self.label2.preferredMaxLayoutWidth = self.label2.frame.size.width
self.label3.preferredMaxLayoutWidth = self.label3.frame.size.width
Just add estimatedHeightForHeaderInSection function and return your estimated height. It will resolve your issue. Download your modified project from here
func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat
{
return 30;
}
In general
heightForHeaderInSection
always called before
viewForHeaderInSection
when using UITableViewAutomaticDimension, sectionheader height will only calculate once unless called tableview.reloadData() manually.
In the code, you change sectionheader text everytime. the height was calculate at the first time, and doesnt change automatic.
you can change setup func to:
func setup(someNum: Int) {
let text = String(repeating: "This is where the really long text goes so that it will wrap lines appropriately", count: someNum)
mainLabel.text = text
}
and pass the section index to the function
A workaround can be hold the header views in array and then return the height of view from estimated height method
for _ in 0 ..< data.count {
let view = tableView.dequeueReusableHeaderFooterView(withIdentifier: "CustomHeaderView") as! CustomHeaderView
view.setup()
headerViews.append(view)
}
func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat
{
let view = headerViews[section] as? UIView
return (view?.frame.size.height)!
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return headerViews[section] as? UIView
}
From the code's documentation:
// tableView:titleForHeaderInSection: or tableView:titleForFooterInSection: if the title is not nil.
In other words. UITableViewAutomaticDimension is only intended for use if you are providing a section title using titleForHeaderInSection or titleForFooterInSection
Add
self.label1.preferredMaxLayoutWidth = self.label1.frame.size.width
self.label1.numberoflines = 0;
self.label1.linebreakmode = NSLineBreakByWordWrapping;
in awakeFromNib method
or
Kindly check this once

UITableView HeaderView margin when removing separator's left space

I've removed the separator left margin from my tableview using in the viewDidLoad:
self.tableView.layoutMargins = UIEdgeInsetsZero
self.tableView.separatorInset = UIEdgeInsetsZero
In my cellForRowAtIndexPath:
cell.layoutMargins = UIEdgeInsetsZero
The problem is that the header and footer views has been moved too and I want to keep it aligned with the cells' content.
I've tried to change the frame in the willDisplayHeaderView and willDisplayFooterView without success. Any suggestion? Thanks!
As Memon Irshad suggest, I've created a new label but rather than do it inside the viewForHeaderInSection I've do it in willDisplayHeaderView because while using this method we can know the size of the headerView.
Then I hide the original textLabel of the TableViewHeader and adding the new framed label. What do you think? Any improvements?
Looks like this:
override func tableView(tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int)
{
let originView = view as! UITableViewHeaderFooterView
let lbl = UILabel(frame: CGRectMake(15,originView.frame.height-30,originView.frame.size.width,20))
// Setting up the new label
lbl.numberOfLines = 0
lbl.font = Constants.Fonts.HeaderRow
lbl.textColor = Constants.Colors.TextColored
// Copying the original text content
lbl.text = originView.textLabel?.text
// Hidding the original label
originView.textLabel?.hidden = true
originView.addSubview(lbl)
}
Header/Footer cannot stick to your cell's content. I too wasted a lot of time on that
You must add a row to your section and use it as if it is a header/footer
try this code set custom label frame in header view
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView()
let lbl = UILabel(frame: CGRectMake(15,2,self.view.frame.size.width,20))
lbl.textColor = UIColor.whiteColor()
lbl.text = "SOPORTE"
lbl.font = UIFont(name: "Helvetica Neue", size: 15)
view.addSubview(lbl)
return view
}

Resources