이전에 UITextView에 사용자가 텍스트를 입력할 때, iOS 17이상의 단말기에서만 내부 컨텐츠 영역 커서나 스크롤이 이상해지는 현상이 있어 이를 해결한 포스팅을 작성했었다.
이때 변경한 내용이 TextKit2를 TextKit1로 변경한 것인데, 그럼 TextKit이 뭔지 알아보자.
1. TextKit
TextKit2의 소개는 WWDC21 TextKit2에서 처음 소개되었다.
일단 TextKit은 애플이 텍스트 관련 작업을 보다 쉽게 처리할 수 있도록 설계된 프레임워크이다.
iOS, macOS, watchOS, 그리고 tvOS에서 사용 가능한 텍스트 처리 및 렌더링의 복잡성을 추상화하여 개발자가 다양한 텍스트 관련 작업을 효율적으로 처리하도록 도와준다.
TextKit2는 iOS 15에서 처음으로 등장해서 iOS 16에서 완전히 기본으로 TextKit2를 사용한다고 한다.
그래서 저번 게시글에서 UITextView에서와 같은 현상이 SwiftUI의 TextField를 사용할 때도 발생한 듯 하다.
TextKit1은 아주 오래전부터 사용된 엔진으로서, 현대의 디자인 시스템, 기술과 비교했을 때 아주 오래된 Text 기술을 사용했다. 따라서, 성능이나 최적화 측면에서 문제가 있다고 한다.
특히 렌더링 성능과 텍스트 처리의 유연성이 제한되며 복잡한 레이아웃을 다룰 때 코드가 더 복잡해져 해당 문제의 해결을 전적으로 개발자가 부담하게 된다.
이를 개선하기 위해 나온 것이 TextKit2이다.
TextKit2는 보다 직관적이고 효율적인 텍스트 레이아웃과 렌더링을 지원한다. 예를 들어, 새로운 TextLayout API는 자동적으로 텍스트의 레이아웃을 계산하고 코드가 더 간결하고 유지보수하기 쉬워진 특징이 있다.
WWDC21에서 나온 자료로 간단하게 설명하면,
위 텍스트 화면을 구현한다고 할 때, 저 중간 배경색이 다른 텍스트만 다르게 표현하고 싶을 때 TextKit1은 다음의 처리 과정을 거쳐야 한다.
여기서 glyphs라는 개념이 나타나는데, 이에 대한 직관적이고 쉬운 설명은 https://zeddios.tistory.com/1292 을 참고하면 도움이 된다.
쉽게 얘기해서 문자와 문자가 합쳐지면 단어가 되는데, 해당 단어의 레이아웃 영역은 언어마다 상이하다.
'문자의 개별 영역의 합'이 단어의 영역이 될 수도 있고 '새로운 영역'이 단어의 영역이 될 수 있다. 이러한 개념을 glyphs라고 한다.
다시 돌아와서 문자열의 부분만 AttributedString으로 변환하는 작업은 위의 과정이 되는데, 단계는 많아보이지 않아도, 하나 하나의 과정을 지날 때 많은 연산이 필요로하다고 한다. 또한 glyphs의 영역이 제대로 잡히지 않을 경우, 올바른 텍스트 레이아웃을 나타내기 위해 새로운 레이아웃 객체를 생성해줘야한다.
반면에 TextKit2은 다음의 과정을 거친다.
TextKit1와 큰 차이는 glyphs를 사용하지 않는다는 것이다.
TextKit2는 기존에 사용하는 glyphs를 사용하지 않고, value semantics를 사용한 높은 레벨의 객체에서 필요한 정보를 얻어서 텍스트를 나타내고 레이아웃 작업을 하기 때문에 더 적은 코드로 다양한 언어를 View에 텍스트를 더 정확하게 나타낼 수 있다.
여기서 value semantics는 데이터 타입이 값으로 전달되고 복사된다는 개념을 말하는데, Swift에서는 이런 개념이 나타나면 보통 Struct를 떠올리기 쉽다. '아 값 타입이구나'라고 생각하기 쉬운데, 정확히는 다르다.
'값을 복사할 때마다 새로운 인스턴스가 생성되고, 원본 데이터와 복사된 데이터는 서로 독립적이다'가 핵심인데 Swift는 이런 개념으로 struct를 사용하는 것일 뿐, class로도 해당 기능을 구현할 수 있다.
TextKit2의 주요 시스템 객체가 해당 개념으로 동작하기에 코드의 안정성과 유지보수성을 높이는 데 큰 도움이 되었다.
TextKit1에서는 glyph를 NSLayoutManager에서 처리하는데, TextKit2는 NSTextLayoutManager에서 텍스트 레이아웃을 계산한다. 여기서는 glyph를 계산하지 않는데, 그럼 해당 기능을 어떻게 처리하느냐?
Text Content Manager는 입력 받은 Text를 Element 단위로 쪼개어 가지고 있다. glyph와 같은 레이아웃 계산을 위해서 NSTextLayoutManager는 해당 언어, 텍스트와 관련된 정보를 담고 있는 Layout Fragment 객체가 있는데 해당 객체에 정보를 업데이트하거나 수정해서 시스템에 전달해주면 된다. 즉, 기존에 glyph와 관련된 계산의 로직이 필요없어진 셈이다.
2. TextKit2
TextKit2의 가장 큰 장점 중 하나는 viewport-based layout 아키텍처를 사용한다는 점이다.
이는 비연속성 layout을 사용한다는 뜻이고, 더 쉽게 얘기해서 viewport 보여지는 뷰 영역만 layout을 그리게되어 대용량 문서나 텍스트를 보여줄 때도 좋은 성능을 유지할 수 있다.
TextKit2는 glyph를 사용하지 않아서 다양한 언어에 대해 더 정확하게 텍스트를 나타낼 수 있기 때문에 Open Type이나 Variable Font와 같은 현대 Font 기술들도 지원할 수 있게 됐다.
TextKit2 엔진은 모든 Apple 플랫폼의 Text Layout과 Rendering의 기본이 되었고 앞으로도 성능 향상 업데이트는 TextKit2 엔진에 초점을 맞출 것으로 기대된다.
최신 OS 버전에서는 TextKit2가 새로운 표준이기 때문에, UITextView와 NSTextView를 위한 이니셜라이저가 추가되었다.
또한 TextKit2는 복잡한 Text Container도 지원한다. 복잡한 Text Container는 내부에 구멍이나 여백이 있을 수 있는데 텍스트가 특정 이미지나 내부 컨텐츠를 감싸는 형태를 예로 들 수 있겠다. 이런 복잡한 Text Container를 만드려면 NSTextContainer의 exclusionPaths 프로퍼티를 사용해서 텍스트가 위치하면 안되는 곳을 정의해서 구현할 수 있겠다.
그 밖에도 LineBreaking의 성능도 향상되며, Text에서 이제 번호, 기호 등의 열거형 목록 리스트도 지원된다.
3. 전환
TextKit1 초기화
iOS 16부터는 자동으로 TextKit2가 기본으로 설정되었지만, TextKit1을 명시적으로 사용하는 방법은 다음과 같다.
let textStorage = NSTextStorage()
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer()
textStorage.addLayoutManager(layoutManager)
layoutManager.addTextContainer(textContainer)
let textView = UITextView(frame: .zero, textContainer: textContainer)
let textView = UITextView(usingTextLayoutManager: false)
두 가지 방법 있는데, 첫 번째는 Text Container와 LayoutManager를 사용해서 UITextView를 초기화하면 자동으로 TextKit1가 선택되고, 두 번째는 usingTextLayoutManager를 false 값으로 초기화하면 이 역시 TextKit1으로 초기화가 된다.
전환간 유의사항
Text View 하나에는 오로지 하나의 Layout Manager만 존재할 수 있다.
Text View는 NSTextLayoutManager와 NSLayoutManager를 함께 가질 수 없다.
Text View가 한 번 TextKit1로 전환되면 해당 객체가 다시 TextKit2로 돌아갈 수 없다. Layout 시스템을 바꾸는데 많은 작업이 필요하고, 전환이 일어나게 되면 그 때 가지고 있던 모든 UI 상태를 잃게 된다. 그리고 성능과 사용성을 위해 시스템은 TextKit1에서 TextKit2로 전환하지 않는다.
참고
https://zeddios.tistory.com/1292
https://yoojin99.github.io/app/WWDC22-What's-new-in-TextKit-and-text-views/
'Swift' 카테고리의 다른 글
TCA - Binding (0) | 2023.08.17 |
---|---|
TCA - Environment는 어디갔나? (0) | 2023.07.10 |
TCA(The Composable Architecture)란? (1) | 2023.07.06 |
Swift 네트워크 추상화 - URL 처리방법 (0) | 2023.05.09 |
객체지향 생활체조 (0) | 2023.05.05 |