如何锁定 UITextView 中的插入符号 y 位置以创建打字机效果?

How to lock caret y position in UITextView to create typewriter effect?

我试图强制 UITextView 将插入符号始终保持在相同的固定高度,例如在屏幕的 1/4 处。

我的行为应该与旧打字机类似——当用户按下回车键(或到达行尾)时,文本应该向上滚动一行,插入符号应该保持在相同的 y 位置并跳转到新行的开头。

我试图这样做,但它的行为出乎意料,插入符号有时会随机跳转并且可以看到滚动,它向下滚动然后我再次使用 scrollRectToVisible 向上滚动,这似乎不是喜欢这样做的理想方式。

怎样才能达到这样的效果?任何具有类似功能的库或 pod 也将不胜感激。

func setScrollToMiddle() {
    if let selectedRange = textView.selectedTextRange {

        let caretRect = textView.caretRect(for: selectedRange.start)            
        let middleOfCaretHeight = caretRect.origin.y + (caretRect.height / 2)
        let screenHeight = UIScreen.main.bounds.height
        guard let kbSize = self.keyboardSize else { return }
        let keyboardHeight = kbSize.height
        let visibleTextAreaHeight = screenHeight - keyboardHeight - topMenuView.frame.height
        let finalRectY = middleOfCaretHeight - topMenuView.frame.height - (visibleTextAreaHeight / 2)


        let finalRect = CGRect(x: 0.0, y: finalRectY, width: textView.frame.width, height: visibleTextAreaHeight)

        textView.scrollRectToVisible(finalRect, animated: false)
    }
}

这是我会做的:

首先在view中设置这些Did load

override func viewDidLoad() {
    super.viewDidLoad()
    textView.delegate = self
    textView.isEditable = true
    textView.isScrollEnabled = false
}

然后添加这个扩展:

extension ViewController: UITextViewDelegate {
func trackCaret(_ textView:UITextView){
    if let selectedRange = textView.selectedTextRange {
        let caretRect = textView.caretRect(for: selectedRange.start)
        let yPos = caretRect.origin.y - (textView.frame.height/2)
        let xPos = caretRect.origin.x - (textView.frame.width/2)
        textView.bounds.origin = CGPoint(x: xPos, y: yPos)
    }
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    trackCaret(textView)
    return true
}
func textViewDidBeginEditing(_ textView: UITextView) {
    trackCaret(textView)
}
func textViewDidEndEditing(_ textView: UITextView) {
    // If you don't need to move back to the original position you can leave this out
    //  textView.bounds.origin = CGPoint.zero
    //or if you want smooth animated scroll back then
    textView.scrollRectToVisible(CGRect(x:0,
                                        y:0,
                                        width: textView.bounds.width,
                                        height: textView.bounds.height),
                                 animated: true)
}

}

有了这个你就可以得到打字机的效果而不用到处跳了。 didendEditing 方法仅用于滚动回原点 0,0。如果您不需要这样做,请将其删除。