[iOS] UITextView fall back 폰트 적용하기 / flick 현상 해결

2na·2025년 7월 30일

회고

목록 보기
1/2

🔧 문제

UITextView에서 텍스트를 입력 시 글자가 계속 깜빡거리면서, 뷰에서 사라졌다가 생기는 것이 반복되는 현상입니다 .

🧠 원인 분석

텍스트를 하나하나 천천히 입력하면서 테스트를 했을 때, 뷰에서 안보이는 글자들은 모두 폰트에서 미지원되는 글자들이었습니다.

디자인시스템에서는 SUIT 폰트를 사용하고 있었기 때문에, 폰트 사이트 눈누에서, 지원되지 않는 글자가 어떤 것인지 확인하기 위해 먼저 테스트를 했습니다.

‘고은학생’이라는 텍스트를 입력하는 과정에서, 이렇게 폰트가 깨지는 글자가 있습니다.

이경우 뷰에서 확인하게 되면

뷰에서 사라지는 글자들은 모두 폰트에서 미지원되는 글자들임을 확인할 수 있었습니다. 사용자가 입력 중일때 글자가 깜빡거리는 것처럼 보여서 UX적 측면에서 좋지 않다고 느꼈습니다.

🛠 해결 과정

Fall back 폰트를 설정해주기 위해 Core Text를 이용하였습니다.
Core Text는 폰트관련 저수준 인터페이스인데, UIFont보다 더욱 정교한 처리를 구현할 수 있다고 합니다. (https://developer.apple.com/documentation/coretext/)

// String+

extension String {
    func canBeRendered(by font: UIFont) -> Bool {
        let cfFont = CTFontCreateWithName(font.fontName as CFString, font.pointSize, nil)
        return CTFontGetGlyphWithName(cfFont, self as CFString) != 0
    }
}

CTFont는 폰트를 Glyf(글리프)로 변환할 수 있습니다.

입력하고 싶은 글자의 폰트가 SUIT-Regular라면, CRFontCreateWithName 으로를 SUIT-Regular를 글리프 버전으로 만듭니다.

CTFontGetGlyphWithName 으로, 입력하고 싶은 글자가 ctFont 내에 있는지 확인하고 렌더링 여부에 따라 Bool 값을 리턴합니다. 그래서 읁, 핛 이런 글자들은 글리프에 없기 때문에 false가 리턴됩니다.

// UITextViewDelegate 
func textViewDidChange(_ textView: UITextView) {
        if textView.text.count > limitCount {
            textView.deleteBackward()
        }
//입력된 글자를 전부 받아옵니다 
        let fullText = textView.text ?? ""
        
// 지원안될 때 폰트를 설정합니다 (시스템폰트로 설정함)
        let fallbackFont = UIFont.systemFont(ofSize: 16)
        
        let suitFont = FontManager.body3R16.font
        
//입력된 글자를 스타일 설정 가능한 attributed string으로 초기화합니다       
        let attrStr = NSMutableAttributedString(string: fullText)
        
// 전체 글자를 돌면서 폰트 지원이 안되면 fallback폰트로 설정합니다 
        for (index, char) in fullText.enumerated() {
            let range = NSRange(location: index, length: 1)
            let fontToUse = String(char).canBeRendered(by: suitFont) ? suitFont : fallbackFont
            attrStr.addAttribute(.font, value: fontToUse, range: range)
            attrStr.addAttribute(.foregroundColor, value: UIColor.white, range: range)
        }

//최종적으로 글자를 할당합니다    
        textView.attributedText = attrStr
        
        count = textView.text.count
        textCount.text = "(\(count)/\(limitCount))"
        delegate?.changeStyle(count: count)
    }

✅ 결과

+) 여담
안드로이드에서는 이 문제를 해결할 수 없어 결국 폰트가 프리텐다드로 전면 수정되었음...

profile
Studying Frontend 👩🏻‍💻

0개의 댓글