Chinese character sizes not calculated correctly in NSLayoutManager's usedRect(for:) method when using San Francisco font

Originator:jcrowley
Number:rdar://43337516 Date Originated:8/15/2018
Status:Open Resolved:
Product:UIKit Product Version:
Classification:Bug Reproducible:Always
 
Area:
UIKit

Summary:

There's a problem in `NSLayoutManager`'s `usedRect(for:)` when trying to calculate an `NSAttributedString` with Chinese characters in the San Francisco font. It doesn't calculate the appropriate height. The issue doesn't appear to be present when using other fonts (though I haven't tested exhaustively).

Steps to Reproduce:

In the following Playground:

//=======

import UIKit
import PlaygroundSupport

class MyViewController : UIViewController {
    override func loadView() {
        let view = UIView()
        view.backgroundColor = .white

        let string = "这首诗并不是苏轼的名篇,但是初读便被最后一句吸引了。对仗工整,却不失灵动。“殷勤”二字用得别有趣味,又新巧。揣摩起诗人的心绪,是闲适中带着点轻快。“又得浮生一日凉”,能抛弃这对“浮生”的万千想象,尽情享受一日的欢喜,一日的凉爽,便自得其乐,心满意足。"

        let textView = UITextView()
        textView.frame = CGRect(x: 0, y: 200, width: 300, height: getHeight(forString: string, width: 300))
        textView.attributedText = attributedString(string)
        
        view.addSubview(textView)
        self.view = view
    }

    func getHeight(forString string: String, width: CGFloat) -> CGFloat {
        let textContainer = NSTextContainer(size: CGSize(width: width, height: CGFloat.greatestFiniteMagnitude))
        textContainer.lineFragmentPadding = 0
        let textStorage = NSTextStorage(attributedString: attributedString(string))
        let layoutManager = NSLayoutManager()
        layoutManager.addTextContainer(textContainer)
        textStorage.addLayoutManager(layoutManager)

        let rect = layoutManager.usedRect(for: textContainer)
        let height = ceil(rect.height)

        return height
    }

    func attributedString(_ string: String) -> NSAttributedString {
        let mutableAttributedString = NSMutableAttributedString(string: string, attributes: [NSAttributedStringKey.foregroundColor: UIColor.gray, NSAttributedStringKey.font: UIFont(name: "Helvetica Neue", size: 20.0)])

        return mutableAttributedString
    }
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()

//=======

1) View the output. The `UITextView` appears correctly, displaying all text in the string.
2) Change the `attributedString(_ :)` method to:

func attributedString(_ string: String) -> NSAttributedString {
        let mutableAttributedString = NSMutableAttributedString(string: string, attributes: [NSAttributedStringKey.foregroundColor: UIColor.gray, NSAttributedStringKey.font: UIFont.systemFont(ofSize: 20.0)])

        return mutableAttributedString
    }

3) View the output. The `UITextView` is cut off at the bottom.

Expected Results:

Text is properly measured and the resulting rect displays all text.

Actual Results:

Text is improperly measured, resulting in an inaccurate rect that clips the text.

Version/Build:

Version 9.4 (9F1027a)

Configuration:

N/A

Comments


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!