UITextView doesn't update its contentSize - ios

I'm dynamically sizing a UITextView's height and the height of the UITableViewCell it is embedded in when its content changes. But I also have the ability to paste in predefined bits of texts.
As this pasting happens programmatically, the problem is, that after adding the selected text bit to the UITextView's text and calling my UITableView to update its cell heights, the UITextView hasn't yet updated its contentSize, which I use to calculate the cell's height.
Is there a way to force a UITextView to update its contentSize, after I programmatically add text to it?

You can use sizeThatFits: to get the correct size.
CGFloat height = ceilf([textView sizeThatFits:textView.frame.size].height);
Here is a popular open source component that makes use of it to achieve dynamic resizing based on the textview content:
https://github.com/HansPinckaers/GrowingTextView

Swift version of the answer:
var newTextViewHeight = ceil(textView.sizeThatFits(textView.frame.size).height)
textView.frame.size.height = newTextViewHeight
This piece of code might also come in handy if you want to update the superview of the UITextView
textView.superview?.frame.size.height = textView.contentSize.height + 10

Related

iOS storyboard - Auto Layout won't resize and reposition subviews

I don't want to set a Y position constraint because I need the views to be relative to each other, since I have a UITextView that has to and should dynamically change its value based on how many lines of text are in it. It does not do this though. I can FORCE it to do it by calling sizeToFit() on the UITextView, but then it overlaps the views below.
Xcode nags me to reduce ambiguity and there are red lines all over the view controller, but it doesn't make any sense for me to manually have a Y position constraint if that has to be dynamic.
So my question is how do I
1. Make my UITextView resize its height after the number of lines of text increases
2. Make the views below it get pushed down automatically so the UITextView does not overlap them.
I've read multiple answers and have managed to do 1 but not the other.
Solution in Swift:
let sizeThatFitsTextView = textView.sizeThatFits(CGSizeMake(textView.frame.size.width, textView.frame.size.height))
textViewHeightConstraint.constant = sizeThatFitsTextView.height
1) Disable the scroll of Textview from storyboard or by coding.
2) Create an outlet for textview's height constraint. Change the height constraint programmatically from textView delegate method when text change occur based on calculation.
CGSize sizeThatFitsTextView = [TextView sizeThatFits:CGSizeMake(TextView.frame.size.width, MAXFLOAT)];
TextViewHeightConstraint.constant = sizeThatFitsTextView.height;

iOS 8 self-sizing cells - wrongly calculated height for labels and Infinite scrolling

I am trying to get my cells to resize dynamically and I wanted to take advantage of new self-sizing cells in iOS 8. Now, I set constraints to edges of superview and the cells really resizes itself. The problem is that the cell has sometimes wrong height and the text is not displayed all. If the text is longer also the mistake in height of label is greater. I was experimenting if the problem is not caused by newly added margins but it doesn't work either.
When I was trying do the resizing the old way, I discovered that cell.contentView.systemLayoutSizeFittingSize gives also wrong height. There is probably something I am missing in sizing of content view in table view cell, but I can't figure out what it is.
I prepared working example with resizing text and image, for you to test out:
https://dl.dropboxusercontent.com/u/35953801/Cell%20test.zip
EDIT:
There is additional problem, when I try to implement infinite scrolling the reloadTable always scrolls it to the top. When I try to use insertRowsAtIndexPaths instead, it starts to jump to wrong position and glitch very badly.
Here is updated code to test out:
https://dl.dropboxusercontent.com/u/35953801/Cell%20test2.zip
EDIT 2:
So as #gabbler suggested, the best way to make reliable infinite scrolling is to do it the old way. I haven't found way how to use the storyboard prototype cells to measure them in heightForRow method. If you try something like this, you get stuck in infinite loop, because dequeReusableCell actually calls heightForRow:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
var cell = tableView.dequeueReusableCellWithIdentifier("CellProgrammatical", forIndexPath: indexPath) as TableViewCell
It might be possible to set it up with Nib resource for cells, but best will be probably to save yourself a headache, follow https://github.com/smileyborg/TableViewCellWithAutoLayout and set up constraints programatically.
There is one thing that needs to be done for iOS8. It adds extra constraint UIView-Encapsulated-Layout-Height to 44 initially, which breaks the cell if it is taller than that. It's necessary to set all the vertical constraints to lower priority (999).
Here is the example once again, with working infinite scrolling:
https://dl.dropboxusercontent.com/u/35953801/Cell%20test3.zip
Thanks for help
FWIW I had this problem when I set constraints between my label and the cell margins instead of the cell itself. Margins are weird and don't appear to be correctly taken into account when calculating preferredMaxLayoutWidth.
Changing my constraints to be attached to the cell itself fixed the problem.
There is a label in your cell which has preferred width being set, remove the setting seems to fix the problem.
Since you have already make the label width to be equal with the cell width, there is no need to set the Preferred Width.
And try to remove these code.
self.tableView.estimatedRowHeight = 600;
tableView.reloadData()
which seems to be redundant here.
I use the following code to calculate height
Here 20 is my font size
UIFont *font = [UIFont systemFontOfSize:20];
NSDictionary *userAttributes = #{NSFontAttributeName: font,
NSForegroundColorAttributeName: [UIColor blackColor]};
NSString *text = self.userModel.quote;
CGSize textSize = [text sizeWithAttributes: userAttributes];
ht = 20;
if (textSize.width > [FunctionUtils getViewWidth]) {
ht = (textSize.width/([FunctionUtils getViewWidth]));
ht = (ceil(ht))*20+35;
}

runtime expansion of uitableview footerview using autolayout

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);

TextLabel shifts up when detailTextLabel becomes multiline

i'm currently making an app where the suer selects an MKMapView annotation and then the title of the annotation(pin) is set to a detailtextLabel in a Right Detail UITableViewCell.
My Problem is that when the text is large, the detailTextLabel becomes multiple lines. When this happens the TextLabel of the cell(the one the left) shifts up. Heres What I've tried:
In the cellForRowAtIndexPath Method, I tried adjusting the frame through the following code:
CGRect frame = cell.textLabel.frame;
frame.origin.y = cell.frame.size.height/2;
cell.textLabel.frame = frame;
Where cell is a UITableViewCell that is set to right detail
Subclass the cell and tun try to adjust the frame in the -(void)layoutSubviews
How do I stop it from going up and keep it at the center of the cell?
If you want to do a custom layout of UITableViewCell, then you need to add your custom UI elements to its -[UITableViewCell contentView] instead of trying to modify geometry of standard UI elements that are created by default.
So, in your case, you need to add two UILabels and set their position so that:
Title label will not move at all
Detail text label will be also multiline
In this way you'll be able to solve this problem!
Please try to make the font size adjustable amount to the text.
I think you can use,
cell.detailTextLabel.adjustFontSizeToWidth = Yes;
And also set the numberOfLines to 0.
Hope that solves the purpose.

custom cell height based on objects inside the cell

I made a custom cell, placed a UITextView inside it and I want to change the height of the cell based on the length of UITextView's text length. I know how to statically change the cell height using heightForRowAtIndexPath, but I can't put my head around doing it dynamically, based on content.
I have read about a dozen topics on this forum and on several other, but I didn't find the answer I was looking for.
Any ideas?
in heightForRowAtIndexPath
float height = [cell.textView.text sizeWithFont:cell.textView.font constrainedToSize:CGSizeMake(cell.textView.frame.size.width, 10000)].height;
return height;
10000 it's max height of cell, actually you can set max integer value
In your textViewDidChange method, call
[tableView beginUpdates];
[tableView endUpdates];
This will trigger heightForRowAtIndexPath to recalculate all your cell sizes each time the user types a letter. Then in heightForRowAtIndexPath, you can then calculate the necessary size for your cell.
The sizeWithFont method will return the size needed to display the text in a UILabel, which is slightly different than that for a UITextView due to content insets, line spacing, etc. I've used a somewhat hacky solution in the past. If you create a temporary UITextView, set it's text, and use [UIView sizeThatFits:constraintSize] to get the size that will fit all its content within the constraints. (The documentation on this method is a little unclear - take a look at this answer for more info: question about UIView's sizeThatFits)
UITextView *temp = [UITextView alloc] initWithFrame:someArbitraryFrame];
temp.font = DISPLAY_FONT
temp.text = cell.textView.text;
//This gets the necessary size to fit the view's content within the specified constraints
CGSize correctSize = [self.temp sizeThatFits:CGSizeMake(CONSTRAINT_WIDTH, CONSTRAINT_HEIGHT)];
return correctSize.height
In interest of efficiency, you should probably store/lazy-load the temporary textView so that you're not creating a new UITextView for every cell, but rather re-using the same one to calculate height for different text.

Resources