How to get height for NSAttributedString at a fixed width

后端 未结 12 1074
萌比男神i
萌比男神i 2020-12-12 18:53

I want to do some drawing of NSAttributedStrings in fixed-width boxes, but am having trouble calculating the right height they\'ll take up when drawn. So far, I\'ve tried:

相关标签:
12条回答
  • 2020-12-12 19:29
    -[NSAttributedString boundingRectWithSize:options:]
    

    You can specify NSStringDrawingUsesDeviceMetrics to get union of all glyph bounds.

    Unlike -[NSAttributedString size], the returned NSRect represents the dimensions of the area that would change if the string is drawn.

    As @Bryan comments, boundingRectWithSize:options: is deprecated (not recommended) in OS X 10.11 and later. This is because string styling is now dynamic depending on the context.

    For OS X 10.11 and later, see Apple's Calculating Text Height developer documentation.

    0 讨论(0)
  • 2020-12-12 19:32

    The answer is to use
    - (void)drawWithRect:(NSRect)rect options:(NSStringDrawingOptions)options
    but the rect you pass in should have 0.0 in the dimension you want to be unlimited (which, er, makes perfect sense). Example here.

    0 讨论(0)
  • 2020-12-12 19:32

    As lots of guys mentioned above, and base on my test.

    I use open func boundingRect(with size: CGSize, options: NSStringDrawingOptions = [], context: NSStringDrawingContext?) -> CGRect on iOS like this bellow:

    let rect = attributedTitle.boundingRect(with: CGSize(width:200, height:0), options: NSStringDrawingOptions.usesLineFragmentOrigin, context: nil)
    

    Here the 200 is the fixed width as your expected, height I give it 0 since I think it's better to kind tell API height is unlimited.

    Option is not so important here,I have try .usesLineFragmentOrigin or .usesLineFragmentOrigin.union(.usesFontLeading) or .usesLineFragmentOrigin.union(.usesFontLeading).union(.usesDeviceMetrics), it give same result.

    And the result is expected as my though.

    Thanks.

    0 讨论(0)
  • 2020-12-12 19:34

    I have a complex attributed string with multiple fonts and got incorrect results with a few of the above answers that I tried first. Using a UITextView gave me the correct height, but was too slow for my use case (sizing collection cells). I wrote swift code using the same general approach described in the Apple doc referenced previously and described by Erik. This gave me correct results with must faster execution than having a UITextView do the calculation.

    private func heightForString(_ str : NSAttributedString, width : CGFloat) -> CGFloat {
        let ts = NSTextStorage(attributedString: str)
    
        let size = CGSize(width:width, height:CGFloat.greatestFiniteMagnitude)
    
        let tc = NSTextContainer(size: size)
        tc.lineFragmentPadding = 0.0
    
        let lm = NSLayoutManager()
        lm.addTextContainer(tc)
    
        ts.addLayoutManager(lm)
        lm.glyphRange(forBoundingRect: CGRect(origin: .zero, size: size), in: tc)
    
        let rect = lm.usedRect(for: tc)
    
        return rect.integral.size.height
    }
    
    0 讨论(0)
  • 2020-12-12 19:34

    Swift 3:

    let attributedStringToMeasure = NSAttributedString(string: textView.text, attributes: [
            NSFontAttributeName: UIFont(name: "GothamPro-Light", size: 15)!,
            NSForegroundColorAttributeName: ClickUpConstants.defaultBlackColor
    ])
    
    let placeholderTextView = UITextView(frame: CGRect(x: 0, y: 0, width: widthOfActualTextView, height: 10))
    placeholderTextView.attributedText = attributedStringToMeasure
    let size: CGSize = placeholderTextView.sizeThatFits(CGSize(width: widthOfActualTextView, height: CGFloat.greatestFiniteMagnitude))
    
    height = size.height
    

    This answer works great for me, unlike the other ones which were giving me incorrect heights for larger strings.

    If you want to do this with regular text instead of attributed text, do the following:

    let placeholderTextView = UITextView(frame: CGRect(x: 0, y: 0, width: ClickUpConstants.screenWidth - 30.0, height: 10))
    placeholderTextView.text = "Some text"
    let size: CGSize = placeholderTextView.sizeThatFits(CGSize(width: widthOfActualTextView, height: CGFloat.greatestFiniteMagnitude))
    
    height = size.height
    
    0 讨论(0)
  • 2020-12-12 19:36

    I found helper class to find height and width of attributedText (Tested code)

    https://gist.github.com/azimin/aa1a79aefa1cec031152fa63401d2292

    Add above file in your project

    How to use

    let attribString = AZTextFrameAttributes(attributedString: lbl.attributedText!)
    let width : CGFloat = attribString.calculatedTextWidth()
    print("width is :: >> \(width)")
    
    0 讨论(0)
提交回复
热议问题