This new approach will simplify your original code, allowing you to move to a specific end location.
Note: Efficiency depends on how far the target end location is from the start of the document.
override func moveToEndOfDocument(_ sender: Any?) {
moveToEndLocation(textLayoutManager.documentRange.endLocation)
}
func moveToEndLocation(_ targetEndLocation: NSTextLocation) {
var lineFragmentFrame: CGRect = .null
textLayoutManager.enumerateTextLayoutFragments(from: textLayoutManager.documentRange.location, options: [.ensuresLayout]) { layoutFragment in
if layoutFragment.rangeInElement.endLocation.compare(targetEndLocation) != .orderedAscending {
lineFragmentFrame = layoutFragment.layoutFragmentFrame
return false
}
return true
}
textLayoutManager.textViewportLayoutController.layoutViewport()
self.scrollToVisible(lineFragmentFrame)
}
Post
Replies
Boosts
Views
Activity
A hacky way:
Tested on a MacBook Mid 2014; Scrolling takes approximately 0.001 seconds.
var counter = 0
override func moveToEndOfDocument(_ sender: Any?) {
let measureStartDate = Date()
resetScrollSettings()
scrollTextView()
print("⏰ scrolling took \(Date().timeIntervalSince(measureStartDate))s")
}
func resetScrollSettings() {
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(scrollTextView), object: nil)
counter = 0
}
@objc func scrollTextView() {
/// Change this number to ensure that all necessary recursions are executed
let magicNumber = 50
if counter > magicNumber {
return
} else {
counter += 1
}
let range = NSTextRange(location: textLayoutManager.documentRange.endLocation)
textLayoutManager.ensureLayout(for: range)
let targetLocation = textLayoutManager.documentRange.endLocation
let beforeTargetLocation = textLayoutManager.location(targetLocation, offsetBy: -1)!
textLayoutManager.textViewportLayoutController.layoutViewport()
guard let textLayoutFragment = textLayoutManager.textLayoutFragment(for: beforeTargetLocation) else {
return
}
guard let textLineFragment = textLayoutFragment.textLineFragment(for: targetLocation, isUpstreamAffinity: true) else {
return
}
let lineFrame = textLayoutFragment.layoutFragmentFrame
let lineFragmentFrame = textLineFragment.typographicBounds.offsetBy(dx: 0, dy: lineFrame.minY)
scrollToVisible(lineFragmentFrame)
perform(#selector(scrollTextView), with: nil, afterDelay: 0.001)
}
I have same problem.