计算UITextView中的行数,按帧大小包装的行

时间:2011-04-29 20:58:48

标签: ios objective-c cocoa-touch uitextview

我想知道文本视图的框架包装文本的时间是否有任何分隔符,我们可以使用它来识别文本是否被包装。

例如,如果我的文本视图的宽度为50像素且文本超出该宽度,则会将文本包装到下一行。

我想计算文字视图中的行数。现在“\ n”和“\ r”对我没有帮助。

我的代码是:

NSCharacterSet *aCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"\n\r"];
    NSArray *myArray = [textViewText componentsSeparatedByCharactersInSet:aCharacterSet];
    NSLog(@"%d",[myArray count]);

14 个答案:

答案 0 :(得分:27)

此变体会考虑如何包裹线条以及UITextView的最大尺寸,并可输出更精确的高度。例如,如果文本不适合,它将截断为可见大小,如果你包装整个单词(这是默认值),它可能会产生比你更多的行。

UIFont *font = [UIFont boldSystemFontOfSize:11.0];
CGSize size = [string sizeWithFont:font 
                      constrainedToSize:myUITextView.frame.size 
                      lineBreakMode:UILineBreakModeWordWrap]; // default mode
float numberOfLines = size.height / font.lineHeight;

答案 1 :(得分:21)

您需要使用lineHeight属性和字体lineHeight

<强>目标C

int numLines = txtview.contentSize.height / txtview.font.lineHeight;

<强>夫特

let numLines = (txtview.contentSize.height / txtview.font.lineHeight) as? Int

我获得了正确的行数,希望它也能为您提供帮助。

答案 2 :(得分:15)

斯威夫特3:

let layoutManager:NSLayoutManager = textView.layoutManager
let numberOfGlyphs = layoutManager.numberOfGlyphs
var numberOfLines = 0
var index = 0
var lineRange:NSRange = NSRange()

while (index < numberOfGlyphs) {
    layoutManager.lineFragmentRect(forGlyphAt: index, effectiveRange: &lineRange)
    index = NSMaxRange(lineRange);
    numberOfLines = numberOfLines + 1
}

print(numberOfLines)

答案 3 :(得分:9)

我在Apple的Text Layout Programming Guide找到了解决这个问题的完美解决方案。以下是Apple提供的解决方案:

NSLayoutManager *layoutManager = [textView layoutManager];
unsigned numberOfLines, index;
unsigned numberOfGlyphs = [layoutManager numberOfGlyphs];
NSRange lineRange;

for (numberOfLines = 0, index = 0; index < numberOfGlyphs; numberOfLines++){
    (void) [layoutManager lineFragmentRectForGlyphAtIndex:index effectiveRange:&lineRange];
    index = NSMaxRange(lineRange);
}

这可以很容易地写入UITextView的扩展,或作为一个独立的方法,将UITextView对象作为参数

答案 4 :(得分:8)

快速扩展:

使用@himanshu padia回答

//MARK: - UITextView
extension UITextView{

    func numberOfLines() -> Int{
        if let fontUnwrapped = self.font{
            return Int(self.contentSize.height / fontUnwrapped.lineHeight)
        }
        return 0
    }

}

用法:yourTextView.numberOfLines()

请注意,如果由于某种原因,文本视图的字体为nil,则返回值为零。

答案 5 :(得分:7)

您需要考虑textView.textContainerInset,还需要舍入计算值,因为行号肯定是一个整数

float rawLineNumber = (textView.contentSize.height - textView.textContainerInset.top - textView.textContainerInset.bottom) / textView.font.lineHeight;
int finalLineNumber = round(rawLineNumber)

在实际情况中,您可能会看到以下结果 rawLineNumber = 3.008099 finalLineNumber = 3(3行)

答案 6 :(得分:4)

使用此(其中_text_v是您的文本视图):

-(NSInteger) linesCount {
    return _text_v.contentSize.height/_text_v.font.lineHeight;
}

答案 7 :(得分:4)

   func numberOfLines(textView: UITextView) -> Int {
    let layoutManager = textView.layoutManager
    let numberOfGlyphs = layoutManager.numberOfGlyphs
    var lineRange: NSRange = NSMakeRange(0, 1)
    var index = 0
    var numberOfLines = 0

    while index < numberOfGlyphs {
        layoutManager.lineFragmentRect(forGlyphAt: index, effectiveRange: &lineRange)
        index = NSMaxRange(lineRange)
        numberOfLines += 1
    }
    return numberOfLines
}
  

为我工作

答案 8 :(得分:3)

Swift 4 版本的 Luke Chase的 answer

let numberOfGlyphs = textView.layoutManager.numberOfGlyphs
var index = 0, numberOfLines = 0
var lineRange = NSRange(location: NSNotFound, length: 0)

while index < numberOfGlyphs {
  textView.layoutManager.lineFragmentRect(forGlyphAt: index, effectiveRange: &lineRange)
  index = NSMaxRange(lineRange)
  numberOfLines += 1
}

答案 9 :(得分:3)

改进了 Luke Chase's answer并将其更新为Swift 5,XCode 11,iOS 13以获取文本视图行数和自动调整表格视图单元格高度。

  1. 您可以使用具有静态单元格高度的情节提要来进行设计。使UITextView 滚动启用:false (禁用滚动)。

  2. 在viewDidLoad中添加您的估计行高和textView委托。

override func viewDidLoad() {
        super.viewDidLoad()

        quoteTextView.delegate = self
        tableView.estimatedRowHeight = 142

    }
  1. 为heightForRowAt添加表视图委托:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableView.automaticDimension
}
  1. 当用户输入文本时,遵循UITextViewDelegate进行监听。
extension ViewController: UITextViewDelegate {
    func textViewDidChange(_ textView: UITextView) {
        // Refresh tableView cell
        if textView.numberOfLines > 2 { // textView in storyboard has two lines, so we match the design
            // Animated height update
            DispatchQueue.main.async {
                self.tableView?.beginUpdates()
                self.tableView?.endUpdates()
            }
        }
    }
}
  1. 添加UITextView扩展名,以便避免冗余代码并在整个应用程序中使用。
extension UITextView {
    var numberOfLines: Int {
        // Get number of lines
        let numberOfGlyphs = self.layoutManager.numberOfGlyphs
        var index = 0, numberOfLines = 0
        var lineRange = NSRange(location: NSNotFound, length: 0)

        while index < numberOfGlyphs {
            self.layoutManager.lineFragmentRect(forGlyphAt: index, effectiveRange: &lineRange)
          index = NSMaxRange(lineRange)
          numberOfLines += 1
        }

        return numberOfLines
    }
}

->不要忘记禁用uitextview滚动。干杯!<-

预览

enter image description here

答案 10 :(得分:3)

对于那些具有动态字体大小,或混合或不同字体大小以进行丰富消息传递的用户。您可以使用 NSLayoutManager 准确计算其他人正确指出的行高。

一个额外的问题是一个简单的实现,没有考虑到 Dmitry Petukhov 指出的新行。您可以使用布局管理器通过检查 layoutManager.extraLineFragmentRect 根据文档 NSLayoutManager#extraLineFragmentRect

<块引用>

在额外的线段矩形中包围插入点的矩形

矩形在其 NSTextContainer 的坐标系中定义。如果没有多余的线段矩形,则为 NSZeroRect。

所以我们只需要检测何时填充 extraLineFragmentUsedRect (!= CGRect.zero) - 这意味着有一个换行符作为结尾,并相应地更正行数。

完整示例如下。

extension UITextView {
    public var lineCount: Int {
        let numberOfGlyphs = layoutManager.numberOfGlyphs
        var index = 0, numberOfLines = 0
        var lineRange = NSRange(location: NSNotFound, length: 0)

        while index < numberOfGlyphs {
            layoutManager.lineFragmentRect(forGlyphAt: index, effectiveRange: &lineRange)
            index = NSMaxRange(lineRange)
            numberOfLines += 1
        }
        // Take into account newlines at the bottom.
        if layoutManager.extraLineFragmentUsedRect != CGRect.zero {
            numberOfLines += 1
        }
        return numberOfLines
    }
}

答案 11 :(得分:1)

我花了很多时间试图实时计算输入TextView的行数(例如聊天文本输入框),并且在这种情况下唯一可行的解​​决方案是Luke Chase的解决方案(出于某种原因,该框架.height方法似乎只会更新键入到textView中的第三个或第四个字母,因此不准确)。

但是,正如评论者所提到的,存在一个小错误,其中没有正确考虑用户生成的换行符(“ \ n”或键盘回车键)。甚至更陌生,它只会“错过”第一个这样的换行符,任何后续的换行符都会被正确捕获(例如,您进入该行4次,它将仅返回明显缺少第一个换行符的3行)。

为解决该错误,我简单地记录了第一个这样的换行符(字符“ \ n”),并在gliph方法返回的#行中手动添加了一行。

代码中提供:

    func offSetTableViewIfNeeded() {
    let numberOfGlyphs = textView.layoutManager.numberOfGlyphs
    var index : Int = 0
    var lineRange = NSRange(location: NSNotFound, length: 0)
    var currentNumOfLines : Int = 0
    var numberOfParagraphJump : Int = 0

    while index < numberOfGlyphs {
        textView.layoutManager.lineFragmentRect(forGlyphAt: index, effectiveRange: &lineRange)
        index = NSMaxRange(lineRange)
        currentNumOfLines += 1

        // Observing whether user went to line and if it's the first such line break, accounting for it.
        if textView.text.last == "\n", numberOfParagraphJump == 0 {
            numberOfParagraphJump = 1
        }
    }

    currentNumOfLines += numberOfParagraphJump

    print("Number of lines is:", currentNumOfLines)

希望这可以帮助那些一直在努力应对输入textView的超奇怪行为的人(无法理解为什么Apple不提供开箱即用的方法!)。

答案 12 :(得分:1)

Swift 5.0

extension UITextView {
    func sizeFit(width: CGFloat) -> CGSize {
        let fixedWidth = width
        let newSize = sizeThatFits(CGSize(width: fixedWidth, height: .greatestFiniteMagnitude))
        return CGSize(width: fixedWidth, height: newSize.height)
    }

    func numberOfLine() -> Int {
        let size = self.sizeFit(width: self.bounds.width)
        let numLines = Int(size.height / (self.font?.lineHeight ?? 1.0))
        return numLines
    }
}

答案 13 :(得分:0)

我认为您可以尝试使用NSLayoutManager

NSLayoutManager *layoutManager = [textView layoutManager];
unsigned numberOfLines, index, numberOfGlyphs =
        [layoutManager numberOfGlyphs];
NSRange lineRange;
for (numberOfLines = 0, index = 0; index < numberOfGlyphs; numberOfLines++){
    (void) [layoutManager lineFragmentRectForGlyphAtIndex:index
            effectiveRange:&lineRange];
    index = NSMaxRange(lineRange);
}

参考

CountLines