Issues with drawing a line during tableview reload - ios

I have a tableview. I reload a section. My sectionheader has a line that requires to be drawn.
The positioning of this line and how it’s drawn is based on contentview’s frame
If if do tableview.reloadData() it get’s drawn correctly. Tableview header’s frame is non-zero
If I do notesTable.reloadSections([1], with: .automatic) it doesn’t get drawn correctly. Tableview header’s frame is zero!
( I need to use reloadSections because I want to animate it. reloadData() doesn’t give any animation)
so a very watered down example of my viewForHeaderInSection is:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
// some code
sectionView.setupUI()
return sectionView
}
Header class:
class SectionHeaderclass: UITableViewHeaderFooterView{
func setupUI(){
let triangleViewArea = ViewWithTriangleLine(triangleCenter:Double(contentView.frame.width - 30))
}
}
‍👆 contentView.frame.width returns 0 if I animate the section reload. Why?! How can I fix this?

If you want to update header, and it has some drawing view, its better you call setNeedsDisplay() on headerView instead of reloading tableview.
Note that reloadSections(...) updates cells in the sections not the header view.

Related

UITableView weird scroll behaviour with navigation bar large title, bounce effect at the top auto cut off / jerky when scroll to top

I have a UITableView with multiple sections with the header, collapsable/expandable. And also a tableHeaderView with custom UIView.
Using custom section header UIView and also custom UITableViewCell.
When all sections and rows are fully expanded, there is this weird scroll behavior, when I'm scrolling to the top (scroll down), when I reach the very top, the large navigation bar title should follow my scroll down gesture and animate down its way (Bounce effect). However, in this case, the bounce effect did not happen, the moment I scrolled to the top and try to scroll more for bounce effect, the scroll automatically got cut off and the navigation bar automatically becomes a small title.
Surprisingly, when I collapsed all the rows of the first section, the scroll behavior goes back to normal.
Here's a gif to show my screen recording.
https://i.imgur.com/WwXmpmZ.gifv
I have set the following to my UITableView:
self.tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 15, right: 0)
self.tableView.estimatedRowHeight = 0
self.tableView.estimatedSectionHeaderHeight = 0
self.tableView.estimatedSectionFooterHeight = 0
I have also tried:
self.tableView.contentInsetAdjustmentBehavior = .never
This indeed solved the weird scrolling behavior, however it causes my section headers and rows to overlap with my tableHeaderView.
The way I handle the collapse/expand. If the object property isCollapsed is true, I just simply return 0 row for that section:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == self.scheduleCount {
//Last section
return self.dataSource.count
}
guard let schedule = self.itineraryDataSource?.schedule[section] else { return 0 }
if schedule.isCollapsed {
return 0
} else {
return schedule.items.count
}
}
These are all the height delegates, the last section is having different UITableViewCell, hence the different height.
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == self.scheduleCount {
//Last section
return 40
}
return 64
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == self.scheduleCount {
//Last section
return 112
} else {
return 76
}
}
Managed to fix this.
Turns out I was returning UIView for my viewForHeaderInSection and I wasn't using tableView.dequeueReusableHeaderFooterView. I literally init a new xib UIView every time I scroll the tableView which causes it to automatically adjust the content inset, hence, caused the jerky scroll.
So, I created new custom xib for my section header view with type UITableViewHeaderFooterView, and simply return it at viewForHeaderInSection.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let dayHeaderView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "ItineraryDayHeaderView") as? ItineraryDayHeaderView
// Configure View...
return dayHeaderView
}

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

iOS UITableView Custom Footer with UIView

I want to make a custom footer for my UITableView.
For that, I will need the position of the last cell in the UITableView and set my UIView under the TableView.
But I don't know how to get the position of the last cell.
Is it even possible to make this kind of custom Tableview-footer?
You can add a footer directly in the storyboard or xib--just drag your view so that it's inside your tableView, but after all of the tableView's cells.
You don't need to make custom footer or to know the position of your last cell. It is already available on UITableViewDelegate.
You need to make a custom UIView then on the callback of func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? return the UIView.
Example:
func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let view = UIView.init(frame: CGRectMake(0, 0, tableView.frame.size.width, tableView.frame.size.height))
view.backgroundColor = UIColor.greenColor()
return view
}
The above code will give you a green UIView for the UITableView. Just make sure you do not set the Footer height to 0 on Storyboard or on code.

Change UITableViewHeaderFooterView height at runtime

My target is to change the UITableView section header height at runtime. Let me be more specific. Default height is 44 but on scroll before touching the top, height will be 64. I have created a subclass of UITableViewHeaderFooterView and it's uses autolayout.
I tried
var frame = sectionHeader.frame
frame.size.height = 64
sectionHeader.frame = frame
and also
tableView.sectionHeaderHeight = 64
but nothing work for me. Can anyone put some light on this problem.
when using autolayout change frame directly won't work
to change tableview section header you need implement delegate method and reload data after change
optional func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
You don't need to subclass the footer to change I'ts height.
Check you are only setting I'ts height at: (no delegate methods)
tableView.sectionHeaderHeight = 64
Check you call or "reloadData()" on the tableView after. or
[UIView setAnimationsEnabled:NO];
[tableView beginUpdates];
[tableView endUpdates];
[UIView setAnimationsEnabled:YES];
To only change height without reloading the tableView.
I think you have to reload the entire section of the table view, since there is no public API for only updating the section header.
self.isOnTop = true // or false
tableView.reloadSections(NSIndexSet(index: 0), withRowAnimation: .None)
And change a property that modifies the return value from the aproppriate delegate method:
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
isOnTop ? 44 : 64
}
You can also try the following methods, that can update the layout without reloading:
tableView.beginUpdates()
tableView.endUpdates()
Or move to a collection view solution, that supports the tableview's behaviour: https://github.com/jamztang/CSStickyHeaderFlowLayout

How to scroll up an entire table view in Swift?

I have a horizontal scrolling paged collectionview on top, and a tableview at the bottom, like this:
What I'm trying to achieve is that when the user scrolls up the table view, I want the whole page to scroll upwards. Currently, the tableview is confined to the bottom space when scrolling up. Lots of apps have this pattern, but the Fiverr app is an example, and here is a screenshot. The second picture is when I started to scroll up:
I am a complete beginner in Swift so I have no idea even what keywords to search for. What would be the easiest way to accomplish this?
I think you want to put all the views into a UIScrollView.
You can also put the UICollectionView into the table's header view. But if you have more components below the UITableView, I find it easier to just put the whole thing into a UIScrollView.
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
//build header view here
headerView.addSubview(collectionView)
return headerView
}
override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
//Return the height of the headerview.
return height
}
Edited to answer comment :)

Resources