Don_Mag icon

Untitled

Don_Mag | PRO | 08/20/21 09:21:14 PM UTC (Edited) | 0 ⭐ | 2404 👁️ | Never ⏰ | []
Swift |

9.55 KB

|

None

|

0 👍

/

0 👎

 
extension String {
    func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
        let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
        
        return ceil(boundingBox.height)
    }
    
    func width(withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
        let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
        
        return ceil(boundingBox.width)
    }
}
 
class CommentCell: UICollectionViewCell {
    let profileImageView = UIImageView()
    let nameLabel = UILabel()
    let titleLabel = UILabel()
    let commentLabel = UILabel()
    
    var titleFont: UIFont = .systemFont(ofSize: 16.0)
    var commentFont: UIFont = .systemFont(ofSize: 16.0)
 
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        contentView.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
 
        commentLabel.numberOfLines = 6
        
        [titleLabel, commentLabel].forEach { v in
            v.setContentHuggingPriority(.required, for: .vertical)
            v.backgroundColor = .yellow
        }
        
        [profileImageView, nameLabel, titleLabel, commentLabel].forEach { v in
            v.translatesAutoresizingMaskIntoConstraints = false
            contentView.addSubview(v)
        }
        
        profileImageView.backgroundColor = .red
        nameLabel.text = "Name Label"
        titleLabel.text = "Title Label"
        
        let g = contentView
        
        NSLayoutConstraint.activate([
            
            profileImageView.topAnchor.constraint(equalTo: g.topAnchor, constant: 8.0),
            profileImageView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 8.0),
            profileImageView.widthAnchor.constraint(equalToConstant: 80.0),
            profileImageView.heightAnchor.constraint(equalTo: profileImageView.widthAnchor),
            
            nameLabel.leadingAnchor.constraint(equalTo: profileImageView.trailingAnchor, constant: 8.0),
            nameLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0),
            nameLabel.centerYAnchor.constraint(equalTo: profileImageView.centerYAnchor),
            
            titleLabel.topAnchor.constraint(equalTo: profileImageView.bottomAnchor, constant: 8.0),
            titleLabel.leadingAnchor.constraint(equalTo: profileImageView.leadingAnchor, constant: 0.0),
            titleLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0),
 
            commentLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8.0),
            commentLabel.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor, constant: 0.0),
            commentLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0),
            commentLabel.bottomAnchor.constraint(lessThanOrEqualTo: g.bottomAnchor, constant: -8.0),
 
        ])
    }
}
 
class SampleViewController: UIViewController {
    
    var collectionView: UICollectionView!
    var collectionViewHeightConstraint: NSLayoutConstraint!
    var flowLayout: UICollectionViewFlowLayout!
 
    var comments: [String] = []
    
    let shortComments: [String] = [
        "First example.",
        "This is a short set... it should have a max of two lines.",
        "String with some text.",
        "This is a longer string for the two-line label.",
        "This potential solution regarding two-line-labels works better in this case.",
        "Last short string.",
    ]
 
    let longComments: [String] = [
        "This is a set of longer strings.",
        "Depending on the available view-width, it may not work well if the text is too long.",
        "Our final example string will be much longer than the others. This will demonstrate that, unless we also set a max-width, the calculated width will end up extending the label outside the bounds of our view (assuming we're on an iPhone in Portrait orientation).",
        "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.",
        "You can set the title, image, and other appearance properties of a button. In addition, you can specify a different appearance for each button state.",
        "Displays a rounded rectangle that can contain editable text. When a user taps a text field, a keyboard appears; when a user taps Return in the keyboard, the keyboard disappears and the text field can handle the input in an application-specific way. UITextField supports overlay views to display additional information, such as a bookmarks icon. UITextField also provides a clear text control a user taps to erase the contents of the text field.",
    ]
 
    var commentToggle: Int = 0
    
    let titleFont: UIFont = .systemFont(ofSize: 18.0)
    let commentFont: UIFont = .systemFont(ofSize: 12.0)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let toggleButton = UIButton()
        toggleButton.setTitle("Toggle Data", for: [])
        toggleButton.setTitleColor(.white, for: .normal)
        toggleButton.setTitleColor(.gray, for: .highlighted)
        toggleButton.backgroundColor = .systemBlue
        toggleButton.addTarget(self, action: #selector(toggleTapped(_:)), for: .touchUpInside)
        
        flowLayout = UICollectionViewFlowLayout()
        flowLayout.scrollDirection = .horizontal
 
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
        
        toggleButton.translatesAutoresizingMaskIntoConstraints = false
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        
        view.addSubview(toggleButton)
        view.addSubview(collectionView)
 
        let g = view.safeAreaLayoutGuide
        
        collectionViewHeightConstraint = collectionView.heightAnchor.constraint(equalToConstant: flowLayout.itemSize.height)
        
        NSLayoutConstraint.activate([
 
            toggleButton.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
            toggleButton.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 60.0),
            toggleButton.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -60.0),
 
            collectionView.topAnchor.constraint(equalTo: toggleButton.bottomAnchor, constant: 40.0),
            collectionView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            collectionView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            collectionViewHeightConstraint,
        ])
        
        collectionView.dataSource = self
        collectionView.delegate = self
        
        collectionView.register(CommentCell.self, forCellWithReuseIdentifier: "commentCell")
        
        updateViews()
    }
    
    func updateViews() -> Void {
        
        // assuming we want the cell width to be 280-pts,
        //  with 8-pts "padding" on each side
        //  the cell's title label and comment label Widths are: 264-pts
        let w: CGFloat = 264.0
        
        // Max 6 lines? Then get height of 6 lines
        let sixLines: String = "1\n2\n3\n4\n5\n6"
        let sixLineHeight: CGFloat = sixLines.height(withConstrainedWidth: w, font: commentFont)
        
        // start with short comments
        comments = commentToggle % 2 == 0 ? shortComments : longComments
        
        var maxCommentHeight: CGFloat = 0
        
        for i in 0..<comments.count {
            let h: CGFloat = comments[i].height(withConstrainedWidth: w, font: commentFont)
            maxCommentHeight = max(h, maxCommentHeight)
            if maxCommentHeight >= sixLineHeight {
                maxCommentHeight = sixLineHeight
                break
            }
        }
        
        // get height of title label
        let th: CGFloat = "A".height(withConstrainedWidth: w, font: titleFont)
        
        // cell height will be
        //  profileImageView height (80)
        //  +
        //  title label height
        //  +
        //  maxCommentHeight
        //  +
        //  vertical spacing (4 spaces * 8)
        let cellHeight: CGFloat = 80.0 + th + maxCommentHeight + 32.0
        
        flowLayout.itemSize = CGSize(width: w + 16.0, height: cellHeight)
        
        collectionViewHeightConstraint.constant = flowLayout.itemSize.height
 
        collectionView.reloadData()
    }
    
    @objc func toggleTapped(_ sender: Any?) -> Void {
        commentToggle += 1
        updateViews()
    }
 
}
 
extension SampleViewController: UICollectionViewDataSource, UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return comments.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let c = collectionView.dequeueReusableCell(withReuseIdentifier: "commentCell", for: indexPath) as! CommentCell
        c.titleLabel.font = titleFont
        c.commentLabel.font = commentFont
        c.commentLabel.text = comments[indexPath.item]
        return c
    }
    
}
 
// -------------------
// original paste follows
// -------------------
 
        // assuming comment label Width is 260-pts
        let w: CGFloat = 260.0
        
        // assuming font of comment label
        let font: UIFont = .systemFont(ofSize: 12.0)
        
        // Max 6 lines? Then get height of 6 lines
        let sixLines: String = "1\n2\n3\n4\n5\n6"
        let sixLineHeight: CGFloat = sixLines.height(withConstrainedWidth: w, font: font)
        
        var maxCommentHeight: CGFloat = 0
        
        for i in 0..<comments.count {
            let h: CGFloat = comments[i].height(withConstrainedWidth: w, font: font)
            print("h:", h)
            maxCommentHeight = max(h, maxCommentHeight)
            if maxCommentHeight > sixLineHeight {
                maxCommentHeight = sixLineHeight
                break
            }
        }
        
        print("Max comment height:", maxCommentHeight)
 
        // set itemSize of collection view flow layout to
        // CGSize(width: w, height: maxCommentHeight)
 

Comments

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

    0 B

    |

    👍

    /

    👎