Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get number of lines in UITextView without contentSize.height

I'm creating an image to export in my iOS app. I am using UIGraphicsBeginImageContext to draw the elements of the image but I've been struggling drawing a UITextView in the image.

First I create a parent view for rendering:

var parentView : UIView = UIView(frame: CGRectMake(0, 0, 700, 700))

Then I programatically create the textView and add all the properties. I give it x=100 and y=100 to align the text in the image. Width = 500 is good to have a margin of 100px on each side:

var textView: UITextView = UITextView(frame: CGRectMake(100, 100, 500, 350))
textView.text = myText
textView.textContainerInset = UIEdgeInsetsMake(20, 20, 20, 20)
textView.backgroundColor = UIColor(red: 255, green: 255, blue: 255, alpha: 0.70)
textView.font = UIFont(name: "Helvetica Neue Light Italic", size: 35)

Then I render the textview in context by adding it as a subview to the parent view and then I get the image for export:

parentView.addSubview(textView)        
parentView.layer.renderInContext(UIGraphicsGetCurrentContext())    
var newIMG = UIGraphicsGetImageFromCurrentImageContext()

The problem is that the height is fixed to 350px, but sometimes the text is not long enough to use all that area. Ideally I would like to know how many lines will my text use, so I can give it a proportional height and not have a white unused area (see pic for example):

Info Any ideas on how to render the text view with a proportional height? Thanks a lot!

like image 231
Pablo Quemé Avatar asked Jul 27 '15 20:07

Pablo Quemé


1 Answers

I found the perfect solution to this problem in Apple's Text Layout Programming Guide. The solution Apple provides is in Objective-C, so I tested and re-wrote it for Swift.

Both methods work great and return the exact number of lines that are being used in a UITextView, without funky math and 100% accurate every time.

Here is my extension to UITextView to add a numberOfLines() method:

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

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

Just call this directly on your UITextView like so: myTextView.numberOfLines() <- returns an Int


This extension is also easily converted to a method like so:

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.lineFragmentRectForGlyphAtIndex(index, effectiveRange: &lineRange)
        index = NSMaxRange(lineRange)
        numberOfLines += 1
    }
    return numberOfLines
}

Just call numberOfLines(myTextView) to retrieve an Int of the number of lines

like image 112
Luke Chase Avatar answered Oct 19 '22 22:10

Luke Chase