Littlebobbydroptables
Littlebobbydroptables

Reputation: 3731

Autosizing UITableViewCell programatically based on content

I am creating an application what simulates chatting with a real user, for chat log layout i am using UITableView, because i am very new to Swift, but used to program with other languages, i am creating whole layout programatically.

Currently my ViewController looks like this

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var navigationBar: UINavigationBar!
    @IBOutlet weak var tableView: UITableView!

    let rows: [String] = [
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam euismod nunc id nulla ultricies tempus. Mauris a euismod nunc."
    ]

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return rows.count;
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "AiMessageCell") as! AiMessageCell

//        cell.textLabel?.text = rows[indexPath.row]
//        cell.textLabel?.numberOfLines = 0;

        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
//        tableView.beginUpdates()
//        tableView.endUpdates()

        return UITableViewAutomaticDimension;
    }

    override func viewDidLoad() {
        super.viewDidLoad()

//        tableView.estimatedRowHeight = 140
//        tableView.rowHeight = UITableViewAutomaticDimension;

        tableView.register(AiMessageCell.self, forCellReuseIdentifier: "AiMessageCell")
        tableView.register(UserMessageCell.self, forCellReuseIdentifier: "UserMessageCell")
        tableView.register(UserSelectionCell.self, forCellReuseIdentifier: "UserSelectionCell")
    }
}

it just takes single row and based on it (currently not passing text to cell) outputs one single row to my UITableView

and i have AiMessageCell logic

class AiMessageCell: UITableViewCell, NSLayoutManagerDelegate {

//    @IBOutlet weak var wrapper: UIView!

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        let wrapper = UIView(
            frame: CGRect(
                x: 10, y: 10,
                width: UIScreen.main.bounds.width - 10 - 10,
                height: 45
            )
        )

        let balloon = UIView(
            frame: CGRect(
                x: 40, y: 0,
                width: 80,
                height: 45.5
            )
        )
        balloon.layer.cornerRadius = 10;
        balloon.backgroundColor = UIColor.init(
            red: CGFloat(240/255.0),
            green: CGFloat(240/255.0),
            blue: CGFloat(240/255.0),
            alpha: CGFloat(1.0)
        );

        let message = UITextView(
            frame: CGRect(
                x: 10, y: 5,
                width: balloon.layer.bounds.width - 10,
                height: balloon.layer.bounds.height - 10
            )
        );
        message.text = "What is shown in the scrollable content area of a scroll view is determined by its subviews. 🤗";
        message.font = UIFont.systemFont(ofSize: 16);
        message.isEditable = false;
        message.isScrollEnabled = false;
        message.isSelectable = false;
        message.backgroundColor = UIColor.clear;
        message.layoutManager.delegate = self;

        let avatar = UIImageView(
            image: UIImage(named: "avatar")
        );

        avatar.clipsToBounds = true;
        avatar.layer.cornerRadius = 15;

        let calculated = message.sizeThatFits(
            CGSize(width: wrapper.bounds.width - 50, height: CGFloat.greatestFiniteMagnitude)
        )

        avatar.frame = CGRect(x: 0, y: calculated.height - 25, width: 30, height: 30)

        message.frame.size = CGSize(width: calculated.width, height: calculated.height)
        balloon.frame.size = CGSize(width: calculated.width + 20, height: calculated.height + 5);
        wrapper.frame.size = CGSize(width: wrapper.bounds.width, height: balloon.bounds.height);

        balloon.addSubview(message)

        wrapper.addSubview(balloon)
        wrapper.addSubview(avatar)

        wrapper.backgroundColor = UIColor.purple
        contentView.backgroundColor = UIColor.green

        contentView.addSubview(wrapper)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func setTimeout(_ delay:TimeInterval, block:@escaping ()->Void) -> Void {
        Timer.scheduledTimer(timeInterval: delay, target: BlockOperation(block: block), selector: #selector(Operation.main), userInfo: nil, repeats: false)
    }

    func layoutManager(_ layoutManager: NSLayoutManager, lineSpacingAfterGlyphAt glyphIndex: Int, withProposedLineFragmentRect rect: CGRect) -> CGFloat {
        return 5;
    }
}

here i am constructing whole message row, with "user" avatar, and message bubble (UITextView), which autosizes to text.

And here comes the problem, despite message bubble is autosizing to text, table row height is fixed.

This is how it looks like, i honestly tried at least 5 different answers from SO, but no luck. Currently i think that problem is because i don't have constraints between my wrapper (purple block) and cells content view (green block)

enter image description here

Upvotes: 0

Views: 74

Answers (2)

davebcn87
davebcn87

Reputation: 859

I think that the problem here is that you are defining the cell subviews sizes with frame and not with layout constraints. When you use UITableViewAutomaticDimension you need to properly setup the constraints to the top and the bottom of the UITableViewCell.

And instead of using the delegate, I think it's much cleaner the way you have commented in viewDidLoad:

    tableView.estimatedRowHeight = 140
    tableView.rowHeight = UITableViewAutomaticDimension

Upvotes: 1

Maxim Zakopaylov
Maxim Zakopaylov

Reputation: 556

You need check constraint in your storyboard. It's seems like you're forget constraint from bottom text block to bottom cell. And then uncomment this

tableView.estimatedRowHeight = 140

Upvotes: 1

Related Questions