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.
I have a register user screen for an app that uses a UITableView to create a list of required fields. Each section has the thing (i.e. "Username", "Email", etc) in the header, a text field, and a footer to display messages pertaining to that field, such as "Username too short." I have found two different ways of updating this text on the fly. The first way is to create an action for whenever the user types into those text fields and in that I call the following code:
UIView.setAnimationsEnabled(false)
self.tableView.beginUpdates()
if let containerView = tableView.footerView(forSection: section) {
containerView.textLabel!.textColor = UIColor.red
containerView.textLabel!.text = "Username too short"
containerView.textLabel!.font = UIFont(name: containerView.textLabel!.font.fontName, size: 12)
containerView.sizeToFit()
}
self.tableView.endUpdates()
UIView.setAnimationsEnabled(true)
This works, updating the footer when I type into the text box (I update the text and message depending on what is needed). However, when I scroll down and for example the username section unloads, when I scroll back up the "Username too short" text is gone. When I again type into the text field it reappears. I have also had issues with this updating it when the section isn't rendered (as expected). Thus, I tried overriding the tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? method in the following way:
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let view = UIView(frame: CGRect(x: 8, y: 0, width: 100, height: 40))
let label = UILabel(frame: view.frame)
label.text = "Username too short"
label.sizeToFit()
view.addSubview(label)
view.frame = CGRect(x: 8, y: 0, width: 100, height: 20)
return view
}
Again updating the text appropriately and not calling the previous code. This meant that I had to call tableView.reloadData() every time the user typed in a character. This also worked, but it caused the app to stop letting the user type in to the text field. They would have to click on it again to start typing after every character. I have tried countless ways of merging the two methods, attempting to call the former code in the tableView, but I found that whenever I returned a custom view tableView.footerView(forSection: <anything>) would return nil and if I called the tableView method itself, I would have infinite recursion. I also tried many methods to try to get just the footer to reload (thus not losing the text view focus), but none of them seemed to work.
How can I get the footer to update, persist when I unload the section (and as an extension be able to update it while the section isn't loaded), and not lose focus on the text field?
I apologize in advance as I am a bit new to app development. For some context, I have a static table and I am referring to the headers and footers of each section, not the table footer.
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.
I have recently migrated some code to new iOS 11 beta 5 SDK.
I now get a very confusing behaviour from UITableView. The tableview itself is not that fancy. I have custom cells but in most part it is just for their height.
When I push my view controller with tableview I get an additional animation where cells "scroll up" (or possibly the whole tableview frame is changed) and down along push/pop navigation animation. Please see gif:
I manually create tableview in loadView method and setup auto layout constraints to be equal to leading, trailing, top, bottom of tableview's superview. The superview is root view of view controller.
View controller pushing code is very much standard: self.navigationController?.pushViewController(notifVC, animated: true)
The same code provides normal behaviour on iOS 10.
Could you please point me into direction of what is wrong?
EDIT: I have made a very simple tableview controller and I can reproduce the same behavior there. Code:
class VerySimpleTableViewController : UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = String(indexPath.row)
cell.accessoryType = .disclosureIndicator
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let vc = VerySimpleTableViewController.init(style: .grouped)
self.navigationController?.pushViewController(vc, animated: true)
}
}
EDIT 2: I was able to narrow issue down to my customisation of UINavigationBar. I have a customisation like this:
rootNavController.navigationBar.setBackgroundImage(createFilledImage(withColor: .white, size: 1), for: .default)
where createFilledImage creates square image with given size and color.
If I comment out this line I get back normal behaviour.
I would appreciate any thoughts on this matter.
This is due to UIScrollView's (UITableView is a subclass of UIScrollview) new contentInsetAdjustmentBehavior property, which is set to .automatic by default.
You can override this behavior with the following snippet in the viewDidLoad of any affected controllers:
tableView.contentInsetAdjustmentBehavior = .never
https://developer.apple.com/documentation/uikit/uiscrollview/2902261-contentinsetadjustmentbehavior
In addition to maggy's answer
OBJECTIVE-C
if (#available(iOS 11.0, *)) {
scrollViewForView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
This issue was caused by a bug in iOS 11 where the safeAreaInsets of
the view controller's view were set incorrectly during the navigation
transition, which should be fixed in iOS 11.2. Setting the
contentInsetAdjustmentBehavior to .never isn't a great workaround
because it will likely have other undesirable side effects. If you do
use a workaround you should make sure to remove it for iOS versions >=
11.2
-mentioned by smileyborg (Software Engineer at Apple)
You can edit this behavior at once throughout the application by using NSProxy in for example didFinishLaunchingWithOptions:
if (#available(iOS 11.0, *)) {
[UIScrollView appearance].contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
Here's how I managed to fix this issue while still allowing iOS 11 to set insets automatically. I am using UITableViewController.
Select "Extend edges under top bars" and "Extend edges under opaque bars" in your view controller in storyboard (or programmatically). The safe area insets will prevent your view from going under the top bar.
Check the "Insets to Safe Area" button on your table view in your storyboard. (or tableView.insetsContentViewsToSafeArea = true) - This might not be necessary but it's what I did.
Set the content inset adjustment behavior to "Scrollable Axes" (or tableView.contentInsetAdjustmentBehavior = .scrollableAxes) - .always might also work but I did not test.
One other thing to try if all else fails:
Override viewSafeAreaInsetsDidChange UIViewController method to get the table view to force set the scroll view insets to the safe area insets. This is in conjunction with the 'Never' setting in Maggy's answer.
- (void)viewSafeAreaInsetsDidChange {
[super viewSafeAreaInsetsDidChange];
self.tableView.contentInset = self.view.safeAreaInsets;
}
Note: self.tableView and self.view should be the same thing for UITableViewController
This seems more like a bug than intended behavior. It happens when navigation bar is not translucent or when background image is set.
If you just set contentInsetAdjustmentBehavior to .never, content insets won't be set correctly on iPhone X, e.g. content would go into bottom area, under the scrollbars.
It is necessary to do two things:
1. prevent scrollView animating up on push/pop
2. retain .automatic behaviour because it is needed for iPhone X. Without this e.g. in portrait, content will go below bottom scrollbar.
New simple solution: in XIB: Just add new UIView on top of your main view with top, leading and trailing to superview and height set to 0. You don't have to connect it to other subviews or anything.
Old solution:
Note: If you are using UIScrollView in landscape mode, it still doesn't set horizontal insets correctly(another bug?), so you must pin scrollView's leading/trailing to safeAreaInsets in IB.
Note 2: Solution below also has problem that if tableView is scrolled to the bottom, and you push controller and pop back, it will not be at the bottom anymore.
override func viewDidLoad()
{
super.viewDidLoad()
// This parts gets rid of animation when pushing
if #available(iOS 11, *)
{
self.tableView.contentInsetAdjustmentBehavior = .never
}
}
override func viewDidDisappear(_ animated: Bool)
{
super.viewDidDisappear(animated)
// This parts gets rid of animation when popping
if #available(iOS 11, *)
{
self.tableView.contentInsetAdjustmentBehavior = .never
}
}
override func viewDidAppear(_ animated: Bool)
{
super.viewDidAppear(animated)
// This parts sets correct behaviour(insets are correct on iPhone X)
if #available(iOS 11, *)
{
self.tableView.contentInsetAdjustmentBehavior = .automatic
}
}
I can reproduce the bug for iOS 11.1 but it seems that the bug is fixed since iOS 11.2.
See http://openradar.appspot.com/34465226
please make sure along with above code, add additional code as follows. It solved the problem
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) // print(thisUISBTV.adjustedContentInset)
}
Also if you use tab bar, bottom content inset of the collection view will be zero. For this, put below code in viewDidAppear:
if #available(iOS 11, *) {
tableView.contentInset = self.collectionView.safeAreaInsets
}
In my case this worked (put it in viewDidLoad):
self.navigationController.navigationBar.translucent = YES;
Removing extra space at top of collectionView or tableView
if #available(iOS 11.0, *) {
collectionView.contentInsetAdjustmentBehavior = .never
//tableView.contentInsetAdjustmentBehavior = .never
} else {
automaticallyAdjustsScrollViewInsets = false
}
Above code collectionView or tableView goes under navigation bar.
Below code prevent the collection view to go under the navigation
self.edgesForExtendedLayout = UIRectEdge.bottom
but I love to use below logic and code for the UICollectionView
Edge inset values are applied to a rectangle to shrink or expand the
area represented by that rectangle. Typically, edge insets are used
during view layout to modify the view’s frame. Positive values cause
the frame to be inset (or shrunk) by the specified amount. Negative
values cause the frame to be outset (or expanded) by the specified
amount.
collectionView.contentInset = UIEdgeInsets(top: -30, left: 0, bottom: 0, right: 0)
//tableView.contentInset = UIEdgeInsets(top: -30, left: 0, bottom: 0, right: 0)
The best way for UICollectionView
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: -30, left: 0, bottom: 0, right: 0)
}
Delete this code work for me
self.edgesForExtendedLayout = UIRectEdgeNone
if #available(iOS 11, *) {
self.edgesForExtendedLayout = UIRectEdge.bottom
}
I was using UISearchController with custom resultsControllers that has table view. Pushing new controller on results controller caused tableview to go under search.
The code listed above totally fixed the problem
I have a simple UITableViewController with a set of data, employee first name and last name. I have created a custom header prototype cell with just one Label to set the header title.
However the issue is that, when ever user "Long Press" on the header cell on the table, the App Crashes.
In the attached screen shot, Header0, Header1, Header3, Header4 when "Long Press"ed, the app crashes.
However, the header section marked in the red oval is a simple UIView for header but without any Label or any control. The strange thing is if the user "Long Press" this empty header, the app won't crash.
The code for header view
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if ( section == 2 ){
var emptyView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.bounds.width, height: 50))
return emptyView
}
let cell = tableView.dequeueReusableCellWithIdentifier("HeaderCell") as! UITableViewCell
let c = cell as! HeaderTableViewCell
c.contentView.backgroundColor = UIColor.darkGrayColor()
c.headerTextLabel.text = "Header" + toString(section)
return c.contentView
}
Wondering what is happening. I have recently updated Xcode to 6.3 which has Swift 1.2. How to fix this issue?
Any help is much appreciated
Within the
I got it fixed by removing gestures on the content view (c.contentView) of cell.
if let recognizers = c.contentView.gestureRecognizers
{
for recognizer in recognizers {
c.contentView.removeGestureRecognizer(recognizer as! UIGestureRecognizer)
}
}
You can also try the solution from here: https://stackoverflow.com/a/31877323/67667 i.e. disable user interactions for the header cell:
c.contentView.userInteractionEnabled = false