如何调整UITextView的内容?
有一个好的方法来调整UITextView
的大小以符合其内容? 说例如我有一个UITextView
包含一行文本:
"Hello world"
然后我添加另一行文字:
"Goodbye world"
在Cocoa Touch中是否有一个很好的方法来获得将保存文本视图中所有行的rect
,以便我可以相应地调整父视图?
作为另一个例子,查看日历应用程序中事件的注释字段 – 注意单元格(以及它所包含的UITextView
)如何扩展,以保存注释string中的所有文本行。
这适用于iOS 6.1和iOS 7:
- (void)textViewDidChange:(UITextView *)textView { CGFloat fixedWidth = textView.frame.size.width; CGSize newSize = [textView sizeThatFits:CGSizeMake(fixedWidth, MAXFLOAT)]; CGRect newFrame = textView.frame; newFrame.size = CGSizeMake(fmaxf(newSize.width, fixedWidth), newSize.height); textView.frame = newFrame; }
或者在Swift中
let fixedWidth = textView.frame.size.width textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude)) let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude)) var newFrame = textView.frame newFrame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height) textView.frame = newFrame
如果你想支持iOS 6.1,那么你还应该:
textview.scrollEnabled = NO;
这不再适用于iOS 7或以上
实际上有一个非常简单的方法来调整UITextView
的正确高度的内容。 它可以使用UITextView
contentSize
来完成。
CGRect frame = _textView.frame; frame.size.height = _textView.contentSize.height; _textView.frame = frame;
有一点需要注意的是正确的contentSize
只有在 UITextView
通过addSubview
添加到视图后才可用。 在此之前,它等于frame.size
如果自动布局为ON,这将不起作用。 对于自动布局,一般的方法是使用sizeThatFits
方法并在高度约束上更新constant
量值。
CGSize sizeThatShouldFitTheContent = [_textView sizeThatFits:_textView.frame.size]; heightConstraint.constant = sizeThatShouldFitTheContent.height;
heightConstraint
是一个布局约束,您通常通过将属性链接到在故事板中创build的高度约束来通过IBOutlet进行设置。
只是为了增加这个惊人的答案,2014年,如果你:
[self.textView sizeToFit];
只有在iPhone6 +的行为有所不同:
只有6+(不是5或6),它会在UITextView中增加一个“空白行”。 “RL解决scheme”完美地解决了这个问题:
CGRect _f = self.mainPostText.frame; _f.size.height = self.mainPostText.contentSize.height; self.mainPostText.frame = _f;
它修复了6+的“额外线路”问题。
为了在UITableViewCell
创build一个dynamicresize的UITextView
,我在iOS 8 SDK中发现了Xcode 6中的以下组合:
- 将
UITextView
添加到UITableViewCell
并将其限制在边上 - 将
UITextView
的scrollEnabled
属性设置为NO
。 启用滚动function后,UITextView
的框架与其内容大小无关,但禁用滚动function时,两者之间存在关系。 -
如果您的表格使用的是原始的默认行高度44,那么它将自动计算行高度,但是如果将默认行高度更改为其他值,则可能需要手动打开
viewDidLoad
中行高的自动计算:tableView.estimatedRowHeight = 150; tableView.rowHeight = UITableViewAutomaticDimension;
对于只读dynamic调整UITextView
的大小,就是这样。 如果您允许用户编辑UITextView
的文本,则还需要:
-
实现
UITextViewDelegate
协议的textViewDidChange:
方法,并在每次编辑文本时告诉tableView
自己重绘:- (void)textViewDidChange:(UITextView *)textView; { [tableView beginUpdates]; [tableView endUpdates]; }
-
不要忘记设置
UITextView
委托的地方,在Storyboard
或在tableView:cellForRowAtIndexPath:
Swift:
textView.sizeToFit()
在我(有限)的经历中,
- (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode
不尊重换行符,所以最终CGSize
比实际需要的CGSize
更短。
- (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size
似乎尊重换行符。
另外,文本实际上并不在UITextView
的顶部。 在我的代码中,我将UITextView
的新高度设置为比sizeOfFont
方法返回的高度大24个像素。
在iOS6中,您可以在设置文本后立即检查UITextView的contentSize
属性。 在iOS7中,这将不再起作用。 如果您想为iOS7恢复此行为,请将以下代码放在UITextView的子类中。
- (void)setText:(NSString *)text { [super setText:text]; if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_6_1) { CGRect rect = [self.textContainer.layoutManager usedRectForTextContainer:self.textContainer]; UIEdgeInsets inset = self.textContainerInset; self.contentSize = UIEdgeInsetsInsetRect(rect, inset).size; } }
我会在页面的底部张贴正确的解决scheme,以防万一有人勇敢(或绝望地)读到这一点。
这里是gitHub回购为那些谁不想读取所有文本: resizableTextView
这与iOs7(我相信它会与iOs8一起工作)和自动布局一起工作。 你不需要神奇的数字,禁用布局和类似的东西。 简洁优雅的解决scheme。
我认为所有与约束有关的代码都应该去updateConstraints
方法。 所以,我们来创build我们自己的ResizableTextView
。
我们遇到的第一个问题是,在viewDidLoad
方法之前不知道真正的内容大小。 我们可以采取漫长的马路,并根据字体大小,换行符等进行计算。但是我们需要强大的解决scheme,所以我们要做:
CGSize contentSize = [self sizeThatFits:CGSizeMake(self.frame.size.width, FLT_MAX)];
所以现在我们知道真正的contentSize,无论我们在哪里:在viewDidLoad
之前或之后。 现在,添加高度约束textView(通过故事板或代码,无论如何)。 我们将使用contentSize.height
调整该值:
[self.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) { if (constraint.firstAttribute == NSLayoutAttributeHeight) { constraint.constant = contentSize.height; *stop = YES; } }];
最后要做的是告诉超类updateConstraints
。
[super updateConstraints];
现在我们的class级看起来像:
ResizableTextView.m
- (void) updateConstraints { CGSize contentSize = [self sizeThatFits:CGSizeMake(self.frame.size.width, FLT_MAX)]; [self.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) { if (constraint.firstAttribute == NSLayoutAttributeHeight) { constraint.constant = contentSize.height; *stop = YES; } }]; [super updateConstraints]; }
漂亮干净,对吧? 而且您不必在控制器中处理该代码!
可是等等! 没有animation!
你可以很容易地animation变化,使textView
顺利拉伸。 这里是一个例子:
[self.view layoutIfNeeded]; // do your own text change here. self.infoTextView.text = [NSString stringWithFormat:@"%@, %@", self.infoTextView.text, self.infoTextView.text]; [self.infoTextView setNeedsUpdateConstraints]; [self.infoTextView updateConstraintsIfNeeded]; [UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{ [self.view layoutIfNeeded]; } completion:nil];
刚刚设置
textView.scrollEnabled = false
除此之外不需要做任何事情。
你有没有尝试[textView sizeThatFits:textView.bounds]
?
编辑:sizeThatFits返回的大小,但不实际调整组件大小。 我不确定这是你想要的,或者如果[textView sizeToFit]
更多的是你要找的。 无论哪种情况,我都不知道它是否能够完美地符合你想要的内容,但是这是第一个尝试的方法。
另一种方法是使用NSString
方法查找特定string将要占用的大小:
-(CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size
这将返回适合给定string的矩形的大小。 通过所需宽度和最大高度的尺寸,然后您可以查看返回的高度以适合文本。 有一个版本可以让你指定换行符模式。
然后,您可以使用返回的大小来更改视图的大小以适应。
如果你没有方便的UITextView
(例如,你正在调整表格视图单元格大小),你将不得不通过测量string来计算大小,然后计算UITextView
每边的8点填充。 例如,如果您知道文本视图的所需宽度并想要找出相应的高度:
NSString * string = ...; CGFloat textViewWidth = ...; UIFont * font = ...; CGSize size = CGSizeMake(textViewWidth - 8 - 8, 100000); size.height = [string sizeWithFont:font constrainedToSize:size].height + 8 + 8;
在这里,每个8是占四个填充边缘之一,而100000只是一个非常大的最大尺寸。
在实践中,您可能需要添加一个额外的font.leading
到高度; 这会在文本下面添加一个空白行,如果在文本视图下方直接显示可视化控件,这可能会更好一些。
以下事情已经足够了:
- 只记得为你的
UITextView
设置滚动为NO :
- 正确设置自动布局约束。
你甚至可以使用UITableViewAutomaticDimension
。
我们可以通过约束来实现。
- 为UITextView设置高度约束。
2.创buildIBOutlet的高度约束。
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *txtheightconstraints;
3.不要忘记为你的textview设置代表。
4。
-(void)textViewDidChange:(UITextView *)textView { CGFloat fixedWidth = textView.frame.size.width; CGSize newSize = [textView sizeThatFits:CGSizeMake(fixedWidth, MAXFLOAT)]; CGRect newFrame = textView.frame; newFrame.size = CGSizeMake(fmaxf(newSize.width, fixedWidth), newSize.height); NSLog(@"this is updating height%@",NSStringFromCGSize(newFrame.size)); [UIView animateWithDuration:0.2 animations:^{ _txtheightconstraints.constant=newFrame.size.height; }]; }
然后像这样更新你的约束:)
结合迈克·麦克马斯特的回答,你可能想要做一些事情:
[myTextView setDelegate: self]; ... - (void)textViewDidChange:(UITextView *)textView { if (myTextView == textView) { // it changed. Do resizing here. } }
我发现了一种方法来根据文本里面的文本来调整文本字段的高度,并根据文本字段的高度在它下面安排一个标签! 这是代码。
UITextView *_textView = [[UITextView alloc] initWithFrame:CGRectMake(10, 10, 300, 10)]; NSString *str = @"This is a test text view to check the auto increment of height of a text view. This is only a test. The real data is something different."; _textView.text = str; [self.view addSubview:_textView]; CGRect frame = _textView.frame; frame.size.height = _textView.contentSize.height; _textView.frame = frame; UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(10, 5 + frame.origin.y + frame.size.height, 300, 20)]; lbl.text = @"Hello!"; [self.view addSubview:lbl];
使用自动布局和你的sizetofit不工作的人,那么请检查你的宽度约束一次。 如果你错过了宽度约束,那么高度将是准确的。
不需要使用任何其他的API。 只有一条线可以解决所有问题。
[_textView sizeToFit];
在这里,我只关心高度,保持宽度不变,错过了Storyboard中TextView的宽度约束。
这是为了显示服务的dynamic内容。
希望这可能会帮助..
从iOS 8开始,可以使用UITableView的自动布局function来根据自定义代码自动调整UITextView的大小。 我已经在github上做了一个项目来certificate这一点,但这里是关键:
- UITextView必须禁用滚动function,您可以通过编程或通过界面生成器进行操作。 如果启用了滚动,则不会resize,因为滚动可以查看较大的内容。
- 在UITableViewController的viewDidLoad中,必须为estimatedRowHeight设置一个值,然后将
rowHeight
设置为UITableViewAutomaticDimension
。
- (void)viewDidLoad { [super viewDidLoad]; self.tableView.estimatedRowHeight = self.tableView.rowHeight; self.tableView.rowHeight = UITableViewAutomaticDimension; }
- 项目部署目标必须是iOS 8或更高版本。
这里是@jhibberd的快速版本
let cell:MsgTableViewCell! = self.tableView.dequeueReusableCellWithIdentifier("MsgTableViewCell", forIndexPath: indexPath) as? MsgTableViewCell cell.msgText.text = self.items[indexPath.row] var fixedWidth:CGFloat = cell.msgText.frame.size.width var size:CGSize = CGSize(width: fixedWidth,height: CGFloat.max) var newSize:CGSize = cell.msgText.sizeThatFits(size) var newFrame:CGRect = cell.msgText.frame; newFrame.size = CGSizeMake(CGFloat(fmaxf(Float(newSize.width), Float(fixedWidth))), newSize.height); cell.msgText.frame = newFrame cell.msgText.frame.size = newSize return cell
当我需要使UITextView
文本适合特定区域时,这很好地工作:
//文本必须已经添加到子视图,否则contentviewsize将会出错。 - (void)reduceFontToFit:(UITextView *)tv { UIFont * font = tv.font; double pointSize = font.pointSize; while(tv.contentSize.height> tv.frame.size.height && pointSize> 7.0){ pointSize - = 1.0; UIFont * newFont = [UIFont fontWithName:font.fontName size:pointSize]; tv.font = newFont; } 如果(pointSize!= font.pointSize) NSLog(@“从%.1f到%.1f的字体”,pointSize,tv.font.pointSize); }
对于iOS 7.0,不是将frame.size.height
设置为contentSize.height
(目前什么都不做),而是使用[textView sizeToFit]
。
看到这个问题 。
我回顾了所有的答案,都保持固定的宽度,只调整高度。 如果你想调整宽度,你可以很容易地使用这种方法:
所以当configuration你的文本视图,设置滚动禁用
textView.isScrollEnabled = false
然后在委托方法func textViewDidChange(_ textView: UITextView)
添加下面的代码:
func textViewDidChange(_ textView: UITextView) { let newSize = textView.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)) textView.frame = CGRect(origin: textView.frame.origin, size: newSize) }
输出:
希望这可以帮助:
- (void)textViewDidChange:(UITextView *)textView { CGSize textSize = textview.contentSize; if (textSize != textView.frame.size) textView.frame.size = textSize; }
如果有任何其他的到这里,这个解决scheme为我工作,1“Ronnie Liew”+4“user63934”(我的文本从web服务到达):注意1000(在我的情况下,没有什么可以这么大)
UIFont *fontNormal = [UIFont fontWithName:FONTNAME size:FONTSIZE]; NSString *dealDescription = [client objectForKey:@"description"]; //4 CGSize textSize = [dealDescription sizeWithFont:fontNormal constrainedToSize:CGSizeMake(containerUIView.frame.size.width, 1000)]; CGRect dealDescRect = CGRectMake(10, 300, containerUIView.frame.size.width, textSize.height); UITextView *dealDesc = [[[UITextView alloc] initWithFrame:dealDescRect] autorelease]; dealDesc.text = dealDescription; //add the subview to the container [containerUIView addSubview:dealDesc]; //1) after adding the view CGRect frame = dealDesc.frame; frame.size.height = dealDesc.contentSize.height; dealDesc.frame = frame;
那就是……干杯
我发现根据文本的大小重新调整UITextView的高度的最佳方式。
CGSize textViewSize = [YOURTEXTVIEW.text sizeWithFont:[UIFont fontWithName:@"SAMPLE_FONT" size:14.0] constrainedToSize:CGSizeMake(YOURTEXTVIEW.frame.size.width, FLT_MAX)];
或者你可以使用
CGSize textViewSize = [YOURTEXTVIEW.text sizeWithFont:[UIFont fontWithName:@"SAMPLE_FONT" size:14.0] constrainedToSize:CGSizeMake(YOURTEXTVIEW.frame.size.width, FLT_MAX) lineBreakMode:NSLineBreakByTruncatingTail];
对于那些希望textview实际上移动并保持底线位置的人
CGRect frame = textView.frame; frame.size.height = textView.contentSize.height; if(frame.size.height > textView.frame.size.height){ CGFloat diff = frame.size.height - textView.frame.size.height; textView.frame = CGRectMake(0, textView.frame.origin.y - diff, textView.frame.size.width, frame.size.height); } else if(frame.size.height < textView.frame.size.height){ CGFloat diff = textView.frame.size.height - frame.size.height; textView.frame = CGRectMake(0, textView.frame.origin.y + diff, textView.frame.size.width, frame.size.height); }
唯一能够工作的代码是在上面的jhibberd回答中使用“SizeToFit”的代码,但实际上,除非您在ViewDidAppear中调用它或将其连接到UITextView文本更改的事件,否则它将不会启动。
基于Nikita Took的回答,我来到了Swift中的以下解决scheme,该解决scheme使用自动布局在iOS 8上工作:
descriptionTxt.scrollEnabled = false descriptionTxt.text = yourText var contentSize = descriptionTxt.sizeThatFits(CGSizeMake(descriptionTxt.frame.size.width, CGFloat.max)) for c in descriptionTxt.constraints() { if c.isKindOfClass(NSLayoutConstraint) { var constraint = c as! NSLayoutConstraint if constraint.firstAttribute == NSLayoutAttribute.Height { constraint.constant = contentSize.height break } } }
Swift的答案:下面的代码计算你的textView的高度。
let maximumLabelSize = CGSize(width: Double(textView.frame.size.width-100.0), height: DBL_MAX) let options = NSStringDrawingOptions.TruncatesLastVisibleLine | NSStringDrawingOptions.UsesLineFragmentOrigin let attribute = [NSFontAttributeName: textView.font!] let str = NSString(string: message) let labelBounds = str.boundingRectWithSize(maximumLabelSize, options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: attribute, context: nil) let myTextHeight = CGFloat(ceilf(Float(labelBounds.height)))
现在,您可以将myTextHeight
的高度设置为myTextHeight
如果您需要在staticTableView
dynamic调整staticTableView
和tableViewCell
大小,这里是答案
这个方法似乎适用于ios7
// Code from apple developer forum - @Steve Krulewitz, @Mark Marszal, @Eric Silverberg - (CGFloat)measureHeight { if ([self respondsToSelector:@selector(snapshotViewAfterScreenUpdates:)]) { CGRect frame = internalTextView.bounds; CGSize fudgeFactor; // The padding added around the text on iOS6 and iOS7 is different. fudgeFactor = CGSizeMake(10.0, 16.0); frame.size.height -= fudgeFactor.height; frame.size.width -= fudgeFactor.width; NSMutableAttributedString* textToMeasure; if(internalTextView.attributedText && internalTextView.attributedText.length > 0){ textToMeasure = [[NSMutableAttributedString alloc] initWithAttributedString:internalTextView.attributedText]; } else{ textToMeasure = [[NSMutableAttributedString alloc] initWithString:internalTextView.text]; [textToMeasure addAttribute:NSFontAttributeName value:internalTextView.font range:NSMakeRange(0, textToMeasure.length)]; } if ([textToMeasure.string hasSuffix:@"\n"]) { [textToMeasure appendAttributedString:[[NSAttributedString alloc] initWithString:@"-" attributes:@{NSFontAttributeName: internalTextView.font}]]; } // NSAttributedString class method: boundingRectWithSize:options:context is // available only on ios7.0 sdk. CGRect size = [textToMeasure boundingRectWithSize:CGSizeMake(CGRectGetWidth(frame), MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin context:nil]; return CGRectGetHeight(size) + fudgeFactor.height; } else { return self.internalTextView.contentSize.height; } }