Find the number of characters that fits in a UITextView with constant width and height with a given string?
Asked Answered
F

4

11

In my application I need to set Read more into the text view if text input is large.so my approach is to find the range of string that would fit in the text view and append See More to it.Is there any way to achieve it in Swift.The requirement is to emulate the read more option to show the complete text in detailed view on tap like facebook.

Festinate answered 22/1, 2015 at 7:20 Comment(0)
B
8

The function you are looking for is CTFramesetterSuggestFrameSizeWithConstraints. Essentially, it allows you to find out the number of characters that fit in a certain frame. You can use that number to cut off your current text and insert a button.

I wrote an implementation of this function for a subclass of a UILabel:

- (NSInteger)numberOfCharactersThatFitLabel {

    // Create an 'CTFramesetterRef' from an attributed string
    CTFontRef fontRef = CTFontCreateWithName((CFStringRef)self.font.fontName, self.font.pointSize, NULL);
    NSDictionary *attributes = [NSDictionary dictionaryWithObject:(__bridge id)fontRef forKey:(id)kCTFontAttributeName];
    CFRelease(fontRef);
    NSAttributedString *attributedString  = [[NSAttributedString alloc] initWithString:self.text attributes:attributes];
    CTFramesetterRef frameSetterRef = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributedString);

    // Get a suggested character count that would fit the attributed string
    CFRange characterFitRange;
    CTFramesetterSuggestFrameSizeWithConstraints(frameSetterRef, CFRangeMake(0,0), NULL, CGSizeMake(self.bounds.size.width, self.numberOfLines*self.font.lineHeight), &characterFitRange);
    CFRelease(frameSetterRef);
    return (NSInteger)characterFitRange.length;
}

Here's a bog post for the full implementation of cutting off random text to a specified number of lines and appending "view more" text to it.

Bahadur answered 8/6, 2015 at 14:23 Comment(1)
Did you observe that it word wrapping, how does one make it for character wrap?Sausage
C
5

A rough swift translation of kgaidis's excellent answer.

extension UILabel {

    func numberOfCharactersThatFitLabel() -> Int {
        let fontRef = CTFontCreateWithName(self.font.fontName as CFStringRef, self.font.pointSize, nil)
        let attributes = NSDictionary(dictionary: [kCTFontAttributeName : fontRef])
        let attributeString = NSAttributedString(string: text!, attributes: attributes as? [String : AnyObject])
        let frameSetterRef = CTFramesetterCreateWithAttributedString(attributeString as CFAttributedStringRef)

        var characterFitRange:CFRange

        CTFramesetterSuggestFrameSizeWithConstraints(frameSetterRef, CFRangeMake(0, 0), nil, CGSizeMake(bounds.size.width, CGFloat(numberOfLines)*font.lineHeight), &characterFitRange)

        return characterFitRange.length
    }
}
Clobber answered 9/11, 2016 at 19:40 Comment(0)
L
5

Here is the Swift 4 UITextView extension

extension UITextView {

    func numberOfCharactersThatFitTextView() -> Int {
        let fontRef = CTFontCreateWithName(font!.fontName as CFString, font!.pointSize, nil)
        let attributes = [kCTFontAttributeName : fontRef]
        let attributedString = NSAttributedString(string: text!, attributes: attributes as [NSAttributedStringKey : Any])
        let frameSetterRef = CTFramesetterCreateWithAttributedString(attributedString as CFAttributedString)

        var characterFitRange: CFRange = CFRange()

        CTFramesetterSuggestFrameSizeWithConstraints(frameSetterRef, CFRangeMake(0, 0), nil, CGSize(width: bounds.size.width, height: bounds.size.height), &characterFitRange)
        return Int(characterFitRange.length)

    }
}

If there are extra text than your expected text, you can try adjusting the height value in CTFramesetterSuggestFrameSizeWithConstraints function parameter.

Lambda answered 28/7, 2018 at 14:41 Comment(0)
J
2

It's a bit complicated, because some letters are wider than others. But you can check the width of your string by using the sizeWithAttributes-method:

var yourString: String = textField.text
let myString: NSString = originalString as NSString

//Set your font and add attributes if needed.
let stringSize: CGSize = myString.sizeWithAttributes([NSFontAttributeName: yourFont])

Now you receive a CGSize and you can check if the width is wider than your textfield.

if(sizeOfYourTextfield < stringSize){
  //String is too large for your UITextField
}
Jochbed answered 22/1, 2015 at 7:29 Comment(4)
Will i be able to get the range of string that resides within the size specified?Festinate
Yes but I think the only possibility is looping through the letters and check if they still fit.Jochbed
I would recommend that you just loop and remove the last char of your String and check after every letter if the size of the string is still too Big.Jochbed
The requirement is to mimic the behavior of Facebook continue reading.Festinate

© 2022 - 2024 — McMap. All rights reserved.