Skip to content

Instantly share code, notes, and snippets.

@christianselig
Created August 17, 2022 23:37
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save christianselig/509ba9c88a6b050ab48558ea15cb0c51 to your computer and use it in GitHub Desktop.
Save christianselig/509ba9c88a6b050ab48558ea15cb0c51 to your computer and use it in GitHub Desktop.
import UIKit
class ViewController: UIViewController {
let textViewWrapper = TextViewWrapper()
override func viewDidLoad() {
super.viewDidLoad()
// The problem view! I want to add this one to my set of Auto Layout constraints,
// even though it's not using Auto Layout internally.
textViewWrapper.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(textViewWrapper)
// This one is well behaved.
let redBox = UIView()
redBox.backgroundColor = .systemRed
redBox.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(redBox)
NSLayoutConstraint.activate([
redBox.widthAnchor.constraint(equalToConstant: 50.0),
redBox.heightAnchor.constraint(equalToConstant: 50.0),
redBox.centerXAnchor.constraint(equalTo: view.centerXAnchor),
redBox.bottomAnchor.constraint(equalTo: textViewWrapper.topAnchor, constant: -50.0),
// Please show up 😢
textViewWrapper.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
textViewWrapper.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
textViewWrapper.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
}
}
/// A simplified version of a class that holds a text view and is laid out with frames
/// because Auto Layout + UITextView expanding/collapsing doesn't animate well.
class TextViewWrapper: UIView {
let textView = UITextView()
init() {
super.init(frame: .zero)
textView.text = "Once upon a time a turtle named Lenny went to the park to meet with his friend the tiger."
textView.backgroundColor = .magenta.withAlphaComponent(0.05)
addSubview(textView)
}
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) { fatalError("\(#file) does not implement coder.") }
override func layoutSubviews() {
super.layoutSubviews()
textView.frame = bounds
}
override func sizeThatFits(_ size: CGSize) -> CGSize {
return CGSize(width: size.width, height: textView.sizeThatFits(size).height)
}
override func systemLayoutSizeFitting(_ targetSize: CGSize) -> CGSize {
return CGSize(width: targetSize.width, height: textView.sizeThatFits(targetSize).height)
}
}
@kyle-fox
Copy link

This is getting closer!

    override var intrinsicContentSize: CGSize {
        textView.sizeToFit()
        
        return CGSize(
            width: super.intrinsicContentSize.width,
            height: textView.contentSize.height
        )
    }

@kyle-fox
Copy link

This combo seems to be working for me, based on some of the suggestions in the thread:

var lastWidth = 0.0

override var intrinsicContentSize: CGSize { textView.frame.size }

override func layoutSubviews() {
    super.layoutSubviews()
        
    textView.frame = bounds
        
    if textView.frame.width != lastWidth {
        lastWidth = textView.frame.width
        textView.sizeToFit()
        invalidateIntrinsicContentSize()
    }
}

@drunknbass
Copy link

drunknbass commented Aug 18, 2022

wrote this at my coffee table w/ swift playgrounds on iPad barely working so excuse the trash quality code. It IS functional..

import UIKit


/// A simplified version of a class that holds a text view and is laid out with frames
/// because Auto Layout + UITextView expanding/collapsing doesn't animate well.
class TextViewWrapper: UIView {
    lazy var textView = UITextView()
    
    var numberOfLines: Int = 1 {
        didSet {
            textView.textContainer.maximumNumberOfLines = numberOfLines
            setNeedsLayout()
            invalidateIntrinsicContentSize()
        }
    }
    
    func sharedInit() {
        backgroundColor = .yellow
        textView.backgroundColor = .blue
        textView.isScrollEnabled = false
        textView.isEditable = false
        textView.textContainer.lineBreakMode = .byTruncatingTail
        textView.textContainer.maximumNumberOfLines = numberOfLines
        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.text = """
        Once upon a time a turtle named Lenny went to the park to meet with his friend the tiger. He had to keep thinking of more and more text to make this text view grow. Maybe this is enough? Turns out the iPad is a beast and requires more text to wrap to a second line.
        """
        addSubview(textView)
        
        let tap = UITapGestureRecognizer(target: self, action: #selector(tapped))
        addGestureRecognizer(tap)
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        sharedInit()
    }
    
    @objc func tapped() {
        numberOfLines = numberOfLines == 1 ? 0 : 1
    }
    
    @available(*, unavailable)
    required init?(coder aDecoder: NSCoder) { fatalError("\(#file) does not implement coder.") }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        textView.frame = bounds
        textView.contentSize.width = frame.size.width
        invalidateIntrinsicContentSize()
    }
    
    override var intrinsicContentSize: CGSize {
        textView.sizeToFit()
        return textView.contentSize
    }
}

import UIKit


class ViewController: UIViewController {
    lazy var textViewWrapper: UIView = TextViewWrapper(frame: .zero)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // The problem view! I want to add this one to my set of Auto Layout constraints,
        // even though it's not using Auto Layout internally.
        textViewWrapper.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(textViewWrapper)
        
        // This one is well behaved.
        let redBox = UIView()
        redBox.backgroundColor = .systemRed
        redBox.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(redBox)
        
        let leading = textViewWrapper.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor)
        
        NSLayoutConstraint.activate([
            redBox.widthAnchor.constraint(equalToConstant: 50.0),
            redBox.heightAnchor.constraint(equalToConstant: 50.0),
            redBox.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            redBox.bottomAnchor.constraint(equalTo: textViewWrapper.topAnchor, constant: -50.0),
            textViewWrapper.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor, constant: 20),
            textViewWrapper.trailingAnchor.constraint(greaterThanOrEqualTo: view.trailingAnchor, constant: 20),
            textViewWrapper.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            textViewWrapper.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
        ])
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment