Dynamic height for table cells with asynchronous loading images

I have a custom cell with dynamic height inside an XIB file that shows a message with a message and image. Since the image will be loaded asynchronously from my API, the aspect ratio of the image will be sent with the resource. Inside the method, updateConstraintsa restriction of the aspect ratio with the corresponding factor will be established.

XIB file:

My xib file

Cell class:

//
//  PostTableViewCell.swift
//  Lome
//
//  Created by Tobias Feistmantl on 10/09/15.
//  Copyright (c) 2015 Tobias Feistmantl. All rights reserved.
//

import UIKit
import Alamofire

class PostTableViewCell: UITableViewCell {

    @IBOutlet weak var userProfileImageView: TFImageView!
    @IBOutlet weak var usersNameLabel: UILabel!
    @IBOutlet weak var usernameLabel: UILabel!
    @IBOutlet weak var userProfileButton: TFCellButton!
    @IBOutlet weak var distanceLabel: UILabel!
    @IBOutlet weak var messageLabel: UILabel!
    @IBOutlet weak var postImageView: UIImageView!
    @IBOutlet weak var likeCountLabel: UILabel!
    @IBOutlet weak var timestampLabel: UILabel!
    @IBOutlet weak var likeButton: UIButton!

    @IBOutlet weak var constraintBetweenMessageLabelAndPostImageView: NSLayoutConstraint!

    var post: Post! {
        didSet {
            post.author.profileImage(version: .Thumbnail) { image, _ in
                self.userProfileImageView.image = image
            }

            if let name = post.author.fullName {
                usersNameLabel.text = name
                usernameLabel.text = post.author.username
            } else {
                usersNameLabel.text = post.author.username
                usernameLabel.hidden = true
            }

            if let attributedMessage = post.attributedMessage {
                messageLabel.attributedText = attributedMessage
                messageLabel.hidden = false
                constraintBetweenMessageLabelAndPostImageView.constant = 15
            } else {
                messageLabel.hidden = true
                messageLabel.attributedText = nil
                constraintBetweenMessageLabelAndPostImageView.constant = 0
            }

            post.image { image, _ in
                if let image = image {
                    self.postImageView.image = image
                    self.setNeedsUpdateConstraints()
                }
            }

            timestampLabel.text = "Posted \(post.createdAt.timeAgoSinceNow())"
            distanceLabel.text = post.distanceText
            likeCountLabel.text = "\(post.likesCount) Likes"
            likeButton.setImage(post.likeButtonImage, forState: .Normal)
        }
    }

    override func updateConstraints() {
        if let aspectRatio = post.imageAspectRatio {
            postImageAspectConstraint = NSLayoutConstraint(item: postImageView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: postImageView, attribute: NSLayoutAttribute.Height, multiplier: CGFloat(aspectRatio), constant: 0)
        }

        super.updateConstraints()
    }

    func setupUserProfileButton(indexPath: NSIndexPath, viewController: UIViewController) {
        userProfileButton.indexPath = indexPath
        userProfileButton.addTarget(viewController, action: "userProfileButtonDidTouch:", forControlEvents: .TouchUpInside)
    }

    var postImageAspectConstraint: NSLayoutConstraint? {
        didSet {
            if let oldValue = oldValue {
                postImageView.removeConstraint(oldValue)
            }
            if let postImageAspectConstraint = postImageAspectConstraint {
                postImageView.addConstraint(postImageAspectConstraint)
            }
        }
    }

    override func prepareForReuse() {
        super.prepareForReuse()

        postImageAspectConstraint = nil
    }

}

Method cellForRowAtIndexPath:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("postCell", forIndexPath: indexPath) as! PostTableViewCell

    cell.post = posts[indexPath.row]
    cell.setupUserProfileButton(indexPath, viewController: self)

    return cell
}

When the application starts, everything works as expected, but if I wave it a little, it leads to a break, and the aspect of the images is no longer right or some other strange things, as shown below:

Broken cell

Errors:

2015-09-28 11:58:27.546 Lome[32812:528936] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0x7f8a008bf6f0 V:[Lome.TFImageView:0x7f8a008bf470(35)]>",
    "<NSLayoutConstraint:0x7f89f9fbc490 V:|-(15)-[Lome.TFCellButton:0x7f8a0087fde0]   (Names: '|':UITableViewCellContentView:0x7f8a008bf270 )>",
    "<NSLayoutConstraint:0x7f8a00831520 Lome.TFCellButton:0x7f8a0087fde0.top == Lome.TFImageView:0x7f8a008bf470.top>",
    "<NSLayoutConstraint:0x7f8a008315c0 Lome.TFCellButton:0x7f8a0087fde0.bottom == Lome.TFImageView:0x7f8a008bf470.bottom>",
    "<NSLayoutConstraint:0x7f8a00831750 V:[Lome.TFCellButton:0x7f8a0087fde0]-(18)-[UILabel:0x7f8a008806d0'Water']>",
    "<NSLayoutConstraint:0x7f8a00831840 H:|-(0)-[UIImageView:0x7f8a00830910]   (Names: '|':UITableViewCellContentView:0x7f8a008bf270 )>",
    "<NSLayoutConstraint:0x7f8a00831890 H:[UIImageView:0x7f8a00830910]-(0)-|   (Names: '|':UITableViewCellContentView:0x7f8a008bf270 )>",
    "<NSLayoutConstraint:0x7f8a008318e0 V:[UILabel:0x7f8a008806d0'Water']-(15)-[UIImageView:0x7f8a00830910]>",
    "<NSLayoutConstraint:0x7f8a00831930 V:[UIImageView:0x7f8a00830910]-(20)-[UILabel:0x7f8a00830b90'0 Likes']>",
    "<NSLayoutConstraint:0x7f8a00831980 V:[UILabel:0x7f8a00830b90'0 Likes']-(20)-|   (Names: '|':UITableViewCellContentView:0x7f8a008bf270 )>",
    "<NSLayoutConstraint:0x7f8a00860870 UIImageView:0x7f8a00830910.width == 0.6672*UIImageView:0x7f8a00830910.height>",
    "<NSLayoutConstraint:0x7f8a0084e990 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7f8a008bf270(611)]>",
    "<NSLayoutConstraint:0x7f8a0084f930 'UIView-Encapsulated-Layout-Width' H:[UITableViewCellContentView:0x7f8a008bf270(375)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7f8a008bf6f0 V:[Lome.TFImageView:0x7f8a008bf470(35)]>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
2015-09-28 11:58:27.547 Lome[32812:528936] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0x7f89f9fbc490 V:|-(15)-[Lome.TFCellButton:0x7f8a0087fde0]   (Names: '|':UITableViewCellContentView:0x7f8a008bf270 )>",
    "<NSLayoutConstraint:0x7f8a00831520 Lome.TFCellButton:0x7f8a0087fde0.top == Lome.TFImageView:0x7f8a008bf470.top>",
    "<NSLayoutConstraint:0x7f8a008315c0 Lome.TFCellButton:0x7f8a0087fde0.bottom == Lome.TFImageView:0x7f8a008bf470.bottom>",
    "<NSLayoutConstraint:0x7f8a00831750 V:[Lome.TFCellButton:0x7f8a0087fde0]-(18)-[UILabel:0x7f8a008806d0'Water']>",
    "<NSLayoutConstraint:0x7f8a00831840 H:|-(0)-[UIImageView:0x7f8a00830910]   (Names: '|':UITableViewCellContentView:0x7f8a008bf270 )>",
    "<NSLayoutConstraint:0x7f8a00831890 H:[UIImageView:0x7f8a00830910]-(0)-|   (Names: '|':UITableViewCellContentView:0x7f8a008bf270 )>",
    "<NSLayoutConstraint:0x7f8a008318e0 V:[UILabel:0x7f8a008806d0'Water']-(15)-[UIImageView:0x7f8a00830910]>",
    "<NSLayoutConstraint:0x7f8a00831930 V:[UIImageView:0x7f8a00830910]-(20)-[UILabel:0x7f8a00830b90'0 Likes']>",
    "<NSLayoutConstraint:0x7f8a00831980 V:[UILabel:0x7f8a00830b90'0 Likes']-(20)-|   (Names: '|':UITableViewCellContentView:0x7f8a008bf270 )>",
    "<NSLayoutConstraint:0x7f8a00860870 UIImageView:0x7f8a00830910.width == 0.6672*UIImageView:0x7f8a00830910.height>",
    "<NSLayoutConstraint:0x7f8a0084e990 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7f8a008bf270(611)]>",
    "<NSLayoutConstraint:0x7f8a0084f930 'UIView-Encapsulated-Layout-Width' H:[UITableViewCellContentView:0x7f8a008bf270(375)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7f8a00860870 UIImageView:0x7f8a00830910.width == 0.6672*UIImageView:0x7f8a00830910.height>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

, - , , prepareForReuse.

, .

, - .

.

+4
3

, ImageView.

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7f8a00860870 UIImageView:0x7f8a00830910.width == 0.6672*UIImageView:0x7f8a00830910.height>

ImageView, superview 0.

, , width ( ) ImageView height, . / . ImageView, .

postImageAspectConstraint = NSLayoutConstraint(item: postImageView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: postImageView, attribute: NSLayoutAttribute.Width, multiplier: 1/CGFloat(aspectRatio), constant: 0)
+1

, , . rowHeightArray, rowHeights, .

[rowHeightArray replaceObjectAtIndex:indexPath.row withObject:[NSNumber numberWithFloat:imageHeight]];

[aTableView reloadRowsAtIndexPaths:indexPath withRowAnimation:YES];

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return [[rowHeightArray objectAtIndex:indexPath.row] floatValue];   
}

indexPath .

0

, :

[aView setTranslatesAutoresizingMaskIntoConstraints:NO];
[aView removeConstraints:view.constraints];

for(NSLayoutConstraint *constraint in aView.constraints){
    [aView.view removeConstraint:constraint];
}
0

Source: https://habr.com/ru/post/1609273/


All Articles