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

sni*_*kst 470 iphone cocoa-touch uitextview uikit ios

UITextView我的iOS应用程序中有一个显示大量文本的应用程序.然后我通过使用的偏移边距参数来分页这个文本UITextView.我的问题是,填充UITextView是混乱我的计算,因为它似乎是不同的,这取决于我使用的字体大小和字体.

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

期待听到您的回复!

use*_*195 782

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

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

textView.textContainer.lineFragmentPadding = 0
Run Code Online (Sandbox Code Playgroud)

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

textView.textContainerInset = .zero
Run Code Online (Sandbox Code Playgroud)

需要两条线才能完全去除边距/填充.

  • 将`lineFragmentPadding`设置为0是我正在寻找的魔力.我不知道为什么Apple会如此难以将UITextView内容与其他控件对齐. (18认同)
  • 注意,第二行也可以写成:`self.descriptionTextView.textContainerInset = UIEdgeInsetsZero;` (7认同)
  • 这是正确的答案.此解决方案不会发生水平滚动. (3认同)
  • 这对我来说有两条线路,但是当我到达5行时,文本就会被切断. (2认同)
  • `lineFragmentPadding`不是为了修改边距.来自[docs](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSTextContainer_Class/#//apple_ref/occ/instp/NSTextContainer/lineFragmentPadding)_Line片段填充不是旨在表达文本边距.相反,您应该在文本视图上使用insets,调整段落边距属性,或在其superview中更改文本视图的位置._ (2认同)

Fat*_*tie 328

正确更新2019年

这是iOS中最愚蠢的错误之一.

这个课UITextViewFixed通常是一个合理的解决方案.

这是班级:

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

不要忘记在Inspector中关闭scrollEnabled!

  1. 该解决方案在故事板中可以正常工作

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

就是这样,你已经完成了.

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

即使您正在动态更改文本视图的高度,这通常也是您所需要的.

(动态更改高度的常见示例是在用户输入时更改它.)

这是来自Apple的破坏的UITextView ......

使用UITextView的IB的屏幕截图

这是UITextViewFixed:

带有UITextViewFixed的IB的屏幕截图

请注意,当然你必须

在Inspector中关闭scrollEnabled!

别忘了关闭scrollEnabled!:)


一些其他问题

(1)在一些不寻常的情况下 - 例如,一些具有动态变化高度的灵活单元高度表视图的情况 - Apple是世界上最令人愤怒的事情:它们在底部增加了额外的空间.不完全是!这必须是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
 ...
Run Code Online (Sandbox Code Playgroud)

(2)有时,为了解决另一个微妙的Apple麻烦,你必须添加:

override func setContentOffset(_ contentOffset: CGPoint, animated: Bool) {
    super.setContentOffset(contentOffset, animated: false)
}
Run Code Online (Sandbox Code Playgroud)

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

contentInset = UIEdgeInsets.zero
Run Code Online (Sandbox Code Playgroud)

只是之后UITextViewFixed.lineFragmentPadding = 0.

但是......相信与否......它只是在当前的iOS中不起作用!(检查在2019年.)将来可能需要添加该行.

UITextViewFixed在iOS 中被破坏的事实是所有移动计算中最奇怪的事情之一.这个问题十周年纪念日仍然没有修复!

最后,这里有一个类似文字字段的提示:https://stackoverflow.com/a/43099816/294884

  • 我在我的应用程序中使用了它,它会导致layoutSubviews和设置之间的递归调用,从而导致堆栈溢出错误!这里不对劲 (4认同)
  • 我已经发布了@Fattie 答案的更新答案,它帮助我使用启用/禁用“translatesAutoresizingMaskIntoConstraints”的特殊技巧真正摆脱了所有插图。仅凭这个答案,我无法使用自动布局删除视图层次结构中的所有边距(一些奇怪的底部边距无法消失)。它还使用“systemLayoutSizeFitting”处理视图大小的计算,之前由于“UITextView”错误而返回无效大小 (3认同)
  • 我在表中有文本视图,具有单行文本的文本视图无法正确计算其高度(自动布局)。为了解决这个问题,我必须重写 didMoveToSuperview 并在那里调用 setup 。 (2认同)

Mic*_*ael 253

这个解决方案是在2009年IOS 3.0发布时编写的.它不再适用.

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

nameField.contentInset = UIEdgeInsetsMake(-4,-8,0,0);
Run Code Online (Sandbox Code Playgroud)

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

此外,这似乎只适用于UITextViews您使用默认对象的位置,即.

nameField.textAlignment = NSTextAlignmentLeft;
Run Code Online (Sandbox Code Playgroud)

例如,与右边对齐,UIEdgeInsetsMake似乎对右边缘没有任何影响.

至少,使用.contentInset属性允许您将字段放置在"正确"的位置,并适应偏差而不会抵消您的UITextViews.

  • 在IOS 7中,我发现UIEdgeInsetsMake(0,-4,0,-4)效果最好. (15认同)
  • 我不明白为什么人们提出了一个利用硬编码值的答案.在未来的iOS版本中它很可能会破坏,这只是个坏主意. (7认同)
  • 在iOS 7中不推荐使用UITextAlignmentLeft.使用NSTextAlignmentLeft. (3认同)
  • 是的,那些是示例数字.我说"它只是我正在绘制的特定字段大小的自定义解决方案".主要是我试图说明你必须使用数字来处理你独特的布局情况. (2认同)

azd*_*dev 71

建立一些已经给出的好答案,这里是一个纯粹的基于Interface Builder的解决方案,它利用用户定义的运行时属性并在iOS 7.0+中工作:

在此输入图像描述

  • 复制/粘贴:textContainer.lineFragmentPadding | textContainerInset (13认同)
  • 这显然是最好的答案,特别是如果您需要仅XIB解决方案 (3认同)
  • 这就是做出漂亮答案的原因-即使3年之后,它也容易实现且效果很好:) (2认同)

Eng*_*epe 46

在iOS 5上UIEdgeInsetsMake(-8,-8,-8,-8);看起来效果很好.


ldo*_*ogy 41

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

这是@ user1687195的答案,在没有修改的情况下编写textContainer.lineFragmentPadding(因为文档声明这不是预期的用法).

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

self.textView.textContainerInset = UIEdgeInsetsMake(
                                      0, 
                                      -self.textView.textContainer.lineFragmentPadding, 
                                      0, 
                                      -self.textView.textContainer.lineFragmentPadding);
Run Code Online (Sandbox Code Playgroud)

这实际上是相同的结果,只是有点清洁,因为它不会滥用lineFragmentPadding属性.


Ula*_*mir 19

Storyboard或Interface Builder解决方案,使用用户定义的运行时属性. 更新.添加了iOS7.1和iOS6.1屏幕截图,其中contentInset = {{-10,-5},{0,0}} 在此输入图像描述 在此输入图像描述


mik*_*eho 16

所有这些答案都解决了标题问题,但我想针对OP问题正文中提出的问题提出一些解决方案.

文字内容的大小

计算内部文本大小的快速方法UITextView是使用NSLayoutManager:

UITextView *textView;
CGSize textSize = [textView usedRectForTextContainer:textView.textContainer].size;
Run Code Online (Sandbox Code Playgroud)

这给出了总可滚动内容,其可能比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)
Run Code Online (Sandbox Code Playgroud)

线高

UIFont有一个属性,可以快速获取给定字体的行高.因此,您可以快速找到UITextView带有文本的行高:

UITextView *textView;
CGFloat lineHeight = textView.font.lineHeight;
Run Code Online (Sandbox Code Playgroud)

计算可见文本大小

确定实际可见的文本量对于处理"分页"效果很重要.UITextView有一个属性textContainerInset,实际上是实际UITextView.frame和文本本身之间的边距.要计算可见框架的实际高度,您可以执行以下计算:

UITextView *textView;
CGFloat textViewHeight = textView.frame.size.height;
UIEdgeInsets textInsets = textView.textContainerInset;
CGFloat textHeight = textViewHeight - textInsets.top - textInsets.bottom;
Run Code Online (Sandbox Code Playgroud)

确定分页大小

最后,既然您拥有可见的文本大小和内容,您可以通过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
Run Code Online (Sandbox Code Playgroud)

使用所有这些方法,我没有碰到我的插图,我能够去插入符号或文本中我想要的任何地方.


Him*_*dia 12

你可以使用以下textContainerInset属性UITextView:

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

(上,左,下,右)


Ans*_*Yao 9

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

captionTextView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
Run Code Online (Sandbox Code Playgroud)


Fab*_*b1n 9

这是Fattie非常有用的答案的更新版本.它增加了2条非常重要的线条,帮助我在iOS 10和11上完成了完美的布局(也可能在较低的版本上).
d

@IBDesignable class UITextViewFixed: UITextView {
    override func layoutSubviews() {
        super.layoutSubviews()
        setup()
    }
    func setup() {
        translatesAutoresizingMaskIntoConstraints = true
        textContainerInset = UIEdgeInsets.zero
        textContainer.lineFragmentPadding = 0
        translatesAutoresizingMaskIntoConstraints = false
    }
}
Run Code Online (Sandbox Code Playgroud)

重要的是两条translatesAutoresizingMaskIntoConstraints = <true/false>陈述!

在我的所有情况下,这令人惊讶地消除了所有利润!

虽然textView不是第一响应者,但可能会发生一些奇怪的底部边缘,使用sizeThatFits接受的答案中提到的方法无法解决.
当突然进入textView时,奇怪的底部边缘消失了,一切看起来都应该如此,但只有textView有了firstResponder.

所以我在这里读到translatesAutoresizingMaskIntoConstraints,在调用之间手动设置帧/边界时启用和禁用确实有帮助.

幸运的是,这不仅适用于帧设置,而且两条线路setup()之间夹有两条线translatesAutoresizingMaskIntoConstraints!

例如,在使用a计算视图的帧时非常有用.返回正确的尺寸(之前没有)!systemLayoutSizeFittingUIView

正如在原始答案中提到的:
不要忘记在Inspector中关闭scrollEnabled!
该解决方案在故事板以及运行时都能正常工作.

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


Urv*_*odi 9

最新的Swift:

self.textView.textContainerInset = .init(top: -2, left: 0, bottom: 0, right: 0)
self.textView.textContainer.lineFragmentPadding = 0
Run Code Online (Sandbox Code Playgroud)


Sha*_* Ye 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)
Run Code Online (Sandbox Code Playgroud)


P. *_*Ent 5

对于SwiftUI

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

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

或者任何你想要的。