UITextView
我的iOS应用程序中有一个,可显示大量文本。
然后,我使用的offset margin参数分页此文本UITextView
。
我的问题是的填充UITextView
使我的计算混乱,因为根据我使用的字体大小和字体,它似乎有所不同。
因此,我提出一个问题:是否可以删除围绕内容的填充UITextView
?
期待听到您的回复!
UITextView
我的iOS应用程序中有一个,可显示大量文本。
然后,我使用的offset margin参数分页此文本UITextView
。
我的问题是的填充UITextView
使我的计算混乱,因为根据我使用的字体大小和字体,它似乎有所不同。
因此,我提出一个问题:是否可以删除围绕内容的填充UITextView
?
期待听到您的回复!
Answers:
它是iOS中最严重的错误之一。
此处给出的类UITextViewFixed
通常是总体上最合理的解决方案。
这是课程:
@IBDesignable class UITextViewFixed: UITextView {
override func layoutSubviews() {
super.layoutSubviews()
setup()
}
func setup() {
textContainerInset = UIEdgeInsets.zero
textContainer.lineFragmentPadding = 0
}
}
不要忘记在检查器中关闭scrollEnabled!
该解决方案在情节提要中正常工作
该解决方案在运行时正常运行
通常,在大多数情况下,这就是您所需要的。
即使你正在改变文本视图的高度上飞行,UITextViewFixed
通常做所有你需要的。
(动态更改高度的一个常见示例是,随着用户的输入而改变高度。)
这是苹果公司破碎的UITextView ...
这里是UITextViewFixed
:
请注意,您当然必须
不要忘记关闭scrollEnabled!:)
(1)在某些不常见的情况下-例如,在某些情况下表格具有灵活且动态变化的单元格高度-苹果公司做了一件奇怪的事情:它们在底部增加了额外的空间。不完全是!这将是iOS中最令人毛骨悚然的事情之一。
这是添加到上面的“快速修复”,通常可以解决这种疯狂问题。
...
textContainerInset = UIEdgeInsets.zero
textContainer.lineFragmentPadding = 0
// this is not ideal, but you can sometimes use this
// to fix the "extra space at the bottom" insanity
var b = bounds
let h = sizeThatFits(CGSize(
width: bounds.size.width,
height: CGFloat.greatestFiniteMagnitude)
).height
b.size.height = h
bounds = b
...
(2)有时,要修复另一个微妙的Apple混乱,您必须添加以下内容:
override func setContentOffset(_ contentOffset: CGPoint, animated: Bool) {
super.setContentOffset(contentOffset, animated: false)
}
(3)可以说,我们应该添加:
contentInset = UIEdgeInsets.zero
就.lineFragmentPadding = 0
在UITextViewFixed
。
但是……信不信由你……它在当前的iOS中不起作用!(在2019年检查。)将来可能需要添加该行。
事实 UITextView
在iOS被破坏是所有移动计算中最不可思议的事情之一。这个问题已经十周年了,现在还没有解决!
最后,这是“文本字段”的一些类似技巧:https : //stackoverflow.com/a/43099816/294884
通常,您使用的是UITextView,就像“ UILabel”一样。因此,您希望它使用省略号“ ...”截断文本
如果是这样,请在“设置”中添加第三行代码:
textContainer.lineBreakMode = .byTruncatingTail
通常,您使用文本视图仅显示文本。因此,用户实际上无法编辑任何内容。您使用“ 0”行表示文本视图将根据文本的多少行自动更改高度。
很好,但是如果根本没有文本,那么不幸的是,您得到的高度与只有一行文本一样!文本视图永不“消失”。
如果您希望它“消失”,只需添加它
override var intrinsicContentSize: CGSize {
var i = super.intrinsicContentSize
print("for \(text) size will be \(i)")
if text == "" { i.height = 1.0 }
print(" but we changed it to \(i)")
return i
}
(我将其设置为“ 1”,所以很清楚发生了什么,“ 0”就可以了。)
仅显示文本时,UILabel优于UITextView。UILabel不会遇到此质量检查页面上描述的问题。确实,我们所有人通常都“放弃”而仅使用UITextView的原因是UILabel难以使用。特别是,仅向UILabel中正确添加padding非常荒谬。实际上,这里是有关如何“最终”正确向UILabel中添加填充的完整讨论:https : //stackoverflow.com/a/58876988/294884 在某些情况下,如果您使用动态高度单元进行困难的布局,有时更好地使用UILabel做到这一点。
self.textView.isScrollEnabled = false
进去了viewDidLayoutSubviews()
,那也行得通。苹果只是喜欢让我们跳入篮筐:)
translatesAutoresizingMaskIntoConstraints
。单靠这个答案,我无法使用自动布局移除视图层次结构中的所有页边距(某些奇怪的底部页边距无法消失)。它还使用来处理视图大小的计算systemLayoutSizeFitting
,该视图先前由于越野车而返回了无效的大小UITextView
对于iOS 7.0,我发现contentInset技巧不再起作用。这是我用来摆脱iOS 7中的边距/填充的代码。
这会将文本的左边缘带到容器的左边缘:
textView.textContainer.lineFragmentPadding = 0
这将导致文本顶部与容器顶部对齐
textView.textContainerInset = .zero
两条线都需要完全去除边距/填充。
self.descriptionTextView.textContainerInset = UIEdgeInsetsZero;
lineFragmentPadding
为0是我一直在寻找的魔术。我不知道为什么Apple很难将UITextView内容与其他控件对齐。
lineFragmentPadding
并非旨在修改边距。来自docs 线段填充不旨在表达文本边距。相反,您应该在文本视图上使用插图,调整段落边距属性,或更改文本视图在其超级视图中的位置。
此解决方法是在2009年发布IOS 3.0时编写的。它不再适用。
我遇到了完全相同的问题,最后我不得不使用
nameField.contentInset = UIEdgeInsetsMake(-4,-8,0,0);
其中nameField是一个UITextView
。我碰巧使用的字体是Helvetica 16 point。对于我正在绘制的特定字段大小,这只是一个自定义解决方案。这使得左侧偏移与左侧齐平,顶部偏移在我想要其绘制框的位置。
此外,这似乎仅适用于UITextViews
您使用默认授权的情况,即。
nameField.textAlignment = NSTextAlignmentLeft;
例如,向右对齐,UIEdgeInsetsMake
似乎对右边缘完全没有影响。
至少,使用.contentInset属性可让您将字段放置在“正确”的位置,并容纳偏差而不会偏移UITextViews
。
基于已经给出的一些好的答案,这是一个纯粹的基于Storyboard / Interface Builder的解决方案,可在iOS 7.0+中运行
为以下项设置UITextView的用户定义的运行时属性:
textContainer.lineFragmentPadding
textContainerInset
在iOS 5上UIEdgeInsetsMake(-8,-8,-8,-8);
似乎效果很好。
我肯定会避免涉及硬编码值的任何答案,因为实际的边距可能会随着用户字体大小设置等发生变化。
这是@ user1687195的答案,写时没有修改textContainer.lineFragmentPadding
(因为docs指出这不是预期的用法)。
这非常适用于iOS 7及更高版本。
self.textView.textContainerInset = UIEdgeInsetsMake(
0,
-self.textView.textContainer.lineFragmentPadding,
0,
-self.textView.textContainer.lineFragmentPadding);
这实际上是相同的结果,只是更清洁一点,因为它不会滥用lineFragmentPadding属性。
self.textView.textContainer.lineFragmentPadding = 0;
lineFragmentPadding
并不意味着要控制利润。这就是为什么您应该使用的原因textContainerInset
。
所有这些答案都解决了标题问题,但是我想针对OP的问题正文中提出的问题提出一些解决方案。
计算内文本大小的一种快速方法UITextView
是使用NSLayoutManager
:
UITextView *textView;
CGSize textSize = [textView usedRectForTextContainer:textView.textContainer].size;
这样就给出了总可滚动内容,该内容可能大于UITextView
的框架。我发现textView.contentSize
它比实际计算文本要占用多少空间要准确得多。例如,给定一个空UITextView
:
textView.frame.size = (width=246, height=50)
textSize = (width=10, height=16.701999999999998)
textView.contentSize = (width=246, height=33)
textView.textContainerInset = (top=8, left=0, bottom=8, right=0)
UIFont
具有可以快速获取给定字体的行高的属性。因此,您可以使用以下命令快速找到文本的行高UITextView
:
UITextView *textView;
CGFloat lineHeight = textView.font.lineHeight;
确定实际可见的文本量对于处理“分页”效果很重要。UITextView
有一个称为的属性textContainerInset
,它实际上是实际UITextView.frame
文本和文本本身之间的边距。要计算可见框架的实际高度,可以执行以下计算:
UITextView *textView;
CGFloat textViewHeight = textView.frame.size.height;
UIEdgeInsets textInsets = textView.textContainerInset;
CGFloat textHeight = textViewHeight - textInsets.top - textInsets.bottom;
最后,既然您拥有可见的文本大小和内容,则可以通过textHeight
从中减去来快速确定偏移量textSize
:
// where n is the page number you want
CGFloat pageOffsetY = textSize - textHeight * (n - 1);
textView.contentOffset = CGPointMake(textView.contentOffset.x, pageOffsetY);
// examples
CGFloat page1Offset = 0;
CGFloat page2Offset = textSize - textHeight
CGFloat page3Offset = textSize - textHeight * 2
使用所有这些方法,我没有碰触自己的插图,并且能够转到插入符号或想要的文本中的任何位置。
您可以使用以下textContainerInset
属性UITextView
:
textView.textContainerInset = UIEdgeInsetsMake(10,10,10,10);
(上,左,下,右)
textContainerInset
当我想将其更改为新值时,更新属性的最低值对我不起作用-请参阅stackoverflow.com/questions/19422578/…。
对于iOS 10,以下行适用于顶部和底部填充的删除。
captionTextView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
UIEdgeInset.zero
使用XCode 8.3和iOS 10.3模拟器进行工作
最新的Swift:
self.textView.textContainerInset = .init(top: -2, left: 0, bottom: 0, right: 0)
self.textView.textContainer.lineFragmentPadding = 0
这是Fattie非常有用的答案的更新版本。它添加了2条重要的代码行,这些行帮助我使布局在iOS 10和11(以及可能也在较低的版本)上运行:
@IBDesignable class UITextViewFixed: UITextView {
override func layoutSubviews() {
super.layoutSubviews()
setup()
}
func setup() {
translatesAutoresizingMaskIntoConstraints = true
textContainerInset = UIEdgeInsets.zero
textContainer.lineFragmentPadding = 0
translatesAutoresizingMaskIntoConstraints = false
}
}
重要的是两个translatesAutoresizingMaskIntoConstraints = <true/false>
语句!
在我所有情况下,这都令人惊讶地消除了所有利润!
尽管textView
不是第一响应者,但是可能会发生一些奇怪的底边距,而这种底边距无法使用sizeThatFits
公认的答案中提到的方法来解决。
当进入textView时,突然奇怪的底部边缘消失了,一切看起来都应该如此,但是只有在textView获得后, firstResponder
。
因此,我在此处阅读了SO的文章,指出translatesAutoresizingMaskIntoConstraints
在调用之间手动设置帧/边界时,启用和禁用确实有帮助。
幸运的是,这不仅适用于框架设置,而且setup()
夹在两条线之间translatesAutoresizingMaskIntoConstraints
通话!
例如,这在使用上计算视图框架时非常有用systemLayoutSizeFitting
UIView
。退回正确的尺寸(以前没有)!
如原始答案中所述:
不要忘记在Inspector中关闭scrollEnabled!
该解决方案在情节提要中以及在运行时都能正常工作。
就是这样,现在您真的完成了!
这是一个简单的小扩展程序,它将从应用程序的每个文本视图中删除Apple的默认边距。
注意:Interface Builder仍将显示旧边距,但是您的应用程序将按预期运行。
extension UITextView {
open override func awakeFromNib() {
super.awakeFromNib();
removeMargins();
}
/** Removes the Apple textview margins. */
public func removeMargins() {
self.contentInset = UIEdgeInsetsMake(
0, -textContainer.lineFragmentPadding,
0, -textContainer.lineFragmentPadding);
}
}
对我来说(iOS 11和Xcode 9.4.1),神奇的工作是将textView.font属性设置为UIFont.preferred(forTextStyle:UIFontTextStyle)
样式,同时也是@Fattie提到的第一个答案。但是@Fattie答案在我设置textView.font属性之前不起作用,否则UITextView会一直表现异常。
我发现了另一种方法,即从UITextView的子视图中获取带有文本的视图,并将其设置在子类的layoutSubview方法中:
- (void)layoutSubviews {
[super layoutSubviews];
const int textViewIndex = 1;
UIView *textView = [self.subviews objectAtIndex:textViewIndex];
textView.frame = CGRectMake(
kStatusViewContentOffset,
0.0f,
self.bounds.size.width - (2.0f * kStatusViewContentOffset),
self.bounds.size.height - kStatusViewContentOffset);
}
textView滚动还会影响文本的位置,并使其看起来不垂直居中。通过禁用滚动并将顶部插图设置为0,我设法使文本在视图中居中:
textView.scrollEnabled = NO;
textView.textContainerInset = UIEdgeInsetsMake(0, textView.textContainerInset.left, textView.textContainerInset.bottom, textView.textContainerInset.right);
由于某种原因,我还没有弄清楚,在开始键入之前光标仍未居中,但是在我开始键入时,文本立即居中。
如果要设置HTML字符串并避免底部填充,请确保未使用块标签,例如div,p。
就我而言,这就是原因。您可以通过使用span标签替换出现的block标签来轻松测试它。
[firstNameTextField setContentVerticalAlignment:UIControlContentVerticalAlignmentCenter];