Context:
I have a pop up view that contains a tableview. I would like the pop up view to be less than the whole height of the screen but would like the tableview to scale with the number of rows it has. (If the table view contains 2 rows then it would fit those 2 rows and would not scroll, if the table view has 30 rows it will max out at the height of the screen and allow scrolling).
View Hierarchy:
- viewContainer (clear background, whole view)
-- viewPopUp (pop up container)
--- labelHeader
--- tableView
--- buttonOK
Constraints:
viewPopUp.centerY = viewContainer.centerY
viewPopUp.centerX = viewContainer.centerX
viewPopUp.leadingSpaceTo superView = 32 #750
viewPopUp.trailingSpaceTo superView = 32 #750
viewPopUp.width <= 300
viewPopUp.height <= 0.8 * viewContainer.height
labelHeader.topSpaceTo superView = 0
labelHeader.leadingSpaceTo superView = 0
labelHeader.trailingSpaceTo superView = 0
labelHeader.bottomSpaceTo tableView = 0
labelHeader.height = 33
tableView.topSpaceTo labelHeader = 0
tableView.leadingSpaceTo superView = 0
tableView.trailingSpaceTo superView = 0
tableView.bottomSpaceTo buttonOk = 0
tableView.height >= 0
tableView.height = 0 #200
buttonOk.topSpaceTo tableView = 0
buttonOK.leadingSpaceTo superView = 0
buttonOK.trailingSpaceTo superView = 0
buttonOk.bottomSpaceTo superview = 0
buttonOK.height = 35
My Question:
In my UIViewController's viewDidLoad I know exactly how many rows will need to be represented in this tableView and I would like to add an additional constraint to have the tableView scale to this number as described in the context above.
I have tried tableView.height = count * rowHeight #500 but this does not update the view. I am not sure if I am not calling something like needsUpdateConstraints or needsUpdateLayout etc.
If I add the above constraint as required then it will update the tableView as expected but I receive constraint warnings and I believe this will break if it pushes the tableView past the size of the screen which is why I wanted to add it as a non-required constraint so it will drop off after it reaches the screen height.
Does anyone have any ideas on how I can reach this desired outcome?
Thanks!
Assuming that the height of the rows are always the same, you could calculate the height of the table, check it against the view height and set this value to a referenced height constraint added to the table view.
The code would look something like:
//calculate the height
var tableHeight = numberOfRows * rowHeight
if tableheight > maxHeight {
tableHeight = maxHeight
}
self.tableHeightConstraint.constant = tableHeight //this constraint should be a iboutlet
self.view.layoutIfNeeded()
Hope it helps.
Related
I want to have a cell with a header with title and switch. When the switch is on, it should show the content expanding the cell with an animation. The header should be always visible and have a constant height.
The cell consists of a container view with 2 subviews, the header, and a body. The body has a label for the cell's content.
The cell has a delegate that tells the controller to reload the content like this:
tableView.beginUpdates()
tableView.endUpdates()
(Nothing in between. Should I specify something just to be sure?)
I have tried having a height constraint for the header and one for the body and when the switch updates its value, I update the body's constraint to the height of the label (the calculation is being done correctly). I have also tried having a single constraint for the container (constant header height + label height + padding) which also didn't work.
The controller has is the tableView's delegate. I'm not setting a estimated row height (should I?) and for heightForRowAt I'm returning UITableView.automaticDimension.
If you have any suggestion on how it could be done, even for a different view/subviews organization, please comment.
EDIT:
This was actually a silly mistake. I was updating the constraints after I was calling my delegate cell. For future reference I'm posting here the code.
func configure(with reminder: Reminder, delegate: CellUpdateDelegate) {
self.delegate = delegate
self.reminder = reminder
bodyLabel.text = reminder.body
headerTitle.text = reminder.title
let height = bodyLabel.requiredHeight + 16
containerHeight.constant = 48 + (alertSwitch.isOn ? height : 0 )
}
#objc func switching() {
let height = bodyLabel.requiredHeight + 16
containerHeight.constant = 48 + (alertSwitch.isOn ? height : 0 )
delegate?.didSelect(reminder)
}
I understand that this question has been asked here, but it didn't solve my problem as the link in the accepted answer is down and the minimal example didn't help.
Here is a picture of my custom UITableViewCell & all its constraints:
Each post contains these UI elements. The only element that could make each cell's height different is messageView, because its height depends on the string being displayed. Question is, how do I dynamically set each cell's height? Here's what I have now (Does NOT work, messageView is not shown at all):
func cellForRowAt(indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(...) as! PostCell
let message = ...
cell.messageView.text = message
return cell
}
func heightForRowAt(indexPath: IndexPath) -> CGFloat {
var cellMinimumHeight: CGFloat = 120
if let cell = tableView.cellForRow(at: indexPath) as? PostCell {
let size = cell.messageView.sizeThatFits(cell.messageView.frame.size)
cellMinimumHeight += size.height
}
return cellMinimumHeight
}
in heightForRowAt function, the if is not being executed, therefore, all cells' heights are cellMinimumHeight = 120.
How could I make each cell's height = 120 + messageView's height?
---------------------------------EDIT 1---------------------------------
Made a mistake in the picture, messageView's height is not set
When using Auto-Layout for dynamically sizing cells, you don't really need to implement sizeThatFits(...). If the constraints are setup correctly, then you only need to disable the scrolling of the UITextView.
From code:
yourTextView.scrollEnabled = false
From IB:
Select your Text View and open Attributes inspector, then
In Attributes Inspector select your UITextView(messageView) and Uncheck "Scrolling Enabled".
And then change your UITextView(messageView)'s content compression resistence priority as follows:
Horizontal = 750
Vertical = 1000
I hope this will help you.
Just disable UITextview scroll...
But here is no use of UITextview, you can use label also.
In HeightForRow tableview delegate method remove that stuff and use
return UITableViewAutomaticDimension
You have used constrain to make cell hight dynamic
form apple documentation
To define the cell’s height, you need an unbroken chain of constraints
and views (with defined heights) to fill the area between the content
view’s top edge and its bottom edge.
So for your case you need
cell's height = 120 + messageView's height?
So start from Profile Image to measure unbroken chain of constraints from Top to Bottom
Profile Image top = 10 + ImageHeight = 60 ----> 70
MessageView top = 10 + set minimum height to message say 20 one line if every cell should have message even if one word and set this height Greater than or equal to 20 make sure that you set Scroll enable = false
so message Height minimum = 10 top + 20 + 10 bottom ---> 40
Menu Stack view Height ---> 30
So all Total = 70 + 40 + 30 = 140 this default hight no cell will be less than this
Also you must set the table view’s rowHeight property to UITableViewAutomaticDimension. You must also assign a value to the estimatedRowHeight property
tableView.estimatedRowHeight = 130.0
tableView.rowHeight = UITableViewAutomaticDimension
Here is apple documentation Here
I have a parent view which has a tableview and tableviews leading,trailing,top,bottom is zero.
and I have a label to show if there are no records ... to show a message and I have vertically and horizontally centered it.
Based on content size of tableview I have to change the parentview's height and i can't directly do because there is no height property defined for parent view .. only leading,trailing,top and bottom property.
Now I have to change the bottom property value with respect to tables content size
if there are no records have to set the bottom constraint to higher value so that parent view height reduces
(Xamarin c# but constraints are same as iOS objective c or swift no change though)
if(vuParent.Frame.Height - tblMine.ContentSize.Height > 0 && !tblMine.Hidden && tblMine.ContentSize.Height!=0)
{
constraintBottomvuParent.Constant = 10 + vuParent.Frame.Height - tblMine.ContentSize.Height;
}
else if(vuParent.Frame.Height - tblMine.ContentSize.Height < 0)
{
constraintBottomvuParent.Constant = 10;
}else
{
constraintBottomvuParent.Constant = 200;
}
if there is any better way kindly let me know
Thanks
My image has the following constraints:
leading = 0
trailing = 0
top = 75
height = 250
I want my image to start from the right side of the screen and move to initial place where the constraints are set.
Here is my code:
imageLeadingConstraint.constant = 100
imageTrailingConstraint.constant = -100
imageTopConstraint.constant = 100
image.layoutIfNeeded()
UIView.animate(withDuration: 1.0) {
self.imageLeadingConstraint.constant = 0
self.imageTrailingConstraint.constant = 0
self.imageTopConstraint.constant = 75
self.image.layoutIfNeeded()
}
The issue is that the image is moving to initial place, but not using an animation.
Is my code right ? How come the image just appears in the new position and not animating ?
You should call layoutIfNeeded for imageView's superview.
self.image.superview.layoutIfNeeded()
I'm not sure that I understood you, but:
1) Change constraints (second time) outside of animation block.
2) use self.image.superview?.layoutIfNeeded() instead
So I am currently very new to ios programming and I am having a bit of an issue with my table view. First I call my web API to get the data I required to populate my table View. Using the data I am able to calculate the number of rows and sections that would exist on my table view. After that I calculate the correct height of my table view. I change the height of my table view and reload the table. It looks like this
func tableViewHight(numberOfRows : Int)
{
let sectionHeight = CGFloat(30) * CGFloat(numberOfSections - 1)
//This is the height of all the sections in my tableview put together(except the first section since its height will always be 0)
let tableviewMaximumHeight = UIScreen.mainScreen().bounds.size.height - self.myTableView.frame.origin.y
//Maximum height would be the distance from the y position of my table view, all the way to the bottom of the device.
if(tableviewMaximumHeight <= (cellRowHeight * CGFloat(numberOfRows) + sectionHeight))
{
self.myTableView.frame.size.height = tableviewMaximumHeight
}
else
{
self.myTableView.frame.size.height = cellRowHeight * CGFloat(numberOfRows) + sectionHeight
}
self.myTableView.reloadData()
}
My table view is able to change height and reload data perfectly. The only Problem is that I am not able to reach the bottom of my table view. I do not know what else to do. I have already check that my table view has
myTableView.scrollEnabled = true
myTableView.scrollTop = false
If you guys have any advice, I would appreciate it :).
The problem when you manually set the height of a UITableView is that you'll then also have to manually set its content height.
Use Autolayout instead to set the height.
Have an IBOutlet for an NSLayoutConstraint variable (say, tableHeightConstraint) that sets the height of your table, then, in your code:
tableHeightConstraint.constant = tableviewMaximumHeight
or
tableHeightConstraint.constant = cellRowHeight * CGFloat(numberOfRows) + sectionHeight
I found the main root of my problem. The issued lied in my maximumTableviewHeight variable. The problem was that I would the value for this variable in the view did load and apparently the y position of the my table view would always give zero in the view did load. Once the maximum table view height was resolve. The application worked like a a charm.