관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] 3. 붙여넣기 글자 제한 UITextView 처리, 커서 이동 처리 방법 - isScrollEnabled=false인 상태에서 커서 위치로 스크롤링 방법 (#caretRect(for:), #scrollToCursor) 본문

iOS 응용 (swift)

[iOS - swift] 3. 붙여넣기 글자 제한 UITextView 처리, 커서 이동 처리 방법 - isScrollEnabled=false인 상태에서 커서 위치로 스크롤링 방법 (#caretRect(for:), #scrollToCursor)

jake-kim 2023. 10. 15. 01:33

1. 붙여넣기 글자 제한 UITextView 처리, 커서 이동 처리 방법 - 단순 텍스트

2. 붙여넣기 글자 제한 UITextView 처리, 커서 이동 처리 방법 - UTF16 (이모지를 고려한 처리)

3. 붙여넣기 글자 제한 UITextView 처리, 커서 이동 처리 방법 - isScrollEnabled=false인 상태에서 커서 위치로 스크롤링 방법(#caretRect(for:), #scrollToCursor)

붙여넣기하면 외부 scrollView를 cursor위치로 스크롤링

UITextView에서 cursor 영역으로 스크롤 이동시키는 방법

  • 보통 UITextView의 isScrollEnabled = true로 하고 붙여넣기하면 해당 커서로 자동으로 스크롤 되지만, isScrollEnabled = false로 하고 외부 스크롤 뷰를 사용할 땐 외부 스크롤 뷰를 cursor로 이동시키는 작업이 따로 필요
  • 아이디어
    • textView.selectedTextRange를 사용하여 커서 위치 획득
    • textView.caretRect(for:) 사용하여 커서 위치의 CGRect값 획득
    • textView.convert(_:to:)를 사용하여 커서 위치가 UIScrollView 기준 상대 좌표로 변환
    • scrollView.scrollRectToVisible()를 사용하여 스크롤 이동

구현

  • 예제 UI 준비
    • UIScrollView안에 UIStackView가 있는 형태
    • UIStackView에 label과 textView를 삽입
private let scrollView = {
    let view = UIScrollView()
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
}()
private let stackView = {
    let stackView = UIStackView()
    stackView.axis = .vertical
    stackView.translatesAutoresizingMaskIntoConstraints = false
    return stackView
}()
private let textView = {
    let view = UITextView()
    view.textColor = .black
    view.backgroundColor = .lightGray
    view.isScrollEnabled = false
    view.font = .systemFont(ofSize: 34)
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
}()
private let label: UILabel = {
    let label = UILabel()
    label.text = "0/0"
    label.textColor = .blue
    label.font = .systemFont(ofSize: 24, weight: .regular)
    label.numberOfLines = 0
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
}()
  • 입력한 문자열이 정해지는 UITextView 델리게이트 메소드, shouldChangeTextIn에서 처리 스크롤링
    • 붙여넣기 여부 체크
extension ViewController: UITextViewDelegate {
    func textView(
        _ textView: UITextView,
        shouldChangeTextIn range: NSRange,
        replacementText text: String
    ) -> Bool {
        ...
        let isPasted = 1 < text.count
        ...
    }
}
  • 붙여넣기인 경우,  cursor가 이동되는데 딜레이가 있으므로 0.3초 후에 스크롤 되도록 구현
if isPasted {
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
        self.scrollView.scrollToCursor(in: textView)
    }
}

extension UIScrollView {
    func scrollToCursor(in textView: UITextView) {
        // TODO
    }
}
  • 커서로 이동시키는 코드 추가
    • textView.selectedTextRange를 사용하여 커서 위치 획득
    • textView.caretRect(for:) 사용하여 커서 위치의 CGRect값 획득
    • textView.convert(_:to:)를 사용하여 커서 위치가 UIScrollView 기준 상대 좌표로 변환
    • scrollView.scrollRectToVisible()를 사용하여 스크롤 이동
func scrollToCursor(in textView: UITextView) {
    // 커서 위치를 가져옴
    guard let selectedRange = textView.selectedTextRange else { return }
    
    // 커서 위치를 화면 좌표로 변환 (caret: 텍스트 커서를 의미)
    let cursorRect = textView.caretRect(for: selectedRange.start)
    
    // 커서 위치가 화면에 보이는 rect 영역 가져옴
    let cursorRectInScrollView = textView.convert(cursorRect, to: self)
    let visibleRect = CGRect(x: 0, y: contentOffset.y, width: bounds.size.width, height: bounds.size.height)
    
    if !visibleRect.contains(cursorRectInScrollView.origin) {
        // 커서 위치가 화면에 보이지 않으면 스크롤
        scrollRectToVisible(cursorRectInScrollView, animated: true)
    }
}

(완성)

붙여넣기하면 외부 scrollView를 cursor위치로 스크롤링

* 전체 코드: https://github.com/JK0369/ExScrollToCursor

Comments