异步加载的图像的表格视图单元格的动态高度
问题描述:
我有一个自定义单元格在XIB文件内部具有动态高度,该单元格显示带有消息和图像的文章。由于图像将从我的API异步加载,图像的宽高比将与资源一起发送。在updateConstraints
方法中,将设置具有适当乘数的宽高比约束。异步加载的图像的表格视图单元格的动态高度
的XIB文件:
Cell类:
//
// 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
}
}
的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
}
在应用程序启动按预期工作的一切,但如果我有一点冷静,这个限制开始打破了在图像方面都不对了或像下面的一些其他奇怪的事情:
错误:
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
方法中被删除。
我目前很沮丧,因为我试图解决这个问题,因为几天没有找到解决方案。
我希望有人有一个解决方案。
在此先感谢。
答
此问题是由施加到ImageView
2层冲突的约束造成的。如果你看看调试器输出
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7f8a00860870 UIImageView:0x7f8a00830910.width == 0.6672*UIImageView:0x7f8a00830910.height>
你已经拥有水平约束,以寄托对superview
0间距ImageView
。
现在同时更新宽高比约束你想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)
答
一旦你得到图像高度,你应该 更新你的阵列,例如, rowHeightArray,它跟踪所有的行高,例如
[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返回对象。
答
要删除的限制,检查:
[aView setTranslatesAutoresizingMaskIntoConstraints:NO];
[aView removeConstraints:view.constraints];
或
for(NSLayoutConstraint *constraint in aView.constraints){
[aView.view removeConstraint:constraint];
}
你可以尝试设置图像视图的高度而不是'updateConstraints:'中的宽度。从第一次看,这似乎是一个模棱两可的情况,因为你已经用'0'间距固定'ImageView'水平布局,然后尝试根据高度改变宽度。 – Gandalf
这是一个纵横比约束而非宽度约束。 UIImageView的高度是宽度的0.6672倍。然而,如果我在IB中设置了一个固定的高度,并且不添加这种纵横比约束,它可以工作,但由于每个图像都是不同的,所以我总是有相同的高度和错误的宽高比。 – Tobias
我知道你已经应用了宽高比约束。我想你误解了我指的是什么。在你的纵横比约束中,“第一项”是“宽度”。这意味着约束将试图使'宽度'的变化不是该视图的'高度'。因此,如果您倒转第一个和第二个项目,并在乘数参数中反转宽高比,则您将具有相同的约束,但根据图像宽高比具有不同的高度。 – Gandalf