如何在UITextView中丢失边距/填充?


486

UITextView我的iOS应用程序中有一个,可显示大量文本。

然后,我使用的offset margin参数分页此文本UITextView

我的问题是的填充UITextView使我的计算混乱,因为根据我使用的字体大小和字体,它似乎有所不同。

因此,我提出一个问题:是否可以删除围绕内容的填充UITextView

期待听到您的回复!


9
请注意,此质量检查已将近十年了!拥有100,000多个视图,因为它是iOS中最愚蠢的问题之一。只是FTR我在下面输入了当前的2017年一种相当简单/通常/可接受的解决方案作为答案。
Fattie

1
在2009年IOS 3.0刚发布时,我编写了一个硬编码的变通方法,因此我仍然对此有所了解。我只是编辑了答案,以明确指出它已过期数年,并忽略了已接受的状态。
迈克尔(Michael)

Answers:


383

最新的2019

它是iOS中最严重的错误之一。

此处给出的类UITextViewFixed通常是总体上最合理的解决方案。

这是课程:

@IBDesignable class UITextViewFixed: UITextView {
    override func layoutSubviews() {
        super.layoutSubviews()
        setup()
    }
    func setup() {
        textContainerInset = UIEdgeInsets.zero
        textContainer.lineFragmentPadding = 0
    }
}

不要忘记在检查器中关闭scrollEnabled!

  1. 该解决方案在情节提要中正常工作

  2. 该解决方案在运行时正常运行

就是这样,您完成了。

通常,在大多数情况下这就是您所需要的

即使你正在改变文本视图的高度上飞行UITextViewFixed通常做所有你需要的。

(动态更改高度的一个常见示例是,随着用户的输入而改变高度。)

这是苹果公司破碎的UITextView ...

带有UITextView的IB的屏幕截图

这里是UITextViewFixed

带有UITextViewFixed的IB的屏幕截图

请注意,您当然必须

在检查器中关闭scrollEnabled!

不要忘记关闭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 = 0UITextViewFixed

但是……信不信由你……它在当前的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呢?

仅显示文本时,UILabel优于UITextView。UILabel不会遇到此质量检查页面上描述的问题。确实,我们所有人通常都“放弃”而仅使用UITextView的原因是UILabel难以使用。特别是,仅向UILabel中正确添加padding非常荒谬。实际上,这里是有关如何“最终”正确向UILabel中添加填充的完整讨论:https : //stackoverflow.com/a/58876988/294884 在某些情况下,如果您使用动态高度单元进行困难的布局,有时更好地使用UILabel做到这一点。


1
self.textView.isScrollEnabled = false进去了viewDidLayoutSubviews(),那也行得通。苹果只是喜欢让我们跳入篮筐:)
AnBisw

1
最佳解决方案,以动态高度(UITableViewAutomaticDimension)修复我的UITableView单元格,页眉和页脚中所有荒唐的UITextView自动布局错误。大!
Peter Kreinz

1
我对@Fattie的答案发布了更新的答案,它使用启用/禁用的特殊技巧帮助我真正摆脱了所有插页translatesAutoresizingMaskIntoConstraints。单靠这个答案,我无法使用自动布局移除视图层次结构中的所有页边距(某些奇怪的底部页边距无法消失)。它还使用来处理视图大小的计算systemLayoutSizeFitting,该视图先前由于越野车而返回了无效的大小UITextView
Fab1n

1
1up for IBDesignable。如果可以的话,我
也会投票

1
我在表中有textviews,只有一行文本的viewview不能正确计算其高度(自动布局)。要修复它,我必须重写didMoveToSuperview并在那里也调用安装程序。
El Horrible

790

对于iOS 7.0,我发现contentInset技巧不再起作用。这是我用来摆脱iOS 7中的边距/填充的代码。

这会将文本的左边缘带到容器的左边缘:

textView.textContainer.lineFragmentPadding = 0

这将导致文本顶部与容器顶部对齐

textView.textContainerInset = .zero

两条线都需要完全去除边距/填充。


2
这为我工作了两行,但是到了5行时,文本被截断了。
livings124

7
请注意,第二行也可以写成:self.descriptionTextView.textContainerInset = UIEdgeInsetsZero;
jessepinho 2013年

19
设置lineFragmentPadding为0是我一直在寻找的魔术。我不知道为什么Apple很难将UITextView内容与其他控件对齐。
phatmann

3
这是正确的答案。此解决方案不会发生水平滚动。
2015年

2
lineFragmentPadding并非旨在修改边距。来自docs 线段填充不旨在表达文本边距。相反,您应该在文本视图上使用插图,调整段落边距属性,或更改文本视图在其超级视图中的位置。
Martin Berger

256

此解决方法是在2009年发布IOS 3.0时编写的。它不再适用。

我遇到了完全相同的问题,最后我不得不使用

nameField.contentInset = UIEdgeInsetsMake(-4,-8,0,0);

其中nameField是一个UITextView。我碰巧使用的字体是Helvetica 16 point。对于我正在绘制的特定字段大小,这只是一个自定义解决方案。这使得左侧偏移与左侧齐平,顶部偏移在我想要其绘制框的位置。

此外,这似乎仅适用于UITextViews您使用默认授权的情况,即。

nameField.textAlignment = NSTextAlignmentLeft;

例如,向右对齐,UIEdgeInsetsMake似乎对右边缘完全没有影响。

至少,使用.contentInset属性可让您将字段放置在“正确”的位置,并容纳偏差而不会偏移UITextViews


1
是的,它确实适用于iOS7。确保将UIEdgeInset设置为正确的值。它可能不同于UIEdgeInsetsMake(-4,-8,0,0)。
app_

15
在IOS 7中,我发现UIEdgeInsetsMake(0,-4,0,-4)效果最好。
拉库拉(Racura)2013年

2
是的,这些是示例编号。我说:“它只是针对我正在绘制的特定字段大小的自定义解决方案”。主要是我试图说明您必须针对您独特的布局情况使用数字。
迈克尔

3
iOS 7中不推荐使用UITextAlignmentLeft。请使用NSTextAlignmentLeft。
Jordi Kroon 2014年

7
我不明白为什么人们赞成使用硬编码值的答案。在将来的iOS版本中很可能会破坏它,这只是一个坏主意。
ldoogy

77

基于已经给出的一些好的答案,这是一个纯粹的基于Storyboard / Interface Builder的解决方案,可在iOS 7.0+中运行

为以下项设置UITextView的用户定义的运行时属性

textContainer.lineFragmentPadding
textContainerInset

界面生成器


4
这显然是最好的答案,特别是如果您需要仅使用XIB的解决方案
yano

16
复制/粘贴:textContainer.lineFragmentPadding | textContainerInset
卡·德·安吉利斯

3
这就是一个很漂亮的答案-即使在3年后也很容易并且很好用:)
Shai Mishali


41

我肯定会避免涉及硬编码值的任何答案,因为实际的边距可能会随着用户字体大小设置等发生变化。

这是@ 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;
巴基特·阿布德拉苏洛夫

1
@BakytAbdrasulov请阅读我的回答。当您的解决方案有效时,它与文档不一致(请参阅我的答案中的链接)。lineFragmentPadding并不意味着要控制利润。这就是为什么您应该使用的原因textContainerInset
ldoogy

20

使用用户定义的运行时属性的情节提要或Interface Builder解决方案:

屏幕截图是带有的iOS 7.1和iOS 6.1 contentInset = {{-10, -5}, {0, 0}}

用户定义的运行时属性

输出


1
在iOS中7为我工作
库比拉伊

仅用于用户定义的运行时属性-太棒了!
hris.to

16

所有这些答案都解决了标题问题,但是我想针对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

使用所有这些方法,我没有碰触自己的插图,并且能够转到插入符号或想要的文本中的任何位置。


12

您可以使用以下textContainerInset属性UITextView

textView.textContainerInset = UIEdgeInsetsMake(10,10,10,10);

(上,左,下,右)


1
我不知道为什么这里没有将此作为最佳答案。textContainerInset确实满足了我的需求。
KoreanXcodeWorker 2016年

textContainerInset当我想将其更改为新值时,更新属性的最低值对我不起作用-请参阅stackoverflow.com/questions/19422578/…
Evan R

@MaggiePhillips这不是最好的答案,因为硬编码的值不可能是正确的方法,并且在某些情况下一定会失败。您需要考虑lineFragmentPadding。
ldoogy's

11

对于iOS 10,以下行适用于顶部和底部填充的删除。

captionTextView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)

Xcode 8.2.1。仍然是这个答案中提到的相同问题。我的解决方案是将值编辑为UIEdgeInsets(顶部:0,左侧:-4.0,底部:0,右侧:-4.0)。
Darkwonder '17

UIEdgeInset.zero使用XCode 8.3和iOS 10.3模拟器进行工作
Simon Warta,2017年

10

最新的Swift:

self.textView.textContainerInset = .init(top: -2, left: 0, bottom: 0, right: 0)
self.textView.textContainer.lineFragmentPadding = 0

好答案。此答案将删除其他的顶部插图。textView.textContainerInset = UIEdgeInsets.zero不会从顶部插图中移除2个像素。
korgx9 '19

10

这是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通话!

例如,这在使用计算视图框架时非常有用systemLayoutSizeFittingUIView。退回正确的尺寸(以前没有)!

如原始答案中所述:
不要忘记在Inspector中关闭scrollEnabled!
该解决方案在情节提要中以及在运行时都能正常工作。

就是这样,现在您真的完成了!


5

对于Swift 4,Xcode 9

使用以下功能可以更改UITextView中文本的边距/填充

公共函数UIEdgeInsetsMake(_上:CGFloat,_左:CGFloat,_底:CGFloat,_右:CGFloat)-> UIEdgeInsets

所以在这种情况下是

 self.textView?.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)

3

进行插入解决方案时,右侧和底部仍然有填充物。同样,文本对齐也会引起问题。我发现的唯一确定的方法是将文本视图放入另一个被限制为边界的视图内。


3

这是一个简单的小扩展程序,它将从应用程序的每个文本视图中删除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);
   }
}

1

对我来说(iOS 11和Xcode 9.4.1),神奇的工作是将textView.font属性设置为UIFont.preferred(forTextStyle:UIFontTextStyle) 样式,同时也是@Fattie提到的第一个答案。但是@Fattie答案在我设置textView.font属性之前不起作用,否则UITextView会一直表现异常。


1

如果有人在寻找最新的Swift版本,那么下面的代码可以在Xcode 10.2Swift 4.2上正常工作

yourTextView.textContainerInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

0

我发现了另一种方法,即从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);
}

0

textView滚动还会影响文本的位置,并使其看起来不垂直居中。通过禁用滚动并将顶部插图设置为0,我设法使文本在视图中居中:

    textView.scrollEnabled = NO;
    textView.textContainerInset = UIEdgeInsetsMake(0, textView.textContainerInset.left, textView.textContainerInset.bottom, textView.textContainerInset.right);

由于某种原因,我还没有弄清楚,在开始键入之前光标仍未居中,但是在我开始键入时,文本立即居中。


0

如果要设置HTML字符串并避免底部填充,请确保未使用块标签,例如div,p。

就我而言,这就是原因。您可以通过使用span标签替换出现的block标签来轻松测试它。


0

对于SwiftUI

如果要使用自己的TextView UIViewRepresentable并希望控制填充,请在makeUIView函数中执行以下操作:

uiTextView.textContainerInset = UIEdgeInsets(top: 10, left: 18, bottom: 0, right: 18)

或任何你想要的。


-4
[firstNameTextField setContentVerticalAlignment:UIControlContentVerticalAlignmentCenter];
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.