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

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

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

是否有可能移除UITextView内容周围的填充?


当前回答

2023年最新情况

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

(打开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)在极少数情况下,为了解决苹果的另一个微妙混乱,你必须补充:

override func setContentOffset(_ contentOffset: CGPoint, animated: Bool) {
    super.setContentOffset(contentOffset, animated: false) // sic
}

(3)可以说,我们应该补充:

contentInset = UIEdgeInsets.zero

就在UITextViewFixed中的. linefragmentpadding = 0之后。

不管你信不信,这在当前的iOS系统中并不适用!(检查2023。)将来可能有必要添加这一行。

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

最后,这里有一个有点类似的提示TextField:设置一个UITextField的最大字符长度在Swift

完全随机的提示:如何在结尾加上“…”

通常你会像使用UILabel一样使用UITextView。所以你想用省略号"…"来截断文本

如果是,请补充:

 textContainer.lineBreakMode = .byTruncatingTail

如果你想要零高度,当,根本没有文本

通常使用文本视图只显示文本。你使用lines "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添加填充是非常困难的。

事实上,这里有一个关于如何“最终”正确地添加填充到UILabel的完整讨论:添加空格/填充到UILabel。在某些情况下,如果你正在用动态高度单元格做一个困难的布局,有时候用UILabel来做会更好。

其他回答

2023年最新情况

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

(打开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)在极少数情况下,为了解决苹果的另一个微妙混乱,你必须补充:

override func setContentOffset(_ contentOffset: CGPoint, animated: Bool) {
    super.setContentOffset(contentOffset, animated: false) // sic
}

(3)可以说,我们应该补充:

contentInset = UIEdgeInsets.zero

就在UITextViewFixed中的. linefragmentpadding = 0之后。

不管你信不信,这在当前的iOS系统中并不适用!(检查2023。)将来可能有必要添加这一行。

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

最后,这里有一个有点类似的提示TextField:设置一个UITextField的最大字符长度在Swift

完全随机的提示:如何在结尾加上“…”

通常你会像使用UILabel一样使用UITextView。所以你想用省略号"…"来截断文本

如果是,请补充:

 textContainer.lineBreakMode = .byTruncatingTail

如果你想要零高度,当,根本没有文本

通常使用文本视图只显示文本。你使用lines "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添加填充是非常困难的。

事实上,这里有一个关于如何“最终”正确地添加填充到UILabel的完整讨论:添加空格/填充到UILabel。在某些情况下,如果你正在用动态高度单元格做一个困难的布局,有时候用UILabel来做会更好。

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

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

textContainer.lineFragmentPadding
textContainerInset

你需要在didMoveToSuperView上设置inset和lineFragmentPadding

@IBDesignable class UITextViewFixed: UITextView {
    override func didMoveToSuperview() {
        super.didMoveToSuperview()
        setup()
    }
    func setup() {
        textContainerInset = UIEdgeInsets.zero
        textContainer.lineFragmentPadding = 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);
}