0

I'm trying to determine the amount of attributed text that can fit in the constant size textview.I have tried with the CTFrameSetter but I think that's only helpful when we already know the text that we want to add. So far I have tried this

 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 [NSAttributedString.Key : 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)

    }
NewUser
  • 21
  • 3
  • Do you want to know how much text fits for a certain font or resize the text until it fits in the container? – Lobont Andrei Jan 06 '22 at 19:30
  • @LobontAndrei I want to know the exact range of text that can fit in a constant size textcontainer. – NewUser Jan 06 '22 at 19:47
  • https://stackoverflow.com/questions/30450434/figure-out-size-of-uilabel-based-on-string-in-swift this might help I think. – Lobont Andrei Jan 06 '22 at 20:00
  • *"... determine the amount of attributed text that can fit ... only helpful when we already know the text that we want to add"* -- That sounds like you want to know, for example, "400 characters will fit"? Unless you are using a monospaced font, that's not possible... 10 upper-case `W` - "WWWWWWWWWW" will take up about the same space as 40 lower-case `i` - "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii" – DonMag Jan 06 '22 at 20:10
  • @DonMag if not the exact number of characters can we get the number of lines that can fit ? – NewUser Jan 06 '22 at 20:36
  • @LobontAndrei Thanks that helped. – NewUser Jan 06 '22 at 20:43

1 Answers1

0

Edit

Sometimes, I overthink things...

If all you want to do is get the max number of lines that will fit, you can use the font's .lineHeight property:

    // the height of your text view
    let h: CGFloat = 160.0

    // whatever your font is
    let font: UIFont = .systemFont(ofSize: 24.0)
    
    let maxLines: Int = Int(floor(h / font.lineHeight))
    
    print("Max Number of Lines:", maxLines)
    

Original Answer

If you want the number of lines that will fit for a given textView height, you can do it this way...

First, a convenient extension:

extension NSAttributedString {
    
    func height(containerWidth: CGFloat) -> CGFloat {
        
        let rect = self.boundingRect(with: CGSize.init(width: containerWidth, height: CGFloat.greatestFiniteMagnitude),
                                     options: [.usesLineFragmentOrigin, .usesFontLeading],
                                     context: nil)
        return ceil(rect.size.height)
    }
    
    func width(containerHeight: CGFloat) -> CGFloat {
        
        let rect = self.boundingRect(with: CGSize.init(width: CGFloat.greatestFiniteMagnitude, height: containerHeight),
                                     options: [.usesLineFragmentOrigin, .usesFontLeading],
                                     context: nil)
        return ceil(rect.size.width)
    }
    
}

Then, use this func:

func numberOfLinesThatFit(inHeight height: CGFloat, withFont font: UIFont) -> Int {

    let attributes: [NSAttributedString.Key : Any] = [.font : font]
    
    var n: Int = 0
    
    var str: String = "A"
    
    var attStr: NSAttributedString = NSAttributedString(string: str, attributes: attributes)
    
    // width just needs to be greater than one character width
    var h: CGFloat = attStr.height(containerWidth: 200.0)
    
    while h < height {
        n += 1
        str += "\nA"
        attStr = NSAttributedString(string: str, attributes: attributes)
        h = attStr.height(containerWidth: 200.0)
    }
    
    return n

}

and call it like this:

    // whatever your font is
    let font: UIFont = .systemFont(ofSize: 24.0)
    
    // the height of your text view
    let h: CGFloat = 160.0
    
    let maxLines: Int = numberOfLinesThatFit(inHeight: h, withFont: font)
    
    print("max lines:", maxLines)
DonMag
  • 69,424
  • 5
  • 50
  • 86