Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- combine
- 리펙토링
- Protocol
- Observable
- 클린 코드
- swift documentation
- map
- collectionview
- UICollectionView
- Refactoring
- SWIFT
- Clean Code
- Xcode
- 스위프트
- MVVM
- tableView
- clean architecture
- ios
- ribs
- uiscrollview
- Human interface guide
- uitableview
- UITextView
- RxCocoa
- 리펙터링
- swiftUI
- rxswift
- 리팩토링
- 애니메이션
- HIG
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 2. 붙여넣기 글자 제한 UITextView 처리, 커서 이동 처리 방법 - UTF16 (이모지를 고려한 처리) 본문
iOS 응용 (swift)
[iOS - swift] 2. 붙여넣기 글자 제한 UITextView 처리, 커서 이동 처리 방법 - UTF16 (이모지를 고려한 처리)
jake-kim 2023. 9. 23. 01:171. 붙여넣기 글자 제한 UITextView 처리, 커서 이동 처리 방법 - 단순 텍스트
2. 붙여넣기 글자 제한 UITextView 처리, 커서 이동 처리 방법 - UTF16 (이모지를 고려한 처리)
문자열 처리 시 주의사항
- UTF16를 사용하고 있으므로 🇰🇷 이모지의 크기는 4이며, 1이 아닌 것에 주의
- "some string".count의 값은 단순히 글자 수이며, shouldChangeTextIn에서 현재 포커스를 구할 때 NSRange를 사용하는데 이 값 기준은 UTF16으로 글자의 크기 기준이므로 처리할 때 .count로 처리하면 안되므로 주의
(아래와 같이 UITextView에 텍스트를 입력하면 텍스트를 반영할지 아래 델리게이트에서 판단하는데, 현재 커서 위치를 나타내는 range 값은 String의 Count값이 아닌 UTF16의 크기 기준이므로 주의)
extension ViewController: UITextViewDelegate {
func textView(
_ textView: UITextView,
shouldChangeTextIn range: NSRange,
replacementText text: String
) -> Bool {
// TODO ...
}
}
UTF16 기준 예시
- 이모지 같은 경우 UTF16기준으로 값 카운트가 다름
- 이모지 입력 시, range.lowerBound 값을 출력하여 확인
extension ViewController: UITextViewDelegate {
func textView(
_ textView: UITextView,
shouldChangeTextIn range: NSRange,
replacementText text: String
) -> Bool {
rangeLabel.text = "range.lowerBound: " + String(range.lowerBound)
return true
}
}
(이모지 입력 시 4씩 증가)
UTF16을 고려한 처리
- UTF16을 고려하지 않으면 이모지 입력 시 range값이 일반 count와 다르므로 커서 위치가 이상하게 이동되는 현상 발생
- 기대 결과
- "abc{붙여넣기}def" -> "abc{초과한 만큼 잘린 문자열}def"
- 커서가 정상 위치
(아래에서 구현할 모습)
utf16 고려한 구현 방법
- 필요한 String extension 정의
extension String {
var utf16Size: Int {
utf16.count
}
func substring(from: Int, to: Int) -> String {
guard from < count, to >= 0, to - from >= 0 else { return "" }
let startIndex = index(startIndex, offsetBy: from)
let endIndex = index(startIndex, offsetBy: to + 1)
return String(self[startIndex ..< endIndex])
}
func inserted(string: String, utf16Index: Int) -> String {
let startIndex = index(utf16Index: utf16Index)
guard 0 <= count - startIndex else { return string }
return String(prefix(startIndex)) + string + String(suffix(count - startIndex))
}
func index(utf16Index: Int) -> Int {
var ret = 0
var count = 0
for (i, v) in enumerated() {
guard count <= utf16Index else { break }
count += v.utf16.count
ret = i
}
return ret
}
}
- shouldChangesTextIn 델리게이트에서 관련 로직 구현
extension ViewController: UITextViewDelegate {
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
...
}
}
- 사용자가 붙여넣기한지 체크하는 방법은 입력된 text의 크기가 1보다 크인지 확인하면 완료
let lastNSString = textView.text as NSString
let allText = lastNSString.replacingCharacters(in: range, with: text)
let overSize = allText.utf16Size <= maxCount
let isPasted = 1 < text.utf16Size
guard !overSize else {
textCount = allText.utf16Size
return true
}
guard isPasted else {
textCount = textView.text?.utf16Size ?? 0
return false
}
- maxCount보다 utf16size가 작으면 붙여넣기 처리 작업 수행
- 핵심은 substirng(from:to:)메소드에서는 utf16 카운트 값이 아닌 일반적인 String 카운트 값을 사용하므로 utf16기준 index를 일반 index로 변경후 처리
if textView.text.utf16Size < maxCount {
let isLastCursor = range.lowerBound >= textView.text.utf16Size
/// "abc{붙여넣기}def" -> "abc{초과한 만큼 잘린 문자열}def"
let utf16Index = (maxCount - textView.text.utf16Size)
let index = text.index(utf16Index: utf16Index)
let appendingText = text.substring(from: 0, to: index - 1)
textView.text = textView.text.inserted(string: appendingText, utf16Index: range.lowerBound)
/// 커서
let movingCursorPosition = isLastCursor ? maxCount : (range.lowerBound + appendingText.utf16Size)
let selectedRange = NSMakeRange(movingCursorPosition, 0)
DispatchQueue.main.async {
textView.selectedRange = selectedRange
}
} else {
/// 이전에 입력된 문자열이 maxCount 넘을 때, 붙여넣기 시도한 경우 > 새로운 문자열이 입력되 않지만 커서가 뒤로 이동 > 다시 이전 위치로 커서 이동
let selectedRange = NSMakeRange(range.lowerBound, 0)
DispatchQueue.main.async {
textView.selectedRange = selectedRange
}
}
textCount = textView.text.utf16Size
return false
* 전체 코드: https://github.com/JK0369/ExLimitedStringUtf
* 참고
'iOS 응용 (swift)' 카테고리의 다른 글
Comments