How to stop uitableview after all the rows are done - ios

The title might not be so clear but it was really hard to express it so I will do my best to explain here.
Look at this image:
I want the UITableView to stop entirely after all the rows are done. Meaning after the footer there should be no more white view.
I think what is causing this is the constraints that i have put. But I need my constraints to make it look good in all possible sizes etc. So my question is how can something like this be solved? I am using SWIFT.

This worked like a charm. Thank you #tahavath for pointing me to this direcation
override func viewWillLayoutSubviews() {
dispatch_async(dispatch_get_main_queue(),{
//This code will run in the main thread:
var frame = self.tableView.frame;
frame.size.height = self.tableView.contentSize.height;
self.tableView.frame = frame;
});
}
EDIT
It does work however this will be a problem if the table is bigger than the screen. When trying to run this on iphone 4s it looked terrible.

Try the following code...
Just include the following code... give your table view an automatic dimension.
override func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
return UITableViewAutomaticDimension;
}
Also add the following code where the view loads
tableView.estimatedRowHeight = 89
tableView.rowHeight = UITableViewAutomaticDimension

Why don't you set
tableView.backgroundColor = UIColor.clearColor()
and then you set
cell.backgroundColor = UIColor.whiteColor()
Edit (not sneaky approach)
tableView.frame.size.height = tableView.contentSize.height
But there is a problem, if the screen height is not enough to contain tableView content, it will be bad.
Here's the right way
tableView.frame.size.height =
tableView.contentSize.height > UIScreen.mainScreen().bounds.height ? UIScreen.mainScreen().bounds.height : tableView.contentSize.height

Because the table thinks there is a footer to show, it doesn't display any cells beyond those you explicitly asked for.
Set a zero height table footer view in your viewDidLoad method
self.tableView.tableFooterView = UIView(frame: CGRectZero)
Or
tableView.tableFooterView = UIView()

override func viewDidLoad() {
super.viewDidLoad()
self.tableView.tableFooterView = UIView()
}

Related

Auto size view on tableViewController

I am having a view at the top of my tableViewController. The view is called viewBackground. It is there to show the post the user clicked on and the table cells will show the comments.
What I want to do is to resize the viewBackground depending on the size of the label theLabel. I have done this in the cells by setting the label in the cells to 0 lines and by implementing this little code :
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 205
How do I do the same with my viewBackground?
This is a picture of the tableView:
You can create table view with two sections. First section will contain selected post; second section - comments. If you need, i can provide some code.
Added this code and it helped!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let headerView = tableView.tableHeaderView {
let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
var headerFrame = headerView.frame
//Comparison necessary to avoid infinite loop
if height != headerFrame.size.height {
headerFrame.size.height = height
headerView.frame = headerFrame
tableView.tableHeaderView = headerView
}
}
}
I suggest to make a header for your current tableView -instead of adding an external view above it-, and then you can follow this answer, it should helps you of how you can calculate the label's string height and returning it in the tableView(_:heightForRowAt:) method.
Hope this helped.

Swift - How dynamically fixed UITableViewHeaderFooterView height size?

I have just tried below lines of code, but I doesn't work correctly. I wonder that how can I provide this case for header or footer programmatically, may be using autolayout I don't know exactly which one solved my problem. I'm using xib file both of UITableViewHeaderFooterView.
If someone explain I would be great.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Dynamic sizing for the footer view
if let footerView = tableView.tableFooterView {
let height = footerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
var footerFrame = footerView.frame
if height != footerFrame.size.height {
footerFrame.size.height = height
footerView.frame = footerFrame
tableView.tableFooterView = footerView
}
}
}
The UITableView calculates the HeaderFooter before they displayed, you can't update that height without calling reloadData() again.
Have you tried to use AutoLayout on those HeaderFooterViews?
I would set up the view from xib with the right constraint, and then:
self.tableView.estimatedSectionHeaderHeight = 100
self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension

How can I create a table view cell that changes its height dynamically to fit its content?

I'm displaying some markdown that the user wrote in a table view. The way I display these markdown texts is to convert the markdown to HTML and then display the HTML on a UIWebview. Because, well, I can use some cool CSS.
After some research, I found that I can set estimatedRowHeight to some stuff and set rowHeight to UITableViewAutomaticDimension. I also found out how to set a UIWebView's frame so that it fits its content here:
So I wrote this code:
var results: [Entry] = []
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return results.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("resultCell")
let webView = cell?.contentView.viewWithTag(1) as! UIWebView
let entry = results[indexPath.row]
webView.loadHTMLString(entry.htmlDescription, baseURL: nil)
webView.delegate = self
return cell!
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
tableView.reloadData()
}
override func viewDidLoad() {
tableView.estimatedRowHeight = 400
tableView.rowHeight = UITableViewAutomaticDimension
}
func webViewDidFinishLoad(webView: UIWebView) {
let height = CGFloat(webView.stringByEvaluatingJavaScriptFromString("document.height")!.toFloat()!)
var frame = webView.frame
frame.size.height = height
webView.frame = frame
webView.scrollView.contentSize = frame.size
webView.scrollView.frame = frame
print(frame)
}
The results array is just the stuff I'm displaying. As you can see, I want to display the result's htmlDescription in each web view.
And in the webViewDidFinishLoad delegate method, I just did what the aforementioned post did: evaluating some JS and updating the web view's frame.
However, the behaviour is far from what I expected.
Firstly, the table view cells' heights are the default heights.
When I try to scroll the web views, I can't. They simply bounce back.
The console says that:
Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a tableview cell's content view. We're considering the collapse unintentional and using standard height instead.
I don't know what is suggesting that the table view cell's height should be 0.
The console also prints the new frames of the web views. I only have 3 web views but there are 6 frames:
(0.0, 0.0, 320.0, 315.0)
(0.0, 0.0, 320.0, 363.0)
(0.0, 0.0, 320.0, 216.0)
(0.0, 0.0, 320.0, 315.0)
(0.0, 0.0, 320.0, 363.0)
(0.0, 0.0, 320.0, 216.0)
And the height seems to be pretty correct, not the default table view cell height.
I know that I can fix this if I could either:
know the content size of the web view before the HTML is loaded, or;
reload the table view when all web views finish loading. (Since I will possibly have lots of web views, I should call reloadData as less as possible or it will e very slow.)
How can I do this?
This is not a duplicate of How to make UITableViewCell adjust height to fit its content? because that question's table view is kind of static. Its content won't change once it is loaded. But mine has a web view which will load HTML.
Just calculate height in cellForRowAtIndex and set table.rowHeight. Look at this link can help you
Insert in a tableView set to Autoresize cause a scroll issue

Expand UITableViewCell when size of contents changes

I have a UITextView inside a static UITableViewCell constrained like this:
Picture
In the viewDidLoad() method of my table view class I want to be able to change the text of the UITextView, have the UITextView change size to fit the text (I have scrolling disabled on the UITextView), and then have the UITableViewCell still be constrained as I have intended it to be. This is my attempt to do so:
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.aboutTextView.text = "Some Long String"
//implement self sizing cells
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 400.0
//set the frame of the UITextView to match the size of the text
let fixedWidth = aboutTextView.frame.size.width
let newSize = aboutTextView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.max))
var newFrame = aboutTextView.frame
newFrame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)
aboutTextView.frame = newFrame;
//This is returning the new size correctly
//Reload the tableview, nothing happens, text view remains the same size as in the storyboard
self.tableView.reloadData()
}
Although the new frame is bigger than the default size in the storyboard, nothing happens when the tableView is reloaded. I have tried setNeedsLayout() on the UITextView with no luck as well. I also tried constraining the height of the UITextView and changing the constant of the height through an IBOutlet but then the constraints break for obvious reasons. Anybody know why my code isn't working? Any better method to do what I am trying to do?
All the code about the row height should go in your tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) function. That way, whenever the table is reloaded, the code will be executed again, changing the table height.
Edit: I completely misunderstood what you were doing. In the storyboard, try adding constraints to the UITextView within the cell and give it a constraint on the height, and then make sure that the cell also has a height constraint slightly larger than that of the UITextView.

Setting tableView header's height in Swift

I am trying to set the height of a view that is on top of my prototype cell in a table view controller. I use IB to set it's height (size inspector) and set it to 61 like so (the green view is the 'header' view):
But whenever I run the app, its' height ends up being 568.0. I have an IBOutlet called testUIView for the view in my table view controller, and I do: println("testUIView Height->\(testUIView.frame.height)") and indeed ends up being 568.0 at runtime.
Here is a screenshot showing its' height at runtime:
So my question is: How can I set the view's height so it is 61 at runtime so it indeed looks like my first screenshot (size-wise)?
I tried to set its' height property inside override func viewWillLayoutSubviews() but it did not let me assign a value to the height testUIView.frame.height = CGFloat(61.0).
Any help is appreciated! Thanks in advance!
Cheers!
Here is a solution which uses section header views rather than the actual table header view:
If you'd like to use a header for you UITableView instead you can design another prototype cell in Interface Builder, make a custom class based on a UITableViewCell and assign it to the prototype cell in interface builder on the class inspector.
Then in your controller you're going to use
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
In that function you're actually going to create a reusable cell from your table view but cast as the custom cell you made for the header. You will have access to all of it's properties like a regular UITableViewCell, then you're just going to return the cell's view
return cell.contentView
Another method you're going to use is
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 61.0
}
That one is pretty self explanatory.
Swift 3.0.1
public override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 61.0
}
Swift 3/Xcode 8:
Add this in viewDidLoad():
let HEADER_HEIGHT = 100
tableView.tableHeaderView?.frame.size = CGSize(width: tableView.frame.width, height: CGFloat(HEADER_HEIGHT))
Enjoy!
The accepted answer doesn't actually answer the question. It instead offers an alternative by using the SECTION header. This question has been answered by others but I will duplicate the answer here with a few more instructions.
Loading the view
Table views are as old as iPhones and therefore you sometimes have to force it to do what you want.
First we need to load the header and manually set its height. Otherwise the view will take more height than it needs. We do this on the viewDidLayoutSubviews callback:
lazy var profileHeaderView: ProfileHeaderView = {
let headerView = ProfileHeaderView()
return headerView
}()
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
sizeHeaderToFit()
}
private func sizeHeaderToFit() {
profileHeaderView.setNeedsLayout()
profileHeaderView.layoutIfNeeded()
var frame = profileHeaderView.frame
frame.size.height = profileHeaderView.calculateHeight()
profileHeaderView.frame = frame
tableView.tableHeaderView = profileHeaderView
}
As you can see, I like to put my views inside lazy vars. This ensures that they are always created but only when I start using them.
You can also see that I'm calculating the height. In some cases, your height is fixed and therefore you can just set the frame height to a hardcoded value.
Set some priorities
We will likely see some constraint warnings appear in our debugger. This happens because the table view first forces a 0x0 size before using the size we specified above At this moment, your constraints and the height of the view are in conflict with each other.
To clear these, we simply set the constraint priorities. First you should wrap your header view components inside another view (I generally always do this for header views). This will make managing constraints much easier on your header view.
We then need to set the bottom constraint priorities to high:
containerView.setContentCompressionResistancePriority(.defaultHigh, for: .vertical)
containerView.setContentHuggingPriority(.defaultHigh, for: .vertical)
Here is a more complete example:
WARNING: Thought it is still useful as a guide for laying out your views, do not use this code if you're creating your views using nibs or storyboards.
class ProfileHeaderView: UIView {
lazy var containerView: UIView = {
let view = UIView()
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupLayout()
}
required init?(coder aDecoder: NSCoder) {
// We do this because the view is not created using storyboards or nibs.
fatalError("init(coder:) has not been implemented")
}
private func setupLayout() {
self.addSubview(containerView)
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
containerView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
containerView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
containerView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
containerView.setContentCompressionResistancePriority(.defaultHigh, for: .vertical)
containerView.setContentHuggingPriority(.defaultHigh, for: .vertical)
// Set the rest of your constraints against your containerView not self and add your subviews to your containerView not self
}
}
Here is the example of the constraints set using snap-kit:
containerView.snp.makeConstraints() { make in
make.top.equalTo(self.snp.top)
make.leading.equalTo(self.snp.leading)
make.trailing.equalTo(self.snp.trailing)
make.bottom.equalTo(self.snp.bottom).priority(.high)
}
Make sure you add your constraints to the containerView not self and use containerView to add your subviews and rest of your constraints.
It has to be one of the strangest issues in iOS.
If you do just want a fixed height, as of 2019 you can:
public override func viewDidLayoutSubviews() {
var frame = tableView.tableHeaderView!.frame
frame.size.height = 68
tableView.tableHeaderView!.frame = frame
}
Strange stuff.
In swift 4.1 and Xcode 9.4.1
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.pad {
return 75.0
} else {
return 50.0
}
}
If you use .xib file with UIVIew for your HeaderView, you can use self-sizing header like this
override func layoutSubviews() {
super.layoutSubviews()
// Manually set the view's frame based on layout constraints.
// The parent UITableView uses the header view's frame height when laying out it's subviews.
// Only the header view's height is respected.
// The UITableView ignores the view frame's width.
// Documentation: https://developer.apple.com/documentation/uikit/uitableview/1614904-tableheaderview
frame.size = systemLayoutSizeFitting(
.init(
width: frame.size.width,
height: 0
),
withHorizontalFittingPriority: .required,
verticalFittingPriority: .fittingSizeLevel
)
}

Resources