I’m attempting to draw an attributed string in a CGContext using Core Text. I’ve got an interesting problem, it works for some font sizes but not others. The CTFrame is sometimes empty - visible string range = (0,0)
- which means the framesetter couldn’t layout the text. I figured out this is because the path is too small to draw the text in it. If I add 10 to the height in this case then it works.
Any ideas why that would be? How can I get the size it needs to be, no smaller and no larger?
let fontSize: CGFloat = 191 //FIXME: 190 and 192 work, why not 191?
let attributedString = NSAttributedString(string: "Hello", attributes: [.font: UIFont.systemFont(ofSize: fontSize)])
let textSize = attributedString.size()
let rect = CGRect(origin: .zero, size: CGSize(width: 4000, height: 3000))
let positionX = rect.width - ceil(textSize.width) //right edge of rect
let path = CGPath(rect: CGRect(x: positionX, y: 0, width: ceil(textSize.width), height: ceil(textSize.height)/* + 10*/), transform: nil)
let framesetter = CTFramesetterCreateWithAttributedString(attributedString)
let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attributedString.length), path, nil)
print(frame)
The outcome of this where fontSize == 190
:
<CTFrame: 0x600000598a80>{visible string range = (0, 5), path = <CGPath 0x600001f8fc50>, attributes = (null), lines = (
"<CTLine: 0x60000039a670>{run count = 1, string range = (0, 5), width = 408.574, A/D/L = 180.908/45.8301/0, glyph count = 5, runs = (\n\n<CTRun: 0x7fdde391adf0>{string range = (0, 5), string = \"Hello\", attributes = {\n NSFont = \"<UICTFont: 0x7fdde1c0fc20> font-family: \\\".SFUIDisplay\\\"; font-weight: normal; font-style: normal; font-size: 190.00pt\";\n}}\n\n)\n}"
)}
The outcome of this where fontSize == 191
:
<CTFrame: 0x600003e56bc0>{visible string range = (0, 0), path = <CGPath 0x60000240f0c0>, attributes = (null), lines = (
)}
Answering on a phone so sorry for formatting issues. Use CTFramesetterSuggestFrameSizeWithConstraints instead of textSize from your code.
let attri = NSAttributedString(string: str, attributes: self.attributes(str: str, font: ft))
let frameSetter = CTFramesetterCreateWithAttributedString((attri as CFAttributedString))
var fitRange = CFRangeMake(0, 0)
var fitRange = CFRangeMake(0, 0)
let suggested = CTFramesetterSuggestFrameSizeWithConstraints(frameSetter, CFRangeMake(0,attri.length), nil, CGSize(width:bounds.width,height:.greatestFiniteMagnitude), &fitRange)
path.addRect(CGRect(x: 0, y: 0, width: ceil(max(suggested.width, self.bounds.width)), height: ceil(max(suggested.height, self.bounds.height))))
let ctFrame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0,attri.length), path, nil)