평소 UITextView를 사용해서 사용자에게 여러 줄의 텍스트를 입력받아왔다.
어느 날 테스트 목적으로 UITextView에 엔터를 엄청 많이 입력해보니까
아래와 같이 UITextView 내부 컨텐츠 영역이 점핑되거나 커서 위치가 이상해지는 현상을 발견했다.
이는 SwiftUI의 TextField(_, text:, axis:) 컴포넌트에서도 마찬가지였다.
진짜 미스테리는,, iOS 16까지의 실기기, 시뮬레이터는 문제가 없고 iOS 17부터 이런 현상이 나타난다는 것이다!!!
1. 구현
예제 프로젝트에서는 사용자가 입력하는 텍스트 중에서 숫자만 빨간색, Bold 체로 변경되는 코드를 작성했다.
func textViewDidChange(_ textView: UITextView) {
// 기존 텍스트 저장
let text = textView.text ?? ""
self.textView.text = text
// NSTextStorage 사용하여 텍스트를 변경
let attributedText = NSMutableAttributedString(string: text)
// 모든 텍스트에 대해 스타일을 적용
let fullRange = NSRange(location: 0, length: attributedText.length)
attributedText.addAttribute(.font, value: UIFont.systemFont(ofSize: 17), range: fullRange)
// 숫자에 대해서만 볼드체 적용
let regex = try! NSRegularExpression(pattern: "\\d+", options: [])
let matches = regex.matches(in: text, options: [], range: fullRange)
for match in matches {
let range = match.range
attributedText.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: 17), range: range)
attributedText.addAttribute(.foregroundColor, value: UIColor.red, range: range)
}
textView.attributedText = attributedText
}
UITextViewDelegate의 함수인 textViewDidChange(_: UITextView) 함수 내부에 사용자가 텍스트를 입력하면 해당 텍스트를 체크해서 숫자가 포함된 영역만 폰트를 변경하는 코드를 구현했다.
사실 NSMutableAttributedString을 사용하지 않고 그냥 순수 String 값만 업데이트 할 때는 아무 문제가 없다.
하지만 나는 사용자로부터 입력받는 텍스트 중에서 특정 값들에 대해 실시간으로 다른 폰트를 처리하고 싶다.
2. 해결
이틀동안 이 문제를 가지고 씨름한 결과, UITextView의 TextKit 버전 2를 1로 다운그레이드하면 해결이 된다.
TextKit2를 TextKit1로 명시적으로 변환하는 방법은 다음과 같다.
let textView = UITextView(usingTextLayoutManager: false)
아주 간단했다.
UITextView에 줄글이 많이 입력된 경우, 내부 Content의 영역이 비이상적으로 스크롤되거나 점핑되는 현상이 있었는데, 사실 원인을 모르니 해결 방법에 대한 검색도 어려웠다.
위에 gif처럼 이제 스크롤이나 커서위치가 점핑되는 현상도 사라졌다.
TextKit2는 애플에서 선보인 차세대 Text 관련 기술이라고하는데, iOS 16부터는 해당 Text 엔진으로 Text를 뷰로 나타낸다고 한다. 근데, UI 업데이트와 관련된 이슈들이 많이 나타나는 것 같다...
이에 대한 대처법으로 UITextView를 생성할 때 iOS 버전에 따른 다른 init 함수 코드를 작성했다.
let textView: UITextView
if #available(iOS 16, *) {
textView = UITextView(usingTextLayoutManager: false)
} else {
textView = UITextView()
}
textView.attributedText = NSAttributedString(string: text)
textView.textColor = .black
textView.backgroundColor = .yellow
textView.isEditable = true
textView.delegate = context.coordinator
return textView
TextKit에 대해서는 추후 공부한 후에 새롭게 정리해서 정리하겠다.
'회고' 카테고리의 다른 글
UIViewRepresentable View 레이아웃 업데이트 (UILabel Height 조절), SwiftUI lineBreakMode (0) | 2024.02.05 |
---|