I create a xib file and it includes a UITextView, I add a height constraints for UITextView. When the UIViewController load the xib view I want to change the UITextView height based on the text height. I create an IBOutlet namedheightConstraints for the height constraints and textView for UITextView. I set it in
- (void)viewWillLayoutSubview {
CGFloat height = [self getTextHeight:self.textView];
self.heightConstraints.constant = height;
[self.textView layoutIfNeeded];
}
But it does not change the height of UITextView. Can anyone help?
Can you try
- (void)viewWillLayoutSubview {
self.heightConstraints.constant = self.textView.contentSize.height;
[self.textView layoutIfNeeded];
}
Implement like this, you are required to confirm the UITextView delegate method to change it's height dynamically
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
[self updateTextViewSize:textView];
return YES
}
- (void)updateTextViewSize:(UITextView*)aTextView{
CGFloat height = [self getTextHeight:aTextView];
self.heightConstraints.constant = height;
[self.textView layoutIfNeeded];
}
- (CGFloat)getTextHeight:(UITextView*)aTextView{
/*
calculate frame based on the text and return
*/
}
Related
I have a UIView in .xib file which I'm loading at runtime and setting it as tableHeaderView of UITableView. I have a UILabel in my xib file which can grow dynamically with fixed UIButton at bottom.
If I set the width of UILabel to fixed width it works.
If I set the Leading/Trailing on UILabel then it doesn't work :(
I'm using the below code to handle the height of headerView
-(void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
// Dynamic sizing for the header view
if (table.tableHeaderView) {
UIView *headerView = table.tableHeaderView;
float height = [headerView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
CGRect headerFrame = headerView.frame;
// If we don't have this check, viewDidLayoutSubviews() will get
// repeatedly, causing the app to hang.
if (height != headerFrame.size.height) {
headerFrame.size.height = height;
headerView.frame = headerFrame;
table.tableHeaderView = headerView;
}
}
}
Anybody can explain why setting the leading/trailing is not working?
My view with constraint is like (without fixed width):
call in the end of viewDidLayoutSubviews.
[viewWithLabelAndButton setNeedLayout];
[viewWithLabelAndButton layoutIfNeeded];
I am using the code below in viewDidLoad:
[self.textView scrollRangeToVisible:NSMakeRange(0, 1)];
set the content offset in viewDidLayoutSubviews for it to take effect
- (void)viewDidLayoutSubviews {
[self.yourTextView setContentOffset:CGPointZero animated:NO];
}
I'm trying to implement a growing UITextView with the content size, I am using this class and autolayout and it works great but my problem is that my inner scroll view is not scrolling.
It was scrolling before adding the methods for growing text view in viewDidLoad.
This is what is in my xib:
View
outerScrollView
innerScrollView
labels
Growing UITextView
I do this to switch scrolling between scrollview
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
if([scrollView isEqual:self.innerScrollview]){
self.outerScrollview.scrollEnabled = NO;
self.innerScrollview.scrollEnabled = YES;
}
else{
self.outerScrollview.scrollEnabled = YES;
self.innerScrollview.scrollEnabled = NO;
}
}
and in viewDidLoad where the "msg" is the growing textview:
self.msgHandler = [[GrowingTextViewHandler alloc]initWithTextView:self.msg withHeightConstraint:self.msgHeight];
[self.msgHandler updateMinimumNumberOfLines:1 andMaximumNumberOfLine:INT_MAX];
and in textViewDidChange
- (void)textViewDidChange:(UITextView *)textView {
[self.msgHandler resizeTextViewWithAnimation:YES];
}
so why is the growing text affecting the scrolling of the inner scrollview?
int outerContentHeight = self.outer_last_label.frame.size.height + self.outer_last_label.frame.origin.y;
int innerContentHeight = self.inner_last_label.frame.size.height + self.inner_last_label.frame.origin.y;
[self.outerSrollview setContentSize:CGSizeMake([[UIScreen mainScreen] bounds].size.width,outerContentHeight)];
[self.innerScrollview setContentSize:CGSizeMake(self.scrollview2.frame.size.width, innerContentHeight)];
I have managed to fix it but I removed the use of the class"GrowingTextViewHandler" and I did this instead
I changed "scrollViewDidScroll" to this:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
if([scrollView isEqual:self.innerScrollview]){
[self.view insertSubview:self.innerScrollview atIndex:0];
}
else{
[self.view insertSubview:self.outerScrollview atIndex:0];
}
}
and changed "textViewDidChange" to this
- (void)textViewDidChange:(UITextView *)textView {
float height = self.msg.contentSize.height;
[UITextView beginAnimations:nil context:nil];
[UITextView setAnimationDuration:0.5];
self.msgHeightConstraint.constant = height + 10.0; //Give it some padding
[UITextView commitAnimations];
}
Please set the content size of the scroll view, Do not change anything in the code, and check,
[self.scrollViewOuter setContentSize:CGSizeMake(200, 1000)];
[self.scrollViewInner setContentSize:CGSizeMake(200, 1000)];
My both the scroll views are working if, I add content size in it.
I'm implementing an autogrowing UITextView. I'm aiming for a similar behaviour of the message box in Whatsapp, which autogrows when your text has more than 1 line.
I'm using the approach described below which stores the height constraint in a UITextView subclass and modifies it when the text changes.
My solution animates correctly when I press the enter key inside the TextView, but it doesn't work when my typing goes over the end of the line. In this case it just changes size instantly.
Performing the animation on the delegate's - (void)textViewDidChange:(UITextView *)textView method produces the same result.
How can I correctly animate the TextView height using the auto layout system?
I'm implementing it like this:
#interface OEAutoGrowingTextView ()
#property (strong, nonatomic) NSLayoutConstraint *heightConstraint;
#end
#implementation OEAutoGrowingTextView
- (id)initWithFrame:(CGRect)frame
{
if ( !(self = [super initWithFrame:frame]) )
{
return nil;
}
[self commonInit];
return self;
}
- (void)awakeFromNib
{
[self commonInit];
}
- (void)commonInit
{
// If we are using auto layouts, than get a handler to the height constraint.
for (NSLayoutConstraint *constraint in self.constraints)
{
if (constraint.firstAttribute == NSLayoutAttributeHeight)
{
self.heightConstraint = constraint;
break;
}
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textDidChange:) name:UITextViewTextDidChangeNotification object:self];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)textDidChange:(NSNotification *)notification
{
self.heightConstraint.constant = self.contentSize.height;
[UIView animateWithDuration:1.0f animations:^
{
[self layoutIfNeeded];
}];
}
#end
Note: doing the following doesn't help.
- (void)textDidChange:(NSNotification *)notification
{
self.heightConstraint.constant = self.contentSize.height;
[UIView animateWithDuration:1.0f animations:^
{
[self layoutIfNeeded];
for (UIView *view in self.subviews)
{
[view layoutIfNeeded];
}
}];
}
Further update: This seems to be a bug in iOS 7.x, I think it's fixed on iOS 8.0.
I tried wrapping the heightConstraint change in a UIView animation block and that didn't work
That isn't how you animate a constraint change. You do it by changing the constraint and then animating the act of layout itself, like this:
// change the text view constraint here
[UIView animateWithDuration:duration animations:^{
[self.textView layoutIfNeeded];
}];
Ok, the issue is that as of ios7, .contentSize isn't correct for UITextViews. I have this functionality, and you need to compute the contentSize yourself. I added a category method to UITextView, -contentHeight, and use that instead to compute the contentSize.
See these two links.
UITextView Content Size
SO on the same question
Here is the code that fixes it:
#implementation UITextView (Sizing)
- (CGFloat)contentHeight {
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) {
// This is the code for iOS 7. contentSize no longer returns the correct value, so
// we have to calculate it.
//
// This is partly borrowed from HPGrowingTextView, but I've replaced the
// magic fudge factors with the calculated values (having worked out where
// they came from)
CGRect frame = self.bounds;
// Take account of the padding added around the text.
UIEdgeInsets textContainerInsets = self.textContainerInset;
UIEdgeInsets contentInsets = self.contentInset;
CGFloat leftRightPadding = textContainerInsets.left + textContainerInsets.right + self.textContainer.lineFragmentPadding * 2;
leftRightPadding += contentInsets.left + contentInsets.right;
CGFloat topBottomPadding = textContainerInsets.top + textContainerInsets.bottom + contentInsets.top + contentInsets.bottom;
frame.size.width -= leftRightPadding;
frame.size.height -= topBottomPadding;
NSString* textToMeasure = self.text;
if(![textToMeasure isNotEmpty])
textToMeasure = #"-";
if ([textToMeasure hasSuffix:#"\n"]) {
textToMeasure = [NSString stringWithFormat:#"%#-", self.text];
}
// NSString class method: boundingRectWithSize:options:attributes:context is
// available only on ios7.0 sdk.
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
[paragraphStyle setLineBreakMode:NSLineBreakByWordWrapping];
NSDictionary* attributes = #{NSFontAttributeName : self.font,
NSParagraphStyleAttributeName : paragraphStyle};
CGRect size = [textToMeasure boundingRectWithSize:CGSizeMake(CGRectGetWidth(frame), MAXFLOAT)
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
attributes:attributes
context:nil];
CGFloat measuredHeight = ceilf(CGRectGetHeight(size) + topBottomPadding);
return measuredHeight;
} else {
return self.contentSize.height;
}
}
#end
Instead of contentSize, use this to compute the content height. You also don't need the animate at all - mine just computes and that is smooth enough, so you should make sure you really need the animation.
I would like to create an app that has a function like iOS 7's Notes application. Basically there is one row on the top that has date and time.
My solution is to put the UITextView inside UITableView. First row is UILabel with date and time, the second row is UITextView.
I change both UITextView and UITableViewCell height according to UITextView ContentSize.
The problem is the UITextView size is large so it doesn't automatically scroll when the user hit return key.
Is there any solution to make it scroll as normal?
UITextView is a subclass of UIScrollView. I will suggest an alternative method of implementing a similar functionality. Add the label view as a subview of the text view, and set a contentInset top value of the height of the label.
UILabel* label = [UILabel new];
label.text = #"Test";
[label sizeToFit];
CGRect frame = label.frame;
frame.origin.y -= frame.size.height;
[label setFrame:frame];
[self.textView addSubview:label];
[self.textView setContentInset:UIEdgeInsetsMake(label.frame.size.height, 0, 0, 0)];
Sample project:
http://sdrv.ms/16JUlVD
Try this solution. Fix is based on inheritance. But logic can be used at any place after UITextView text was changed. I have taken some useful code blocks from here:
http://craigipedia.blogspot.ru/2013/09/last-lines-of-uitextview-may-scroll.html
and edited by me for my solution.
Should work.
#interface CustomTextView : UITextView
#end
#implementation CustomTextView
-(id)init {
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textDidChange:) name:UITextViewTextDidChangeNotification object:self];
}
return self;
}
-(void)textDidChange:(NSNotification *)notification {
//iOS 7 UITextView auto scroll fix.
NSRange caretRange = self.selectedRange;
if (caretRange.location == self.text.length) {
CGRect textRect = [self.layoutManager usedRectForTextContainer:self.textContainer];
CGFloat sizeAdjustment = self.font.lineHeight * [UIScreen mainScreen].scale;
if (textRect.size.height >= self.frame.size.height - sizeAdjustment) {
if ([[self.text substringFromIndex:self.text.length - 1] isEqualToString:#"\n"]) {
[UIView animateWithDuration:0.2 animations:^{
[self setContentOffset:CGPointMake(self.contentOffset.x, self.contentOffset.y + sizeAdjustment)];
}];
}
}
}
//end of fix
}
#end