Swift: UITableViewController - modify margin between UITableView and View Controller - ios

I am using UITableViewController in my app. It works well but default UITableViewController is adding some spacing between UITableView embedded inside it and main View of UITableViewController. Look at below image. How can I remove/modify this spacing form storyboard or from code?
In blue is my custom row. The spacing is between this blue tableView row and green navigation bar.

Adding this in ViewDidLoad helped me:
tableView.tableHeaderView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 0.0, height: CGFloat.leastNormalMagnitude))
It is also possible to remove this by implementing delegate methods
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return CGFloat.leastNormalMagnitude
}
But in the second approach it automatically removes all spacing also between TableView sections.
I have also heard that this problems occurs for Grouped style of UITableView.

Related

How can I add lines under each cell in my table view?

I was wondering how I would go about adding a separator line under each of my table view cells on Xcode (using swift) I want to make it so that under all of the cells other than the first cell it will add a separator.
Thank you in advance :D
Update:
Code that used to work for me.
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let result = UIView()
// recreate insets from existing ones in the table view
let insets = tableView.separatorInset
let width = tableView.bounds.width - insets.left - insets.right
let sepFrame = CGRect(x: insets.left, y: -0.5, width: width, height: 0.5)
// create layer with separator, setting color
let sep = CALayer()
sep.frame = sepFrame
sep.backgroundColor = tableView.separatorColor?.cgColor
result.layer.addSublayer(sep)
return result
}
The code above does the following within the old version however now doesn't add any extra lines.
Example of what I want:
The setup that I have it as:
Add a view on bottom of uitableview cell and hide and unhide when required in cellForRowAt. Use following constraints for seperator view
Add a cell with the same width as the other cells and make the height 1~2 according to your demand after every regular cell.

How to pass tap gesture through point on screen that is not in full-screen UITableView's UIEdgeInsets:

I have created a screen in which I have a full-screen UITableView, I set UIEdgeInsets to it, which I have configured as follows:
categoriesTableView.contentInset = UIEdgeInsets.init(top: HEADER_VIEW_HEIGHT, left: 0, bottom: 0, right: 0)
where HEADER_VIEW_HEIGHT is CGFloat = 160.
This allowed me to add a "header view" to the UITableView which is getting covered when I start scrolling the UITableView (...and not getting stuck above the UITableView, as a real header view will).
The Problem: The problem is that I need to have 3 clickable views in the header view, So I designed 3 views in the storyboard and configured Tap Gesture Recognizers on them. But when I try to use them the tap gestures are not passed throw the UITableView even though I see those views on screen (as a result of the contentInset configuration). The only way I can make them tapable/clickable is if I set User Interaction Enabled to false on the UITableView (which I can't do because I need the UITableView to be draggable and clickable as well).
The Question: How would I pass the tap events to the lower "header view" clickable parts when it's not covered by the UITableView as a result of the contentInset setting?
Here is the UI image, in it you can see that there is a full-screen UITableView, behind it there a view with 3 subviews that contains 3 favorite items which I can present to the user for an easier access. when the screen starts, there is a contentInset for the UITableView hence the user can see those easy access options and press them (which he can't do right now). When the user starts scrolling, the UITableView goes on top of the layout with the 3 views and the user able to scroll the list in a full screen. kind of like a parallax effect.
I have a very ugly solution for this..
Now you have a table view and below that there is a header view right? Add one more view on top of table view which contains 3 sub views in it and all transparent in colour.
Position this newly added subview same as that of header view (Either by programmatically giving same frame as that of header view or by connecting its top, left, bottom and right constraints to the header view). Similarly position the 3 sub views of this new view as same as that of the subviews inside the header view. And give tap gesture to the subviews of this new view. So our user will think he is tapping the header view, whereas he is actually tapping in this invisible view.
And if you want to get touch in the table view once it scrolls up and cover the header view, then you can use one of these two..
Call the UIScrollViewDelegate delegate method scrollViewDidScroll() and inside if scrollView.contentOffset.y >= 160, set the User Interaction Enabled to false for this newly added view and revert that back to true if scrollView.contentOffset.y < 160.
Or from inside scrollViewDidScroll(), assign outletOfTopConstraintOfYourNewView.constant = -(scrollView.contentOffset.y), so that the new view will also move up according to the scroll, thereby changing its visible tappable area.
Another not so elegant idea is..
Instead of giving contentInset, add one more section in this table view at the 0th index with only one cell whose height is 160, user interaction enabled is false and colour is transparent. Then you don't have to worry about the logic in scrollViewDidScroll().
Update:
Below solution is not perfect but it may give you a direction to start with.
A) Put the top constraint of tableView top to bottom constraint of headerview.
B) Create IBOutlet of height constraint of headerview in your ViewController
C) Listen to tableview's ScrollViewDidScroll and put code like this
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let y = scrollView.contentOffset.y
if y > 50{
if heightConstant.constant != 0{
view.layoutIfNeeded()
heightConstant.constant = 0
UIView.animate(withDuration: 0.5, delay: 0, options: .allowUserInteraction, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
}else{
view.layoutIfNeeded()
heightConstant.constant = 160
UIView.animate(withDuration: 0.5, delay: 0, options: .allowUserInteraction, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
}
Here 50 is a point form where it should start animating. It is somewhat similar to Collapsable Toolbar in Android. Just make sure headerView.isClipsToBounds = true.
Convert your headerview into a UITableViewCell and in change your UITableViewDataSource as
extension ViewController: UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return section == 0 ? 1 : yourList.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0{
// set your header view here
}else{
// Your normal cell configuration
}
}
}
With this, your headerview will become a part of table view but as a UITableViewCell thus it will not behave like sticky header like the normal one.

Non-floating UITableView footer

I have a UITableView with a bunch of custom cells (and each cell has a custom height and dynamic height).
I want to add a footer view to the tableview that will always be on the bottom of the tableView contentSize.
If the contentSize is bigger than the screen size so in the bottom of the tableView (you scroll down, like a regular last cell).
If the contentSize is smaller than the screen size so the footer will still be in the bottom of the screen and there will be a gap from the last cell to the footer.
I've tried to add a footer like this:
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let myFooterView = Bundle.main.loadNibNamed("MyFooterView", owner: self, options: nil)?.first as! MyFooterView
myFooterView.delegate = self
myFooterView.frame = CGRect(x: 0, y: 0, width: self.tableView.frame.size.width, height: 100)
return myFooterView
}
But the footer is floating in on the bottom when I scroll. Tha'ts not what I want to achieve.
Any idea how can I achieve a non-floating footer like I want?
Thanks! :)
Add a view as a subview to the table view and adjust the position in scrollView:didScroll:. To be able to do this, add conformance to UIScrollViewDelegate to your view controller. I think a UITabelViewController is automatically the delegate for the table view (which is also a UIScrollView).
The tricky part is the calculation of the position based on the content size, the content offset, the size of the view and the size of your footer.

Adding UIVisualEffectView to tableView in swift

I have added a UIVisualEffectView object form Object Library in Xcode on top of my tableView in the storyBoard. The first cell of the tableView is behind the UIVisualEffectView object . I want to display the first cell of the table view below the blur area. Also when i scroll the table cells it should go behind the blur area .
I tried with this code
self.tableView.contentInset = UIEdgeInsetsMake(100, 0, 0, 0)
self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(100, 0, 0, 0)
But when i scroll upside the cells won't go behind the blur area .
Help
Also I am trying to change the color of the blur region. Currently its white for light effect and black for dark effect. How do i set a specific color to blur area without effecting its behavior ?
Yours cell cant go outside of the table. So you need to extend the tableView, then add contentInset.
Also make sure that your tableView is transparent and visualEffectView is under the tableView.
Simple method is make UIVisualEffectView as your table view section header view, for example
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let blurEffect = UIBlurEffect(style: .Light)
let blurView = UIVisualEffectView(effect: blurEffect)
return blurView
}
override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44.0
}
it looks like

UITableView: changing footer view's size programmatically doesn't work

My table view's footer is a view that shows some tweets from a user, so I do not know its height until I got the tweets, I created a FooterViewController that has a method refreshTweets, I add it in viewDidLoad:
FooterViewController *controller = [[FooterViewController alloc] initWithNibName...];
[[self tableView] setFooterView:[controller view]];
[controller refreshTweets];
in refreshTweets method, I read tweets and calculate the total height, and reset view(the footer)'s height:
self.view.frame = newFrame;
but it does not make sense, the footer's height is still the height I set in Interface Builder, is there anything missing or any alternative way of doing this?
Thanks!
edit:
I can change the view's size to be small enough in interface builder, and it will be enlarged after calculating tweets' height and setting view.frame, but the table view still thinks its footer has previous height, that is, the extra space is outside of the tableview and I can only see it if I drag the table up.
myTable.tableFooterView = myFooterView;
Re-assign your footer view to the table. The table will recognize it has a new footer and re-do whatever layout needs to occur for the proper size of the footer.
In order to change the frame of footer view here is the simple tweak:-
In Swift:-
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 0.5
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footerView = UIView()
let footerChildView = UIView(frame: CGRect(x: 60, y: 0, width: tableView.frame.width - 60, height: 0.5))
footerChildView.backgroundColor = UIColor.darkGray
footerView.addSubview(footerChildView)
return footerView
}

Resources