UITextView that expands to text using auto layout

前端 未结 16 2094
面向向阳花
面向向阳花 2020-11-28 01:25

I have a view that is laid out completely using auto layout programmatically. I have a UITextView in the middle of the view with items above and below it. Everything works f

16条回答
  •  我在风中等你
    2020-11-28 01:45

    I've found it's not entirely uncommon in situations where you may still need isScrollEnabled set to true to allow a reasonable UI interaction. A simple case for this is when you want to allow an auto expanding text view but still limit it's maximum height to something reasonable in a UITableView.

    Here's a subclass of UITextView I've come up with that allows auto expansion with auto layout but that you could still constrain to a maximum height and which will manage whether the view is scrollable depending on the height. By default the view will expand indefinitely if you have your constraints setup that way.

    import UIKit
    
    class FlexibleTextView: UITextView {
        // limit the height of expansion per intrinsicContentSize
        var maxHeight: CGFloat = 0.0
        private let placeholderTextView: UITextView = {
            let tv = UITextView()
    
            tv.translatesAutoresizingMaskIntoConstraints = false
            tv.backgroundColor = .clear
            tv.isScrollEnabled = false
            tv.textColor = .disabledTextColor
            tv.isUserInteractionEnabled = false
            return tv
        }()
        var placeholder: String? {
            get {
                return placeholderTextView.text
            }
            set {
                placeholderTextView.text = newValue
            }
        }
    
        override init(frame: CGRect, textContainer: NSTextContainer?) {
            super.init(frame: frame, textContainer: textContainer)
            isScrollEnabled = false
            autoresizingMask = [.flexibleWidth, .flexibleHeight]
            NotificationCenter.default.addObserver(self, selector: #selector(UITextInputDelegate.textDidChange(_:)), name: Notification.Name.UITextViewTextDidChange, object: self)
            placeholderTextView.font = font
            addSubview(placeholderTextView)
    
            NSLayoutConstraint.activate([
                placeholderTextView.leadingAnchor.constraint(equalTo: leadingAnchor),
                placeholderTextView.trailingAnchor.constraint(equalTo: trailingAnchor),
                placeholderTextView.topAnchor.constraint(equalTo: topAnchor),
                placeholderTextView.bottomAnchor.constraint(equalTo: bottomAnchor),
            ])
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        override var text: String! {
            didSet {
                invalidateIntrinsicContentSize()
                placeholderTextView.isHidden = !text.isEmpty
            }
        }
    
        override var font: UIFont? {
            didSet {
                placeholderTextView.font = font
                invalidateIntrinsicContentSize()
            }
        }
    
        override var contentInset: UIEdgeInsets {
            didSet {
                placeholderTextView.contentInset = contentInset
            }
        }
    
        override var intrinsicContentSize: CGSize {
            var size = super.intrinsicContentSize
    
            if size.height == UIViewNoIntrinsicMetric {
                // force layout
                layoutManager.glyphRange(for: textContainer)
                size.height = layoutManager.usedRect(for: textContainer).height + textContainerInset.top + textContainerInset.bottom
            }
    
            if maxHeight > 0.0 && size.height > maxHeight {
                size.height = maxHeight
    
                if !isScrollEnabled {
                    isScrollEnabled = true
                }
            } else if isScrollEnabled {
                isScrollEnabled = false
            }
    
            return size
        }
    
        @objc private func textDidChange(_ note: Notification) {
            // needed incase isScrollEnabled is set to true which stops automatically calling invalidateIntrinsicContentSize()
            invalidateIntrinsicContentSize()
            placeholderTextView.isHidden = !text.isEmpty
        }
    }
    

    As a bonus there's support for including placeholder text similar to UILabel.

提交回复
热议问题