여러분의 앱에서 노출되는 모든 텍스트는
하드코딩으로 작업되어 있나요?
필요에 따라 부분적으로 서버에서 텍스트를 받아와
노출시키는 영역도 있을 것 입니다.
만약 서버에서 텍스트를 받아온다면,
해당 텍스트는 한글로 바로 받아와 사용할 수 있는 상태인가요?
그럴수도 있겠지만
html의 형태로 받는 케이스도 있을텐데요.
(제가 그랬어요.)
그와중에 마주한 문제도 있었구요.
오늘은 그 문제를 해결했던 방법에 대해서 글을 써볼까 합니다.
일단 이슈는 이러했습니다.
위에서 설명을 드렸다시피 노출할 text 값은
서버에서 받아와 설정 해준다고 가정을 하겠습니다.
(html 형태의 String 타입 값)
"<b>12</b> 34 56 <b>78</b>"
html을 NSMutableAttributedString
포멧으로 변환해서
노출시키는 기본적인 방법을 사용하여 진행해보겠습니다.
let resultAtt = NSMutableAttributedString()
let data = "<b>12</b> 34 56 <b>78</b>".data(using: .utf8)
let att = try! NSAttributedString(data: data!, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding:String.Encoding.utf8.rawValue], documentAttributes: nil)
resultAtt.append(att)
label.attributedText = resultAtt
노출되는 기대값은 12, 78이 bold 처리가 되어있어야 하겠죠.
잘 나오네요.
그런데 attribute의 로그를 찍어서 확인 해보면
font-family: \"TimesNewRomanPS-BoldMT\"
로 확인이 되네요.
html을 한글로 인코딩하는 작업중
TimesNewRomanPS-BoldMT
폰트로 변경이 되버린 것 같아요.
맞아요. 문제가 발생했습니다!
SFUI-Bold
폰트가 아닌 다른 폰트로 적용이 되버린 것이죠.
폰트를 SFUI-Bold
로 명시해서 내려주라고 부탁하면 되는거 아니야?
라고 당연히 생각 해봤습니다.
동시에 혹시나 앱에서 이 상황을 해결 할만한 솔루션이 있지 않을까?
라고도 생각해봤고요.
검색을 해봤는데 역시나 있더라구요.😀
private func getAttribute(htmlText: String, withRegularFont regularFont: UIFont, andBoldFont boldFont: UIFont) -> NSMutableAttributedString {
var att = NSMutableAttributedString()
guard let data = htmlText.data(using: .utf8) else { return NSMutableAttributedString() }
do {
att = try NSMutableAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil)
let range = NSRange(location: 0, length: att.length)
att.enumerateAttribute(.font, in: range, options: .longestEffectiveRangeNotRequired)
{ value, range, _ in
let currentFont: UIFont = (value as? UIFont) ?? .init()
var replacementFont: UIFont?
if currentFont.fontName.contains("bold") || currentFont.fontName.contains("Bold") {
replacementFont = boldFont
} else {
replacementFont = regularFont
}
let replacementAttributeFont = [NSAttributedString.Key.font: replacementFont ?? .init()]
att.addAttributes(replacementAttributeFont, range: range)
}
} catch let error {
print(error.localizedDescription)
}
return att
}
label.attributedText = getAttribute(
htmlText: "<b>12</b> 34 56 <b>78</b>",
withRegularFont: UIFont.systemFont(ofSize: 14, weight: .regular),
andBoldFont: UIFont.systemFont(ofSize: 14, weight: .bold))
해결 방법은 enumerateAttribute
라는 메서드를 사용하는 것이였습니다.
htmlText를 인코딩하고
추출된 NSMutableAttributedString
를 기반으로
enumerateAttribute
메서드를 호출시키는 방법이지요.
여기서 중요한건 호출 시 첫번째 인자로 .font를 넣어줬다는 것입니다.
.font를 사용해서 NSMutableAttributedString
메서드를 호출하면
loop를 진행하면서 AttributedString의 모든 케릭터를 순회하게 되는데,
이때 하나의 캐릭터마다의 font를 얻을 수가 있어요.
그 폰트가 bold라면 위에서 확인했다시피 TimesNewRomanPS-BoldMT
이겠죠?
저희는 SFUI-Bold
가 필요한데 말이죠.
그럼 이제 TimesNewRomanPS-BoldMT
인 캐릭터를 찾았으니
SFUI-Bold
로 변경해주기만 하면 되겠네요.
위 로직처럼 UIFont.systemFont
로 설정해주겠습니다 .
이제 의도한대로 만족스럽게 잘 나오네요🙌
+ 위에서 사용한 enumerateAttribute 메소드에서
폰트뿐만 아니라 다른 attribute 속성들을 기준으로 enumerate를 돌릴 수도 있습니다!