3

EDIT 2-I was able to get it to work by using a different method found in a different question (https://stackoverflow.com/a/14413484/2584268), but I also combined it with the code provided by GeneratorOfOne, and I have posted my result below. I am still running into an issue with the end goal of this, though. The goal is to have line numbers next to each line of text, sort of like in a code editor. The problem is that I need to keep a running number of lines, so if there are 3 lines in the first cell, the line numbers in the second cell should start at 4 and so on, but I don't believe I can do this because the number of lines is being calculated in the cell so I cannot return a value back to the tableview to set the text of the second label (line numbers)...any ideas?

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
    }

I have a custom UITableViewCell subclass that is using self-sizing cells. The sizing of the cells is perfect and the label is being extended correctly. However, at run time I am trying to calculate the number of lines that the label will be extended to (I need to display the line numbers next to the label), and I am not able to achieve this. I have tried the following two methods and neither seem to be returning the correct number of lines that I am seeing when the app runs:

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
    }

Am I not able to do this because it is a self-sizing cell?

EDIT-Here is the array of text that I am using:

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.",

    ]
Community
  • 1
  • 1
Jacob
  • 2,338
  • 2
  • 22
  • 39

4 Answers4

3

Trying to use 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))
}

UPDATE:

You can write the type of a dictionary in shorthand form as [Key: Value], so your this line is incorrect:

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

change:

[font : NSFontAttributeName]

to:

[NSFontAttributeName : font]

UPDATE(after the question updated)

You should store a line numbers in your dataSource like this:

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))
    }

}

UPDATE:

There are a few problems in your demo.

Change

lastNumUsed = 1

to

lastNumUsed = 0

and change

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

to

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

Finally, the point of the issue is the gameInfo's size is correct or not. When the cellForRow executing it's superview maybe a nil, so the size of the cell and the gameInfo is incorrect. Instead, you should calculate the number of lines in willDisplayCell like so:

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

Calling the setNeedsLayout method forces your view to update its layout. Calling the layoutIfNeeded method force the layout system to be run now. So the gameInfo's size should be recalculated to correct.

Bannings
  • 10,376
  • 7
  • 44
  • 54
  • This seems to work, and after doing some extra logging, it looks like my methods were working as well. The thing is, the methods get called twice as much as they should. The first time they are called (once for each cell), they return the correct number, then for some reason, they are called again and return an incorrect value, and this was the log statement I was seeing in my logs, the incorrect line numbers, but I need to figure out why it's getting called twice. I am using my tableview in a scrollview to swipe left/right between multiple VCs, I think this could be why. – Jacob Jun 29 '15 at 15:24
  • I've implemented similar things in https://github.com/zhangao0086/DKTabPageViewController – Bannings Jun 29 '15 at 15:40
  • I am using https://github.com/malcommac/DMCircularScrollView which I modified a little bit – Jacob Jun 29 '15 at 15:42
  • I believe this issue is related to the "viewsFromIndex" method as that's where my log statements are before the label methods are called twice. – Jacob Jun 29 '15 at 15:43
  • I think you should make sure the `lineCountForText` always return the correct number. In other words, the point of the issue is that the string and the label are correct or not. – Bannings Jun 29 '15 at 17:20
  • I was able to ensure the numbers were correct by saving them into an array the first time the method is called for each cell, then when/if it is called again, it checks to make sure that spot in the array is filled, and moves along.It seems a little wacky IMO, but I guess it'll do for now...any better ideas? – Jacob Jun 29 '15 at 22:17
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/81923/discussion-between-bannings-and-jacob). – Bannings Jun 30 '15 at 03:30
  • I have not solved it yet, I am still struggling to get it to work on all devices. – Jacob Jul 02 '15 at 13:52
  • Thank you for looking over the demo, I appreciate it. Everything seems to be working correctly in your code, thanks very much! I will mark your answer as correct and award you the bounty. Just a quick question...if I wanted to implement this with section headers as well, could I do that? For example, if the first section ended with "49" as the last line number, how could I make the section header for the next section display "50" and the first cell in that section start with "51"? – Jacob Jul 02 '15 at 23:06
  • Just a few easy steps. When you are calculating the number of lines(`willDisplayCell`), add `lastNumUsed!++` if the row of the section is 0. And the `lastNumUsed` will be initialized to 1… In addition, you should set lineNumbers's text to nil in `cellForRow` and update its text after the lastNumUsed be updated. Do you hope that I update my answer? – Bannings Jul 03 '15 at 01:50
  • Thanks, but how can I reflect this change and set the title of each header without having to reload the table view again? – Jacob Jul 03 '15 at 02:36
  • I am having issues getting this to work with more than one section as I am holding the strings in an array, but when I have more than one array, the numbers do not fill in correctly. The issue seems to arise when the user starts to scroll through the table. The method starts returning incorrect line numbers for the labels, and the line numbers on the side are incorrect as a result. – Jacob Jul 07 '15 at 01:48
1

You can use labelHeight/12.0f for number of lines where 12.0f is the font size being used.

Malik Boy
  • 132
  • 10
1

Here is a simple example which counts the number of lines in a text.

The method goes in recursively to find the glyph index, and get a linefragment and compare if the glyph at index is the last one.

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

Update: It seems like setting the NSTextContainer size based on the titleLabel size is not appropriate. Since the titleLabel size is not quite appropriate at this time. It seems like manually calculating the size for the bounds available to display the label gives correct result. Please see the changes.

Sandeep
  • 20,908
  • 7
  • 66
  • 106
  • Thank you for the very detailed answer, I really appreciate it! I just implemented your idea into my project and it works for the most part, but as I scroll through the table, some of the line numbers are off by one. It is either one more than the actual number of lines or one less. What could be causing this? Could there be extra space at the end that is being pushed over to the next line or something? Does it have to do with the font I'm using? – Jacob Jul 01 '15 at 14:09
  • I have tried making the text you provided a little smaller and I was able to reproduce the incorrect line numbers, I will edit my question with the array that I used... – Jacob Jul 01 '15 at 14:30
  • @Jacob Please see my edit. This should solve your issue :) – Sandeep Jul 01 '15 at 15:52
  • Ok, how have you used it ? How do you calculate the bounds to assign to NSTextContainer. – Sandeep Jul 01 '15 at 17:57
  • I dont remember what the issue was exactly but giving the bounds size of the label to the NSTextContainer fixes the issue. Could you try that. – Sandeep Jul 01 '15 at 18:11
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/82142/discussion-between-jacob-and-generatorofone). – Jacob Jul 01 '15 at 23:07
  • I just edited my question with the code I have working now, it seems to be returning the line numbers correctly. I am still running into an issue with the end goal of this, though. The goal is to have line numbers next to each line of text, sort of like in a code editor. – Jacob Jul 01 '15 at 23:36
  • The problem is that I need to keep a running number of lines, so if there are 3 lines in the first cell, the line numbers in the second cell should start at 4 and so on, but I don't believe I can do this because the number of lines is being calculated in the cell so I cannot return a value back to the tableview to set the text of the second label (line numbers)...any ideas? – Jacob Jul 01 '15 at 23:36
  • Oh yeah that is perfectly possible. – Sandeep Jul 02 '15 at 02:38
  • How would I go about doing that? – Jacob Jul 02 '15 at 02:56
  • Just move the code which does the calculation to view controller. Then in cellForRowAtIndePath, simply set the titleLabel text and call layoutIfNeeded to the cells contentView. Then do the calculation for the number of lines. – Sandeep Jul 02 '15 at 03:07
  • Thanks...I now have the code in the VC, but it does not seem to be working correctly when I run it on different simulators. When I run on iPhone 6 or newer, the line numbers are increased by 1 for some reason... – Jacob Jul 02 '15 at 13:11
  • I also find this very interesting...my cellForRow method is called twice for each row, and on the iPhone 5 simulator, the second time it is called the line numbers are incorrect, and on the iPhone 6 simulator the second time it is called the line numbers are correct, but the first time the yard incorrect, but I dont know how to distinguish between them without logging. This is why I liked having the method called when the cell was created because what the method returned was always correct. – Jacob Jul 02 '15 at 13:16
  • Here is my implementation: https://www.dropbox.com/s/z998c3ry4e9hrk0/Implementation.zip?dl=0 – Jacob Jul 02 '15 at 13:53
0

Implement dynamic height for all cells. Here is an example. In this example cell is having a label.

-(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;

}
Ram Suthar
  • 356
  • 2
  • 9
  • This does not answer my question, and it is actually the incorrect way to implement self resizing cells. I am using dynamic cell height with UITableviewAutomaticDimension and I want to find the number of lines that the label will have, not the height. – Jacob Jun 25 '15 at 13:34