I am having abit of trouble here trying to make this post description label to grow and shrink based on content size on IOS9. I have a view (I will refer it topView) that I am using as a header for the tableview (So when I scroll up the header disappears). Inside the topView, there are a bunch of stack views. I wish to grow and shrink the post description label in height based on content size. I do know how to do it in simple case where everything is inside the prototype cell (i.e. set estimated row height and set uitableviewautomaticDimensions, set sizetoFit on label and change number of lines to 0). However, this is a different case because the post description label is not really inside the cell, it is in its view before the table view cells.
Note that all items in the view has static height except the postdescription label. Post description label is inside a stack view that is pinned only left and right (So that top and bottom would grow?). Also, the main stack view that contains all elements is pinned towards the four sides with the topview that contains the main stack view also pinned towards the four sides. With this setup, I would expect the topview to grow and shrink based on the content size. However, I do not see that in the output. I dont know if it is the stackview that is holding the label refusing to grow or the top view refusing to grow to allow more space for the stackview for the label. Thanks
UPDATE
Thanks Riadluke, I tried doing something as suggested which is resizing the headerview after calculating the required height. I have placed the following code in viewDidLayoutSubview and it works with an issue
postDescriptionLbl.sizeToFit()
let headerView = commentTableView.tableHeaderView!
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let height = TopStackView.frame.size.height + ImageStackView.frame.size.height + postDescriptionLbl.frame.size.height + SpacerStackView.frame.size.height + BottomStackView.frame.size.height
headerView.frame.size.height = height
commentTableView.tableHeaderView = headerView
The issue I now have with this method is that when the view controller appears, I can physically see the postDescription label height grow from the default height in storyboard to the required height. For example, when the VC first appears, I see a line of label with some string being cut off, however after 0.5 second, the headerview and the label grow to the size that I wanted. I know this would be expected because I was calling the manipulation after the viewDidLayout Subview. I was wondering if there is a better way such that I dont see that transition and the view appears to be the right height straight away. Ie. let the view know exactly how high the label is to determine how high the headerview needs to be before appearing on screen?
I'm afraid the view set as tableHeaderView of a UITableView does not get resized automatically. Its height will be fixed to the height it had in IB.
What you have to do is set its size manually and then reassign it as the tableHeaderView so it is displayed in the height you want.
It could take only few lines since you're using autolayout.
You can try this code right after you've set the header view's contents:
//for the target size you have set the width as your tableView's width when it is already displayed on screen.
//note that when it is accesed inside viewDidLoad the tableView's bounds
//may be different to the actual bounds it will be displayed with,
//here I am just using the screen bounds
let targetSize = CGSize(width: UIScreen.mainScreen().bounds.width, height: 10000)
//set the tableHeader's size to its size after its layout constraints are resolved
tableHeader.bounds.size = tableHeader.systemLayoutSizeFittingSize(targetSize)
//reassign it as the tableHeaderView to update the height it will be displayed in
tableView.tableHeaderView = tableHeader
After many many attempts, I could not get anything to work with the original setup. The best I achieved was to resize it after view did appear which is not idea as you see the previous layout.
It is now working with a complete different approach. I have created two prototype cell and have one as "HeaderViewCell" and implemented the following functions
commentTableView.estimatedSectionHeaderHeight = 400
commentTableView.sectionHeaderHeight = UITableViewAutomaticDimension
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
Everything works like a charm after that.
Related
I'm trying to create layout that it structured like this:
- View
-- ScrollView
--- ContentView
---- CustomView
---- CustomView
---- TableView
---- CustomView
The tableView itself is auto-resizable using "invalidateIntrinsicContentSize" and when I add items - the height of the tableview changes, pushing the custom view below it further down.
Once enough items are added I the bottom custom view is hidden and the scroll doesn't work.
important fact - the bottom custom view doesn't have a bottom constraint. It is pushed down by the it's top constraint to the tableView.
If I do set a bottom constraint - the table view will no longer be dynamically resized.
The intended behaviour:
When a user adds items to the list and the list gets too big the ContentView will be scrollable so the user can scroll to see the bottom view.
The actual behaviour:
When a user adds items to the list and the list gets too big, the bottom view is pushed down and outside of sight and content is not scrollable.
What is happening and how can I fix it?
Below is what I think what is happening.
Since you are using UITableView, it has its own scroll view. So when the UITableView list gets too big, UITableView itself becomes scrollable rather than ScrollView's contentView becoming scrollable.
To achieve what you need, you would have to make the UITableView not scrollable and use the intrinsicHeight of the UITableView to get the actual height of UITableView along with all the items. If you have items with varying heights, it will be a problem because you won't know the height before rendering. With same height for all the rows, you can get the total height of the UITableView and set the height constraint to that value. This will increase the contentSize of the outer ScrollView, making it scrollable.
Apart from UITableView, you can also use UIStackView. This is because you are not using the reusing capabilities of UITableView anyways. Managing the datasource and delegates should not be a big problem.
You can create a constraint for tableview height, And take its reference to your swift file, by dragging it as you take other views. Now in your code, Just do this
tableViewHeightConstraint.constant = tableViewNoOfItems * tableViewCellHeight;
if you have set other constraints perfectly inside scrollview, It should work perfectly. Means TableView should have top, bottom, left, right margined constraints from the ScrollView.
try this code
tblViewHeight.constant = CGFloat( tableview row count * 45 )
var size = contentView.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
if size.height < scrollView.frame.size.height
{
size = scrollView.frame.size
}
contenViewHeight.constant = size.height - scrollView.frame.size.height
scrollView.contentSize.height = contenViewHeight.constant
What I think you could do is:
Disable tableView's scroll tableView.isScrollEnabled = false
Every time a user adds items to the list, reload the tableView
Also using UIStackView with vertical axis and .fillEqually distribution as a Content View would be much more convenient as you won't need to set any positional constraints to your views, but may need to set height constraints if intrinsic content size can't be determined by the engine
I'm having a problem with my table view cells as they do not adjust automatically with its content.
I have a label for a title and another label for a name. There is a text view below the two labels which is never displayed when the simulator runs.
This is
what the Table View Cell is supposed to look like, however, this is what the Table View Cell displays.
I have pinned all elements inside the table view cell to the content view using constraints. I read up that adjusting the table view cell height itself will not work, so, I have to adjust the height from the table view itself.
It is set to automatic but it is not adjusting as seen here. I have also tried to set the estimated height to automatic but to no avail. The only solution was to set a custom height but it would look extremely weird if the text view contains only a few text as there would be a large white space. I did not add any code at all to adjust the size.
These are the following constraints:
Table View
Name Label
Title Label
Text View
First You need to add height constraint for textview and add its IBOUTlet then you need to override the updateconstraint of cell and do following in update constraints method.
override func updateConstraints() {
super.updateConstraints()
self.textViewHeightConstraint.constant = self.textView.contentSize.height
}
and also for name label add bottom constraint.
By default the UITextView will not resize itself to fit its content. While you could use #Waqas Sultan approach, I would recommend to let the textView size itself. To achieve that, just use this:
textView.isScrollEnabled = false
Or, in storyboards, find the Scroll Enabled attributed and uncheck it.
This would make textView to size itself.
However, from the constraints you show it is hard to tell if there are really enough constraints to determine the proper frames for all the content - there are constraints related to Review label, but who knows how you constrained that label.
Not seeing all the relevant constraints in the tableView cell, I cannot guarantee that this will be enough to make it work as you expect (you might have forgotten about just a single one constraint, and it might be messing up your whole layout).
Hey buddy i would like you to try this way.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == desiredIndexPath { // the index where you want automatic dimension
return UITableViewAutomaticDimension
} else {
return 100 // the height of every other cell.
}
}
note: Make sure that you do not give height to the label. Otherwise the label wont expand according to content.
A lot of the solutions I've seen here include changing the cell's background to an image and using sections for rows rather than just rows themselves. I'm looking to have only two sections and have each cell expand in height on tap, so neither of those solutions would work.
I saw one solution includes setting the frame of the cell in the layoutSubviews() function like so:
override func layoutSubviews() {
super.layoutSubviews()
self.frame = CGRectOffset(self.frame, 0, 10);
}
When I do this however, it only gives margin to one cell and that's only when I tap on the cell.
Is there a surefire way to add spacing in between UITableViewCells without being hacky and breaking the cell layouts in the process?
I did this yesterday pretty easily with auto layout.
I set the background of the cell and it's content view to clear, then I created a new view and setup constraints all around it and put my labels inside of it. The height changes dynamically based on the label so I needed to use UITableViewAutomaticDimension for the row height and give it an estimated row height as well.
I don't see why this wouldn't work for expanding it on a tap as well, you just might have to reload the cell.
make the cell and it's contentView transparent
contentView addSubview customContentView and layout your cell on customContentView
customContentView pin to contentView top leading trailing with offset 0 but pin to bottom with offset 10 //the margin height
I'm hiding rows that are completed in a collectionView.
I call cell.hidden = isCellHidden in cellForItemAtIndexPath when needed.
After I hide 10 rows there is plenty of empty space left and I'd like to trim down the size of the collectionView to only fit the rows that are not hidden.
The collectionView's design is kind of like a tableView.
I know with the tableView all I had to do to achieve this is set:
func section1VisibilityButton(sender: UIButton){
isCellHidden = !isCellHidden
self.tableView.reloadData()
self.tableView.contentSize.height = CGFloat(500)
}
with a collectionView when I try this it will resize it correctly but as soon as I try to scroll down it resizes itself back to the original height including the cells hidden (the cells layer is still hidden but there's tons of empty space bellow the last visible row as if they were visible)
For your issue, there are two options to change the frame of your collectionView/tableView.
If you are using autolayout, you need to create IBOutlet of bottom constraint or IBOutlet of constant height constraint of your tableView (anyone of these constraints, which you are using).
After reload tableView data you need to update constraint by calculating its height.
Suppose you are using constant height constraint and your calculated height is 150(e.g. 3 rows and 50 height of each row).
constraintTableViewHeight.constant = 150;//this will change height
self.view.layoutIfneed(); // this will apply updated constraints to whole view
If you are not using autolayout, you can manually change the height by changing tableView.frame property.
I have a UIView that is a footerview of a uitableview. At run time, the user enters text into a uitextview within the footerview that should adjust to the size of the text content with a height constraint in autolayout.
All other objects in the view (labels, imageviews) have appropriate constraints to accommodate the expansion of the textview.
HOWEVER the height of the overall footerview will not change size, and it is impossible to use autolayout on the tableview footerview height.
Does anyone have a solution? Thanks
Haven't found an actual, elegant, solution yet, but I've postponed fixing this by using a workaround:
Setting the frame of the view used as a footer to be as large as you might possible need. In my case this meant giving it about 60px of spare vertical room. Since it's the footer and there's nothing below it to reposition the user won't be affected by the workaround.
The contents of the footer view are pinned to the top and have enough space to expand when needed.
For the record: my view is loaded from a nib file.
Although in theory the size one gives to the top level view in interface builder is just for design-time and the runtime size should be calculated based on constraints and the resulting intrinsic size, for this specific case I found the height stays the same as it was in IB.
We can change the height of the footer view run time by the following code:
func methodToChangeTableViewFooterHeight()
{
var footerView:UIView = self._tableView.tableFooterView! as UIView
var frame:CGRect = footerView.frame
frame.size.height = self.heightCollectionCS.constant + 10
footerView.frame = frame
self._tableView.tableFooterView! = footerView
}
Here , self.heightCollectionCS.constant is the height constraint for our Collection View.
We can use text content height on that place.
You may try to set again the footer view each time you footer height changes, to inform the table it should change the footer height. Or use inset. From within the footer view:
SetNeedsLayout()
LayoutIfNeeded()
ownertable.TableFooterView = this
Sorry about that, misread that question long ago. You can access the footer directly through the tableview's property tableFooterView.
What you could do is create your default footer in a xib or in your viewDidLoad:. Once you need to increase the size of the footer, you can pull out the UIView from that property and edit its frame if necessary to make it larger.
So make sure the tableFooterView gets assigned a UIView because it is nil by default. To just make the height taller, you can use self.tableView.tableFooterView.frame = CGRectMake(whatever rect you need);