Don_Mag icon

horizontal scroll textview in scrollview

Don_Mag | PRO | 01/19/22 06:47:03 PM UTC (Edited) | 0 ⭐ | 1815 👁️ | Never ⏰ | []
Swift |

6.63 KB

|

None

|

0 👍

/

0 👎

// 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

  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎