表情符号支持 NSAttributedString 属性(kerning/paragraph 样式)
Emoji support for NSAttributedString attributes (kerning/paragraph style)
我在 UILabel
上使用字距调整属性来显示具有一些自定义字母间距的文本。不幸的是,当我显示用户生成的字符串时,我有时会看到如下内容:
即有时某些表情符号字符无法显示。
如果我注释掉字距调整但改为应用一些段落样式,我会得到相同类型的错误渲染。
我在文档中找不到任何明确拒绝支持特殊 unicode 字符的内容。我做错了什么还是 iOS 错误?
重现错误的代码可在此处作为游乐场使用:https://github.com/Bootstragram/Playgrounds/tree/master/LabelWithEmoji.playground
并复制到这里:
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
extension NSAttributedString {
static func kernedSpacedText(_ text: String,
letterSpacing: CGFloat = 0.0,
lineHeight: CGFloat? = nil) -> NSAttributedString {
// TODO add the font attribute
let attributedString = NSMutableAttributedString(string: text)
attributedString.addAttribute(NSAttributedStringKey.kern,
value: letterSpacing,
range: NSRange(location: 0, length: text.count))
if let lineHeight = lineHeight {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = lineHeight
attributedString.addAttribute(NSAttributedStringKey.paragraphStyle,
value: paragraphStyle,
range: NSRange(location: 0, length: text.count))
}
return attributedString
}
}
//for familyName in UIFont.familyNames {
// for fontName in UIFont.fontNames(forFamilyName: familyName) {
// print(fontName)
// }
//}
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let myString = "1⚽⚾️♂️\n2 "
let label = UILabel()
label.frame = CGRect(x: 150, y: 200, width: 200, height: 100)
label.attributedText = NSAttributedString.kernedSpacedText(myString)
label.numberOfLines = 0
label.textColor = .black
view.addSubview(label)
self.view = view
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
谢谢。
TL,DR:
String.count != NSString.length
。任何时候你看到 NSRange
,你必须将你的 String
转换成 UTF-16:
static func kernedSpacedText(_ text: String,
letterSpacing: CGFloat = 0.0,
lineHeight: CGFloat? = nil) -> NSAttributedString {
// TODO add the font attribute
let attributedString = NSMutableAttributedString(string: text)
attributedString.addAttribute(NSAttributedStringKey.kern,
value: letterSpacing,
range: NSRange(location: 0, length: text.utf16.count))
if let lineHeight = lineHeight {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = lineHeight
attributedString.addAttribute(NSAttributedStringKey.paragraphStyle,
value: paragraphStyle,
range: NSRange(location: 0, length: text.utf16.count))
}
return attributedString
}
更长的解释
你的问题是在 Swift 的 String
和 ObjC 的 NSString
之间转换的常见问题。一个String
的长度是扩展字素簇的个数;在 ObjC 中,它是编码该字符串所需的 UTF-16 代码点数。
以thumb-up字符为例:
let str = ""
let nsStr = str as NSString
print(str.count) // 1
print(nsStr.length) // 2
当涉及到国旗表情符号时,事情会变得更加奇怪:
let str = ""
let nsStr = str as NSString
print(str.count) // 1
print(nsStr.length) // 4
尽管这篇文章写于 2003 年,但今天仍然值得一读:
The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets.
我在 UILabel
上使用字距调整属性来显示具有一些自定义字母间距的文本。不幸的是,当我显示用户生成的字符串时,我有时会看到如下内容:
即有时某些表情符号字符无法显示。
如果我注释掉字距调整但改为应用一些段落样式,我会得到相同类型的错误渲染。
我在文档中找不到任何明确拒绝支持特殊 unicode 字符的内容。我做错了什么还是 iOS 错误?
重现错误的代码可在此处作为游乐场使用:https://github.com/Bootstragram/Playgrounds/tree/master/LabelWithEmoji.playground
并复制到这里:
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
extension NSAttributedString {
static func kernedSpacedText(_ text: String,
letterSpacing: CGFloat = 0.0,
lineHeight: CGFloat? = nil) -> NSAttributedString {
// TODO add the font attribute
let attributedString = NSMutableAttributedString(string: text)
attributedString.addAttribute(NSAttributedStringKey.kern,
value: letterSpacing,
range: NSRange(location: 0, length: text.count))
if let lineHeight = lineHeight {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = lineHeight
attributedString.addAttribute(NSAttributedStringKey.paragraphStyle,
value: paragraphStyle,
range: NSRange(location: 0, length: text.count))
}
return attributedString
}
}
//for familyName in UIFont.familyNames {
// for fontName in UIFont.fontNames(forFamilyName: familyName) {
// print(fontName)
// }
//}
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let myString = "1⚽⚾️♂️\n2 "
let label = UILabel()
label.frame = CGRect(x: 150, y: 200, width: 200, height: 100)
label.attributedText = NSAttributedString.kernedSpacedText(myString)
label.numberOfLines = 0
label.textColor = .black
view.addSubview(label)
self.view = view
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
谢谢。
TL,DR:
String.count != NSString.length
。任何时候你看到 NSRange
,你必须将你的 String
转换成 UTF-16:
static func kernedSpacedText(_ text: String,
letterSpacing: CGFloat = 0.0,
lineHeight: CGFloat? = nil) -> NSAttributedString {
// TODO add the font attribute
let attributedString = NSMutableAttributedString(string: text)
attributedString.addAttribute(NSAttributedStringKey.kern,
value: letterSpacing,
range: NSRange(location: 0, length: text.utf16.count))
if let lineHeight = lineHeight {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = lineHeight
attributedString.addAttribute(NSAttributedStringKey.paragraphStyle,
value: paragraphStyle,
range: NSRange(location: 0, length: text.utf16.count))
}
return attributedString
}
更长的解释
你的问题是在 Swift 的 String
和 ObjC 的 NSString
之间转换的常见问题。一个String
的长度是扩展字素簇的个数;在 ObjC 中,它是编码该字符串所需的 UTF-16 代码点数。
以thumb-up字符为例:
let str = ""
let nsStr = str as NSString
print(str.count) // 1
print(nsStr.length) // 2
当涉及到国旗表情符号时,事情会变得更加奇怪:
let str = ""
let nsStr = str as NSString
print(str.count) // 1
print(nsStr.length) // 4
尽管这篇文章写于 2003 年,但今天仍然值得一读: The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets.