Express yourself
Emoji enhancements - Genmoji
기존에도 이미 많은 형태를 지원하고 있었다.
기존 이미지는 이미지가 아니기 때문에 표준화된 유니코드 문자다.
Genmoji같은 개인화된 이미지는 유니코드 텍스트로 표현할 수 없는 고유한 비트맵 이미지다.
NSAdaptiveImageGlyph
Adopting in your app
UITextView에서 NSAdaptiveImageGlyph 지원 활성화
let textView = UITextView()
textView.supportsAdaptiveImageGlyph = true
다음과 같은 Configuration에서도 기본적으로 지원
시스템에서 사용하는 serialization 에서 NSAdaptiveImageGlyph 지원이 추가되어서 개인적인 이미지도 바로 저장이 가능
NSAttributedString을 통해서 지원
NSAttributedString(adaptiveImageGlyph:, attributes:)
관련 시스템 serialization 프레임워크
읽고 쓰기(rtfd 포맷)
// Extract contents of text view as an attributed string
let textContents = textView.textStorage
// Serialize as data for storage or transport
let rtfData = try textContents.data(from: NSRange(location: 0, length: textContents.length),
documentAttributes: [.documentType: NSAttributedString.DocumentType.rtfd])
// Create attributed string from serialized data
let textFromData = try NSAttributedString(data: rtfData, documentAttributes: nil)
// Set on text view
textView.textStorage.setAttributedString(textFromData)
하지만 유니코드가 아니기 때문에 텍스트만 들어가야 하는 곳에는 적합하지 않다.
사용자 생성 컨텐츠에는 적합하다
근데 언젠가는 서버등으로 보내져서 웹브라우저에서 보는 작업 등을 할 수도 있을텐데 이때는 어떻게 처리해야 하는가?
방법 중 하나는 지금 일반 텍스트나 non-RTF 포맷에서 인라인 이미지를 처리하는 것과 동일하게 처리하는 것이다.
NSAttachmentCharacter(0xFFFC)를 사용
contentIdentifier를 통해서 참조를 유지하고 이미지를 캐싱
// Decompose an attributed string
func decomposeAttributedString(_ attrStr: NSAttributedString) -> (String, [(NSRange, String)], [String: Data]) {
let string = attrStr.string
var imageRanges: [(NSRange, String)] = []
var imageData: [String: Data] = [:]
attrStr.enumerateAttribute(.adaptiveImageGlyph, in: NSMakeRange(0, attrStr.length)) { (value, range, stop) in
if let glyph = value as? NSAdaptiveImageGlyph {
let id = glyph.contentIdentifier
imageRanges.append((range, id))
if imageData[id] == nil {
imageData[id] = glyph.imageContent
}
}
}
return (string, imageRanges, imageData)
}
// Recompose an attributed string
func recomposeAttributedString(string: String, imageRanges: [(NSRange, String)], imageData: [String: Data]) -> NSAttributedString {
let attrStr: NSMutableAttributedString = .init(string: string)
var images: [String: NSAdaptiveImageGlyph] = [:]
for (id, data) in imageData {
images[id] = NSAdaptiveImageGlyph(imageContent: data)
}
for (range, id) in imageRanges {
attrStr.addAttribute(.adaptiveImageGlyph, value: images[id]!, range: range)
}
return attrStr
}
HTML로 바꾸기
// Converting NSAttributedString to HTML
let htmlData = try textContent.data(from: NSRange(location: 0, length: textContent.length),
documentAttributes: [.documentType: NSAttributedString.DocumentType.html])
// 변환 결과
// webkit에서는 표준 이모지처럼 처리
// 미지원 엔진에서는 fallback 이미지 표시
// alt는 NSAdaptiveImageGlyph의 content description에서 오고, 이미지를 어떻게 처리하던 항상 적용된다.
<picture>
<source srcset="genmoji.heic" type="image/x-apple-adaptive-glyph">
<img src="genmoji.png" alt="dog in a spacesuit">
</picture>
Notification 지원(UNNotificationAttributedMessageContext)
func didReceive(_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
...
let message: NSAttributedString = _myAttributedMessageStringWithGlyph
let context = UNNotificationAttributedMessageContext(sendMessageIntent: sendMessageIntent,
attributedContent: _message)
do {
let messageContent = try request.content.updating(from: context)
contentHandler(messageContent)
} catch {
// Handle error
}
}
Compatibility
이 경우는 일반적인 textAttachment로 fallback한다.
Advanced Usage