繁体   English   中英

自调整tableview单元格中UILabel中的行数

[英]Number of lines in UILabel in self-sizing tableview cell

EDIT 2 -I能够通过使用不同问题( https://stackoverflow.com/a/14413484/2584268 )中的不同方法使其工作,但我也将它与GeneratorOfOne提供的代码相结合,我在下面发布了我的结果。 尽管如此,我仍然遇到了最终目标的问题。 目标是在每行文本旁边添加行号,类似于代码编辑器。 问题是我需要保持一定数量的行,所以如果第一个单元格中有3行,则第二个单元格中的行号应该从4开始,依此类推,但我不相信我能做到这一点因为在单元格中计算行数,所以我不能将值返回到tableview来设置第二个标签的文本(行号)......任何想法?

override func layoutSubviews() {
        super.layoutSubviews()
        calculateNumberOfLines()
    }

    var textDetail: String?  {
        didSet {
            gameInfo.text = textDetail
        }
    }

    func calculateNumberOfLines() {

        let layoutManager = NSLayoutManager()

        let textStorage = NSTextStorage(string: self.gameInfo!.text!)
        textStorage.addAttribute(NSFontAttributeName, value: self.gameInfo!.font, range: NSMakeRange(0, textStorage.length))

        let textContainer = NSTextContainer(size: CGSize(width:self.contentView.bounds.size.width - 56.0, height: CGFloat.max))
        layoutManager.addTextContainer(textContainer)
        layoutManager.textStorage = textStorage
        if let text = gameInfo?.text {

            let numberOfLines = getLinesArrayOfStringInLabel(gameInfo)
            lineNumbers.text = "\(startingNum)"
            for index in startingNum+1...startingNum+numberOfLines {

                lineNumbers.text = lineNumbers.text?.stringByAppendingString("\n\(index)")

            }

            endingNum = startingNum+numberOfLines
            //let numberOfLines = totalNumberOfLinesIn(text, currentGlyphIndex:0, currentLineNumber: 1, layoutManager: layoutManager, textContainer: textContainer)
            //lineNumbers.text = "\(numberOfLines)"

        } else { return }


    }

    func getLinesArrayOfStringInLabel(label: UILabel) -> Int {

        var text = label.text as! NSString
        var font = label.font
        var rect = label.frame

        var myFont = CTFontCreateWithName(font.fontName, font.pointSize, nil)
        var attrString = NSMutableAttributedString(string: text as String)
        attrString.addAttribute(String(kCTFontAttributeName), value: myFont, range: NSMakeRange(0, attrString.length))

        let frameSetter = CTFramesetterCreateWithAttributedString(attrString)

        var path = CGPathCreateMutable()
        CGPathAddRect(path, nil, CGRectMake(0,0,rect.size.width,100000))
        var frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, nil)

        var lines = CTFrameGetLines(frame) as NSArray
        var linesArray = NSMutableArray()

        for line in lines as [AnyObject] {

            var lineRef = line as! CTLineRef
            var lineRange = CTLineGetStringRange(lineRef)
            var range = NSMakeRange(lineRange.location, lineRange.length)

            var lineString = text.substringWithRange(range)
            linesArray.addObject(lineString)

        }

        return linesArray.count
    }

我有一个自定义UITableViewCell子类,使用自定义单元格。 细胞的大小是完美的,标签正在正确扩展。 但是,在运行时我试图计算标签将扩展到的行数(我需要显示标签旁边的行号),我无法实现这一点。 我尝试了以下两种方法,似乎没有返回我在应用运行时看到的正确行数:

func lineCountForText(string: String, label: UILabel) -> Int {

        let font: UIFont = UIFont(name: "SourceCodePro-Regular", size: label.font.pointSize)!

        let rect = string.boundingRectWithSize(CGSizeMake(label.frame.width, CGFloat(MAXFLOAT)), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [font : NSFontAttributeName], context: nil)

        return Int(ceil(rect.size.height/font.lineHeight))

    }

    func numberOfLinesForString(string: String, size: CGSize, font: UIFont) -> Int {
        let textStorage = NSTextStorage(string: string, attributes: [NSFontAttributeName: font])

        let textContainer = NSTextContainer(size: size)
        textContainer.lineBreakMode = .ByWordWrapping
        textContainer.maximumNumberOfLines = 0
        textContainer.lineFragmentPadding = 0

        let layoutManager = NSLayoutManager()
        layoutManager.textStorage = textStorage
        layoutManager.addTextContainer(textContainer)

        var numberOfLines = 0
        var index = 0
        var lineRange : NSRange = NSMakeRange(0, 0)
        for (; index < layoutManager.numberOfGlyphs; numberOfLines++) {
            layoutManager.lineFragmentRectForGlyphAtIndex(index, effectiveRange: &lineRange)
            index = NSMaxRange(lineRange)
        }

        return numberOfLines
    }

我不能这样做,因为它是一个自我调整的细胞?

编辑 - 这是我正在使用的文本数组:

var randomSizedTexts = [
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
        "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur?",
        "Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem",
        ", sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora",
        "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident",
        "Et harum quidem rerum facilis est et expedita distinctio",
        "Mauris id efficitur sapien. Nunc lobortis nisi ut ultricies scelerisque. Curabitur accumsan sit amet lacus in finibus. Aliquam dolor ante, rhoncus sit amet fermentum et, semper sit amet nisi. Proin pretium velit ut quam mollis fringilla. Nullam neque risus, vestibulum eget tortor sit amet, suscipit ultricies metus. In tortor ipsum, feugiat lacinia leo id, pulvinar lacinia velit. Suspendisse sit amet porta tellus, et scelerisque odio. Nam convallis sodales congue. Proin vel quam id arcu nisi non.",

    ]

尝试使用NSAttributedString

func lineCountForLabel(label: UILabel) -> Int {

    let font: UIFont = label.font

    let attribtedString = label.attributedText.mutableCopy() as! NSMutableAttributedString
    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.lineBreakMode = label.lineBreakMode
    attribtedString.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: NSRange(location: 0, length: attribtedString.length))

    let rect = attribtedString.boundingRectWithSize(CGSizeMake(label.frame.width, CGFloat(MAXFLOAT)),
        options: .UsesLineFragmentOrigin | .UsesFontLeading,
        context: nil)

    return Int(ceil(rect.size.height/font.lineHeight))
}

更新:

你可以用速记形式写一个字典的类型为[Key:Value] ,所以这一行不正确:

let rect = string.boundingRectWithSize(xx, xx, xx, attributes: [font : NSFontAttributeName], context: nil)

更改:

[font : NSFontAttributeName]

至:

[NSFontAttributeName : font]

更新 (更新问题后)

您应该在dataSource中存储行号,如下所示:

class MyCell: UITableViewCell {

    @IBOutlet var contentLabel: UILabel!
    @IBOutlet private var numberLabel: UILabel!
}

class MasterViewController: UITableViewController {

    var objects = [AnyObject]()

    override func viewDidLoad() {
        super.viewDidLoad()

        let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:")
        self.navigationItem.rightBarButtonItem = addButton

        self.tableView.estimatedRowHeight = 44
        self.tableView.rowHeight = UITableViewAutomaticDimension

        self.insertNewObject(self)
        self.insertNewObject(self)
        self.insertNewObject(self)
        self.insertNewObject(self)
        self.insertNewObject(self)
        self.insertNewObject(self)
        self.insertNewObject(self)
        self.insertNewObject(self)
        self.insertNewObject(self)
        self.insertNewObject(self)
    }

    func insertNewObject(sender: AnyObject) {
        var count = Int(arc4random_uniform(20)) + 1

        var string = ""
        while (count-- > 0) {
            string += "This is a test."
        }
        objects.insert(["content" : string, "lineNumber" : 0], atIndex: 0)
    }

    // MARK: - Table View

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return objects.count
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! MyCell

        var object = objects[indexPath.row] as! [String : AnyObject]

        var startLineNumber = 0
        if indexPath.row > 0 {
            var pObject = objects[indexPath.row - 1] as! [String : AnyObject]
            startLineNumber = pObject["lineNumber"] as! Int
        }

        cell.contentView.setNeedsLayout()
        cell.contentView.layoutIfNeeded()

        cell.contentLabel.text = object["content"] as? String
        let lineNumber = lineCountForLabel(cell.contentLabel)  + startLineNumber
        cell.numberLabel.text = "\(lineNumber)"

        object["lineNumber"] = lineNumber
        objects[indexPath.row] = object

        return cell
    }

    func lineCountForLabel(label: UILabel) -> Int {

        let font: UIFont = label.font

        let attribtedString = label.attributedText.mutableCopy() as! NSMutableAttributedString
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.lineBreakMode = label.lineBreakMode
        attribtedString.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: NSRange(location: 0, length: attribtedString.length))

        let rect = attribtedString.boundingRectWithSize(CGSizeMake(label.frame.width, CGFloat(MAXFLOAT)),
            options: .UsesLineFragmentOrigin | .UsesFontLeading,
            context: nil)

        return Int(ceil(rect.size.height/font.lineHeight))
    }

}

更新

您的演示中存在一些问题。

更改

lastNumUsed = 1

lastNumUsed = 0

并改变

for index in lastNumUsed+1...lastNumUsed+numLines

for index in lastNumUsed+1...lastNumUsed+numLines-1

最后,问题的关键是gameInfo的大小是否正确。 当执行它的superview的cellForRow可能是nil时,因此单元格和gameInfo大小不正确。 相反,你应该计算willDisplayCell的行数, willDisplayCell所示:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    var cell: LineNumberSubtitleCell! = tableView.dequeueReusableCellWithIdentifier("GameCell", forIndexPath: indexPath) as! LineNumberSubtitleCell

    if cell == nil {
        cell = LineNumberSubtitleCell(style: UITableViewCellStyle.Default, reuseIdentifier: "GameCell")
    }

    cell.gameInfo.text = randomSizedTexts[indexPath.row]

    return cell
}

override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
    let cell = cell as! LineNumberSubtitleCell

    cell.contentView.setNeedsLayout()
    cell.contentView.layoutIfNeeded()

    let numLines = getLinesArrayOfStringInLabel(cell.gameInfo)
    cell.lineNumbers.text = "\(lastNumUsed)"

    if !(lineNumbersList.count > indexPath.row) {

        for index in lastNumUsed+1...lastNumUsed+numLines-1 {

            cell.lineNumbers.text = cell.lineNumbers.text?.stringByAppendingString("\n\(index)")

            if index == lastNumUsed+numLines-1 {

                lastNumUsed = index+1
                lineNumbersList.addObject(cell.lineNumbers.text!)
            }
        }
    } else {
        cell.lineNumbers.text = lineNumbersList.objectAtIndex(indexPath.row) as? String
    }
}

调用setNeedsLayout方法会强制视图更新其布局。 调用layoutIfNeeded方法强制布局系统立即运行。 所以gameInfo的大小应该重新计算才能纠正。

您可以将labelHeight / 12.0f用于行数,其中12.0f是使用的字体大小。

这是一个简单的例子,它计算文本中的行数。

该方法以递归方式查找字形索引,并获得一个linefragment并比较index处的字形是否是最后一个。

class TableViewCell: UITableViewCell {

    var index: Int = 0

    var titleLabel: UILabel!
    var numberOfLinesLabel: UILabel!

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        titleLabel = UILabel(frame: CGRectZero)
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        titleLabel.numberOfLines = 0
        titleLabel.lineBreakMode = NSLineBreakMode.ByWordWrapping
        contentView.addSubview(titleLabel)

        numberOfLinesLabel = UILabel(frame: CGRectZero)
        numberOfLinesLabel.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(numberOfLinesLabel)

        contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-10-[titleLabel]-10-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["titleLabel": titleLabel]))
        contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-20-[titleLabel]-[numberOfLinesLabel(==28)]|", options: NSLayoutFormatOptions.AlignAllTop, metrics: nil, views: ["titleLabel": titleLabel, "numberOfLinesLabel": numberOfLinesLabel]))

    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    var textDetail: String?  {
        didSet {
            titleLabel.text = textDetail
        }
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        calculateNumberOfLines()

    }

    func calculateNumberOfLines() {

        let layoutManager = NSLayoutManager()

        let textStorage = NSTextStorage(string: self.titleLabel!.text!)
        textStorage.addAttribute(NSFontAttributeName, value: self.titleLabel!.font, range: NSMakeRange(0, textStorage.length))

        let textContainer = NSTextContainer(size: self.titleLabel!.bounds.size)

        layoutManager.addTextContainer(textContainer)
        layoutManager.textStorage = textStorage
        guard let text = titleLabel?.text else { return }

        let numberOfLines = totalNumberOfLinesIn(text: text, currentGlyphIndex:0, currentLineNumber: 1, layoutManager: layoutManager, textContainer: textContainer)
        numberOfLinesLabel.text = "\(numberOfLines)"
    }

    func totalNumberOfLinesIn(text text: String, currentGlyphIndex glyphIndex:Int, currentLineNumber line:Int, layoutManager: NSLayoutManager, textContainer: NSTextContainer) -> Int
    {
        let maxGlyphIndex = layoutManager.glyphRangeForTextContainer(textContainer)

        let lineFragment = layoutManager.lineFragmentRectForGlyphAtIndex(glyphIndex, effectiveRange: nil)
        let glyphRange = layoutManager.glyphRangeForBoundingRect(lineFragment, inTextContainer: textContainer)

        if NSMaxRange(glyphRange) < NSMaxRange(maxGlyphIndex) {
            return totalNumberOfLinesIn(text: text, currentGlyphIndex:glyphIndex + glyphRange.length,  currentLineNumber: (line + 1), layoutManager: layoutManager, textContainer: textContainer)
        }

        return line
    }
}


class TestViewController: UIViewController{

    static let CellIdentifier = "CellIdentifier"

    var randomSizedTexts = [
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
        "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur?",
        "Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem",
        ", sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora",
        "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident",
        "Et harum quidem rerum facilis est et expedita distinctio",
        "Mauris id efficitur sapien. Nunc lobortis nisi ut ultricies scelerisque. Curabitur accumsan sit amet lacus in finibus. Aliquam dolor ante, rhoncus sit amet fermentum et, semper sit amet nisi. Proin pretium velit ut quam mollis fringilla. Nullam neque risus, vestibulum eget tortor sit amet, suscipit ultricies metus. In tortor ipsum, feugiat lacinia leo id, pulvinar lacinia velit. Suspendisse sit amet porta tellus, et scelerisque odio. Nam convallis sodales congue. Proin vel quam id arcu nisi non.",
    ]

    lazy var tableView: UITableView! = {
        let tableView = UITableView(frame: CGRectZero, style: .Plain)
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.delegate = self
        tableView.dataSource = self
        tableView.rowHeight = UITableViewAutomaticDimension
        tableView.estimatedRowHeight = 44
        self.view.addSubview(tableView)
        self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[tableView]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["tableView": tableView]))
        self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[tableView]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["tableView": tableView]))

        return tableView
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = UIColor.whiteColor()
        tableView.registerClass(TableViewCell.self, forCellReuseIdentifier: TestViewController.CellIdentifier)
    }

}

extension TestViewController: UITableViewDataSource, UITableViewDelegate {

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier(TestViewController.CellIdentifier, forIndexPath: indexPath) as! TableViewCell
        cell.index = indexPath.row
        cell.textDetail = randomSizedTexts[indexPath.row]
        return cell
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return randomSizedTexts.count
    }
}

更新:似乎根据titleLabel大小设置NSTextContainer大小是不合适的。 由于titleLabel大小目前不太合适。 似乎手动计算可用于显示标签的边界的大小给出了正确的结果。 请查看更改。

实现所有单元格的动态高度。 这是一个例子。 在该示例中,单元格具有标签。

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    cellHeight = 40.0f; // Height excluding label height

    // Calculate label height
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 288, 0)];
    label.font = [UIFont systemFontOfSize:13];
    label.text = [details objectAtIndex:indexPath.row];
    label.numberOfLines = 0;
    [label sizeToFit];
    cellHeight += [PSIUtility heightForLabel:label];

    label.text = [tips objectAtIndex:indexPath.row];
    label.numberOfLines = 0;
    [label sizeToFit];

    cellHeight += [self heightForLabel:label];

    //Return cell Height
    return cellHeight;

}

- (CGFloat)heightForLabel:(UILabel *)label{

    //Calculate the expected size based on the font and linebreak mode of your label
    // FLT_MAX here simply means no constraint in height
    label.numberOfLines = 0;

    CGSize maximumLabelSize = CGSizeMake(label.frame.size.width, FLT_MAX);

    //    CGSize expectedLabelSize = [label.text sizeWithFont:label.font constrainedToSize:maximumLabelSize lineBreakMode:label.lineBreakMode];


    CGRect expectedLabelSize = [label.text boundingRectWithSize:maximumLabelSize
                                                        options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                                     attributes:@{NSFontAttributeName:label.font}
                                                        context:nil];

    return expectedLabelSize.size.height;

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM