// Edit - still not a complete example, but this seems much more straightforward...
// ------------------------------------------------
// UIScrollView subclass
// ------------------------------------------------
class MyScrollView: UIScrollView, UIScrollViewDelegate {
var textView: UITextView!
var oneCharSize: CGSize = .zero
let font = UIFont.monospacedSystemFont(ofSize: 23, weight: .regular)
let numCols: Int = 80
let numRows: Int = 25
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
setupTextView()
delegate = self
}
func setupTextView() {
// My text view is going to contain a maximum of 80x25 characters, so here
// I am calculating how much space that is going to take up
let line = String(repeating: "a", count: numCols + 1)
let fullText = Array(repeating: line, count: numRows + 1).joined(separator: "\n")
let unroundedSize = (fullText as NSString).size(withAttributes: [
.font: font
])
let size = CGSize(width: ceil(unroundedSize.width), height: ceil(unroundedSize.height))
// get the size of one character
let unroundedSizeC = ("a" as NSString).size(withAttributes: [
.font: font
])
oneCharSize = CGSize(width: ceil(unroundedSizeC.width), height: ceil(unroundedSizeC.height))
textView = UITextView(frame: CGRect(origin: .zero, size: size))
contentSize = size
addSubview(textView)
textView.spellCheckingType = .no
textView.dataDetectorTypes = []
textView.font = font
// sample text to start with
textView.text = """
UILabel
A label can contain an arbitrary amount of text, but UILabel may shrink, wrap, or truncate the text, depending on the size of the bounding rectangle and properties you set. You can control the font, text color, alignment, highlighting, and shadowing of the text in the label.
UIButton
Displays a plain styled button that can have a title, subtitle, image, and other appearance properties.
"""
// the text view does not need to scroll. All the scrolling is done
// by the scroll view
textView.isScrollEnabled = false
// so we can see it
textView.backgroundColor = .yellow
}
override func scrollRectToVisible(_ rect: CGRect, animated: Bool) {
if !textView.isFirstResponder {
return super.scrollRectToVisible(rect, animated: animated)
}
if let selectedTextRange = textView.selectedTextRange {
// this is the NEW caret position
let caretPositionRect = textView.caretRect(for: selectedTextRange.start)
var charRect = CGRect(origin: caretPositionRect.origin, size: oneCharSize)
let visibleRect = CGRect(origin: contentOffset, size: frame.size)
if !visibleRect.contains(charRect) {
if charRect.origin.x - contentOffset.x < 0 {
charRect.origin.x = 0
}
super.scrollRectToVisible(charRect, animated: animated)
}
}
}
}
// ------------------------------------------------
// example view controller
// ------------------------------------------------
class MyViewController: UIViewController {
var scrollView: MyScrollView!
var textView: UITextView!
override func viewDidLoad() {
scrollView = MyScrollView()
scrollView.backgroundColor = .green
scrollView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20),
scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20),
scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20),
// scroll view height = 240
// because we're not addressing keyboard cover at this time
scrollView.heightAnchor.constraint(equalToConstant: 240.0),
])
}
}
// ------------------------------------------------
// UIScrollView subclass (originally posted)
// ------------------------------------------------
class OriginalMyScrollView: UIScrollView {
var textView: UITextView!
var oneCharSize: CGSize = .zero
let font = UIFont.monospacedSystemFont(ofSize: 23, weight: .regular)
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
setupTextView()
}
func setupTextView() {
// My text view is going to contain a maximum of 80x25 characters, so here
// I am calculating how much space that is going to take up
let line = String(repeating: "a", count: 81)
let fullText = Array(repeating: line, count: 26).joined(separator: "\n")
let unroundedSize = (fullText as NSString).size(withAttributes: [
.font: font
])
let size = CGSize(width: ceil(unroundedSize.width), height: ceil(unroundedSize.height))
let unroundedCharSize = ("W" as NSString).size(withAttributes: [
.font: font
])
oneCharSize = CGSize(width: ceil(unroundedCharSize.width), height: ceil(unroundedCharSize.height))
textView = UITextView(frame: CGRect(origin: .zero, size: size))
contentSize = size
addSubview(textView)
textView.spellCheckingType = .no
textView.dataDetectorTypes = []
textView.font = font
// dummy text
textView.text = """
UILabel
A label can contain an arbitrary amount of text, but UILabel may shrink, wrap, or truncate the text, depending on the size of the bounding rectangle and properties you set. You can control the font, text color, alignment, highlighting, and shadowing of the text in the label.
UIButton
Displays a plain styled button that can have a title, subtitle, image, and other appearance properties.
"""
// the text view do not need to scroll. All the scrolling is done
// by the scroll view
textView.isScrollEnabled = false
// so we can see it
textView.backgroundColor = .yellow
}
override func scrollRectToVisible(_ rect: CGRect, animated: Bool) {
if let selectedTextRange = textView.selectedTextRange {
let caretPositionRect = textView.caretRect(for: selectedTextRange.start)
var x: CGFloat = contentOffset.x
if caretPositionRect.minX - contentOffset.x < oneCharSize.width {
x += (caretPositionRect.minX - contentOffset.x) - oneCharSize.width
x = max(0, x)
} else if ((caretPositionRect.minX - contentOffset.x) - frame.width) > -oneCharSize.width {
x = caretPositionRect.minX - frame.width
x += oneCharSize.width
x = min(x, contentSize.width - frame.width)
}
contentOffset.x = x
}
}
}
Comments
0 B
|👍
/👎