私は同じ問題を抱えていましたが、UITableView内のUITextViewについて、いくつかの調査の後、それを修正する「簡単な」方法が見つからなかったため、受け入れられた回答に基づいて、完全に機能するソリューションを作成しました( UICollectionView、UIScrollView内でも機能するはずです)この拡張機能内でいくつかの変更がコメントされています)。
したがって、簡単に再利用するには、UIKit の上にいくつかの拡張機能が必要です。
extension UITextView {
func scrollToCursor(animated: Bool = false, verticalInset: CGFloat = 8) {
guard let selectedTextRange = selectedTextRange else { return }
var cursorRect = caretRect(for: selectedTextRange.start)
// NOTE: can't point UIScrollView, coz on iOS 10 closest view will be UITableWrapperView
// to extend functionality for UICollectionView or plain UIScrollView it's better to search them one by one
let scrollView = findParent(of: UITableView.self) ?? self
cursorRect = convert(cursorRect, to: scrollView)
if cursorRect.origin.x.isInfinite || cursorRect.origin.y.isInfinite {
return
}
let bottomOverflow = cursorRect.maxY - (scrollView.contentOffset.y + scrollView.bounds.height - scrollView.contentInset.bottom - scrollView.contentInset.top)
if bottomOverflow > 0 {
let offset = CGPoint(x: scrollView.contentOffset.x, y: scrollView.contentOffset.y + bottomOverflow + verticalInset)
scrollView.setContentOffset(offset, animated: animated)
return
}
let topOverflow = scrollView.contentOffset.y - cursorRect.minY
if topOverflow > 0 {
let offset = CGPoint(x: scrollView.contentOffset.x, y: scrollView.contentOffset.y - topOverflow - verticalInset)
scrollView.setContentOffset(offset, animated: animated)
}
}
}
UIView:
extension UIView {
func findParent<Parent: UIView>(of parentType: Parent.Type) -> Parent? {
return superview?.findNext(of: parentType)
}
private func findNext<Parent: UIView>(of parentType: Parent.Type) -> Parent? {
if let res = self as? Parent {
return res
}
return superview?.findNext(of: parentType)
}
}
そのため、UITextViewDelegate では、テキストが変更されたときに、必要な場所を呼び出します (ディスパッチ キューのメイン非同期ブロック内にある可能性があります - これには ReactiveSwift コールバックを使用しています)。
textView.scrollToCursor()
カーソル位置の変更 (画面の上部) に上への移動を追加する場合は、textViewDidChangeSelection
デリゲートのメソッド内でこのメソッドを呼び出す必要があります (もちろん、選択の長さをチェックして)。