539

我的UITextViewiOS 应用程序中有一个,它显示大量文本。

然后,我使用UITextView.

我的问题是填充UITextView使我的计算感到困惑,因为它似乎因我使用的字体大小和字体而异。

是否可以删除围绕内容的填充UITextView

4

24 回答 24

819

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

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

textView.textContainer.lineFragmentPadding = 0

这会导致文本的顶部与容器的顶部对齐:

textView.textContainerInset = .zero

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

于 2013-09-24T17:05:03.450 回答
460

2021 年最新版本

它是 iOS 中最愚蠢的错误之一。

这里给出的类UITextViewFixed, 被广泛使用,通常是总体上最合理的解决方案

这是课程:

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

不要忘记在 Inspector 中关闭 scrollEnabled!

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

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

就是这样,你完成了。

一般来说,在大多数情况下,这应该就是您所需要的

即使您正在动态更改文本视图的高度通常UITextViewFixed也可以满足您的所有需求。

(动态更改高度的一个常见示例是在用户键入时更改它。)

这是 Apple 损坏的 UITextView ......

带有 UITextView 的界面生成器的屏幕截图

这里是UITextViewFixed

带有 UITextViewFixed 的界面生成器的屏幕截图

请注意,您当然必须...

...在 Inspector 中关闭 scrollEnabled!

(开启 scrollEnabled 的意思是“通过尽可能扩大底部边距,让这个视图尽可能垂直扩大。”)


一些进一步的问题

(1) 在一些非常不寻常的情况下动态改变高度,苹果做了一件奇怪的事情:他们在底部增加了额外的空间

不完全是!这一定是 iOS 中最令人愤怒的事情之一。

如果您遇到问题,这里有一个“快速修复”,通常会有所帮助:

...
        textContainerInset = UIEdgeInsets.zero
        textContainer.lineFragmentPadding = 0

        // this is not ideal, but sometimes this "quick fix"
        // will solve 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) // sic
}

(3) 可以说,我们应该添加:

contentInset = UIEdgeInsets.zero

.lineFragmentPadding = 0UITextViewFixed.

但是......信不信由你......这在当前的iOS中不起作用!(检查 2021。)将来可能需要添加该行。

在 iOS 中被破坏的事实UITextView是所有移动计算中最奇怪的事情之一。这个问题十周年了,它仍然没有解决!

最后,这里有一个类似 Text Field的提示:Set the maximum character length of a UITextField in Swift

完全随机提示:如何在末尾添加“...”

您经常使用“像 UILabel”一样的 UITextView。因此,您希望它使用省略号“...”截断文本

如果是这样,请添加:

 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 添加填充的完整讨论:向 UILabel添加空间/填充。在某些情况下,如果您正在使用动态高度单元格进行困难的布局,有时最好使用 UILabel 进行困难的布局。

于 2017-02-19T22:45:34.307 回答
259

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

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

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

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

此外,这似乎只适用于UITextViews您使用默认对齐方式的地方,即

nameField.textAlignment = NSTextAlignmentLeft;

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

至少,使用 .contentInset 属性允许您将字段放置在“正确”的位置,并在不偏移您的UITextViews.

于 2009-07-26T18:59:52.760 回答
87

基于已经给出的一些好的答案,这里是一个纯粹的基于 Storyboard / Interface Builder 的解决方案,适用于 iOS 7.0+

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

textContainer.lineFragmentPadding
textContainerInset

界面生成器

于 2015-02-04T00:58:53.093 回答
47

在 iOS 5 上UIEdgeInsetsMake(-8,-8,-8,-8);似乎工作得很好。

于 2012-05-08T12:36:13.957 回答
43

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

这是user1687195 的答案,没有修改textContainer.lineFragmentPadding(因为文档说明这不是预期的用途)。

这适用于 iOS 7 及更高版本。

self.textView.textContainerInset = UIEdgeInsetsMake(
                                      0,
                                      -self.textView.textContainer.lineFragmentPadding,
                                      0,
                                      -self.textView.textContainer.lineFragmentPadding);

这实际上是相同的结果,只是更简洁一点,因为它不会滥用 lineFragmentPadding 属性。

于 2016-04-24T18:36:34.113 回答
25

使用用户定义的运行时属性的Storyboard 或 Interface Builder 解决方案:

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

用户定义的运行时属性

输出

于 2013-09-05T09:02:31.293 回答
17

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

确定分页大小

最后,现在您有了可见的文本大小和内容,您可以通过从 中减去 来快速确定偏移textHeighttextSize

// 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

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

于 2015-06-26T15:31:44.480 回答
15

您可以使用 的textContainerInset属性UITextView

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

(上、左、下、右)

于 2015-05-05T05:24:57.290 回答
13

这是Fattie 非常有用的答案的更新版本。它添加了两条重要的线,帮助我让布局在 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.

因此,我在 Stack Overflow 上读到translatesAutoresizingMaskIntoConstraints,在调用之间手动设置框架/边界时,启用和禁用确实有帮助。

幸运的是,这不仅适用于框架设置,还适用于setup()夹在两个translatesAutoresizingMaskIntoConstraints调用之间的两条线!

例如,这在使用on计算视图框架时非常有用。它会返回正确的大小(以前没有)!systemLayoutSizeFittingUIView

正如在原始答案中提到的:

不要忘记在 Inspector 中关闭 scrollEnabled!该解决方案在情节提要中以及在运行时都可以正常工作。

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

于 2018-02-14T14:37:43.460 回答
12

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

captionTextView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
于 2016-10-12T06:38:16.090 回答
12

最新的斯威夫特:

self.textView.textContainerInset = .init(top: -2, left: 0, bottom: 0, right: 0)
self.textView.textContainer.lineFragmentPadding = 0
于 2018-05-24T07:54:33.497 回答
6

对于 Swift 4、Xcode 9

使用以下功能。它可以改变 UITextView 中文本的边距/填充:

public func UIEdgeInsetsMake(_ top: CGFloat, _ left: CGFloat, _ bottom: CGFloat, _ right: CGFloat) -> UIEdgeInsets

所以在这种情况下是:

 self.textView?.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
于 2017-10-26T16:54:33.653 回答
4

做插入解决方案我仍然在右侧和底部有填充。文本对齐也引起了问题。我发现的唯一可靠的方法是将文本视图放在另一个裁剪到边界的视图中。

于 2011-03-31T23:49:05.607 回答
4

这是一个简单的小扩展,它将从您的应用程序中的每个文本视图中删除 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);
   }
}
于 2018-03-04T00:19:15.610 回答
3

对于SwiftUI

如果您正在使用自己的 TextViewUIViewRepresentable并想要控制填充,请在您的makeUIView函数中执行以下操作:

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

或任何你想要的。

于 2020-05-06T17:39:29.343 回答
2

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

于 2018-06-17T11:20:45.747 回答
2

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

yourTextView.textContainerInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
于 2019-05-16T10:44:46.133 回答
1

我发现了另一种方法。从 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);
}
于 2013-02-28T10:17:28.253 回答
1

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

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

出于某种原因,我还没有弄清楚,在打字开始之前光标仍然没有居中,但是当我开始打字时,文本立即居中。

于 2016-12-25T19:37:05.553 回答
1

如果您想设置 HTML 字符串并避免底部填充,请确保您没有使用块标签,即divp.

就我而言,这就是原因。您可以通过将出现的块标签替换为例如标签来轻松测试它span

于 2020-01-03T17:07:52.080 回答
1

您需要在 didMoveToSuperView 上设置 inset 和 lineFragmentPadding

@IBDesignable class UITextViewFixed: UITextView {
    override func didMoveToSuperview() {
        super.didMoveToSuperview()
        setup()
    }
    func setup() {
        textContainerInset = UIEdgeInsets.zero
        textContainer.lineFragmentPadding = 0
    }
}
于 2021-06-15T14:39:54.073 回答
1

对于因为将 HTML 解析为 UITextView 而遇到此问题的任何人,只需删除由结束 p 标记“/n”引入的新行。我假设您使用NSMutableAttributedString

if let lastCharacter = attributedString.string.last, lastCharacter == "\n" {
    attributedString.deleteCharacters(in: NSRange(location:(attributedString.length) - 1, length:1))
}
于 2021-08-23T08:14:24.587 回答
-4
[firstNameTextField setContentVerticalAlignment:UIControlContentVerticalAlignmentCenter];
于 2011-08-11T21:48:18.497 回答