I have a quiz in which each question may or may not have an image, and an unknown length of question text.
I am using a Collection View in which the question is in the header, and answers are in the cell. I want the header height to be dynamic to account for the image, and the variable text length. I am close, but there are still a few hiccups.
While browsing SO, and the internet, I have found 2 ways to calculate height of text for a Label and TextView. TextView calculation usually returns a higher height:
Height Calculation
func getHeightForLabelWith(width:CGFloat, font:UIFont, text:String) -> CGFloat {
let label:UILabel = UILabel.init(frame: CGRect.init(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.font = font
label.text = text
label.sizeToFit()
return label.frame.height
}
func getHeightForUItextViewWith(width: CGFloat, font:UIFont, text: String) -> CGFloat {
let textView: UITextView = UITextView.init(frame: CGRect.init(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
textView.isScrollEnabled = false
textView.font = font
textView.text = text
textView.sizeToFit()
return textView.frame.height
}
ViewController:
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView.dataSource = self
self.collectionView.delegate = self
let flowLayout = UICollectionViewFlowLayout()
flowLayout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
flowLayout.minimumLineSpacing = 10
flowLayout.minimumInteritemSpacing = 10
collectionView.collectionViewLayout = flowLayout
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "HeaderCell", for: indexPath) as! HeaderCell
header.text = headerText
header.image = headerImage
return header
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
let height = getHeightForUItextViewWith(width: self.collectionView.bounds.width, font: UIFont.boldSystemFont(ofSize: 20), text: headerText)
return CGSize(width: self.collectionView.bounds.width, height: max(height, 80))
}
HeaderCell:
class HeaderCell: UICollectionReusableView {
var image: UIImage?
var text: String?
var textView: UITextView = {
var view = UITextView()
view.translatesAutoresizingMaskIntoConstraints = false
view.isScrollEnabled = false
view.font = UIFont.boldSystemFont(ofSize: 20.0)
return view
}()
var imageView: UIImageView = {
var view = UIImageView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.cellInit()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
self.cellInit()
}
func cellInit() {
self.addSubview(textView)
self.addSubview(imageView)
textView.backgroundColor = .orange
}
override func layoutSubviews() {
super.layoutSubviews()
if let text = text {
textView.text = text
textView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
textView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
textView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
textView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
textView.sizeToFit()
}
if let _ = text, let image = image {
imageView.image = image
imageView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
imageView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
imageView.heightAnchor.constraint(lessThanOrEqualToConstant: 100.0).isActive = true
imageView.widthAnchor.constraint(lessThanOrEqualToConstant: 100.0).isActive = true
let imageFrame = UIBezierPath(rect: imageView.frame)
textView.textContainer.exclusionPaths = [imageFrame]
textView.sizeToFit()
}
}
}
Snaps:
- Small Text, No Image: Cutoff and almost full width
- Small Text, Image: Cutoff and almost half width
3. Large Text, No Image: Extra height, full width
4. Large Text, Image: Cutoff, full width, extra space under image (not sure if it can be reduced)
I hope I have provided enough information for someone to offer feedback.
Edit: Sample project @ G Drive