异步加载图像的表格视图单元格的动态高度
Posted
技术标签:
【中文标题】异步加载图像的表格视图单元格的动态高度【英文标题】:Dynamic height for table view cells with images asynchronous loaded 【发布时间】:2015-09-28 10:08:42 【问题描述】:我在 XIB 文件中有一个具有动态高度的自定义单元格,该单元格显示带有消息和图像的帖子。因为图像将从我的 API 异步加载,所以图像的纵横比将与资源一起发送。在updateConstraints
方法中,将设置具有适当乘数的纵横比约束。
XIB 文件:
细胞类:
//
// 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
方法中删除。
我目前非常沮丧,因为几天以来我一直在尝试解决这个问题,但没有找到解决方案。
希望有人能解决。
提前致谢。
【问题讨论】:
您可以尝试设置图像视图的高度而不是updateConstraints:
中的宽度。乍一看,这似乎是一个模棱两可的情况,因为您已经将 ImageView
固定为具有 0 间距的水平布局,然后尝试根据高度更改宽度。
这是一个纵横比约束而不是宽度约束。 UIImageView 的高度是宽度的 0.6672 倍。但是,如果我在 IB 中设置固定高度并且不添加此纵横比约束,它可以工作,但我始终具有相同的高度和错误的纵横比,因为每个图像都是不同的。
我知道您已应用纵横比限制。我想你误解了我的意思。在您的纵横比约束中,first item
是width
。这意味着约束将尝试在 width
中进行更改,而不是对该视图的 height
进行更改。因此,如果您从中反转第一项和第二项并反转乘数参数中的纵横比,您将具有相同的约束,但根据图像纵横比具有不同的高度。
谢谢!!它解决了我的问题!将此评论写为答案,我会将其标记为正确答案。
感谢您提供覆盖 updateLayoutConstraints 的提示!这救了我的命!!!不错的代码顺便说一句:-)
【参考方案1】:
此问题是由应用于 ImageView
的 2 个冲突约束引起的。如果您查看调试器输出
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7f8a00860870 UIImageView:0x7f8a00830910.width == 0.6672*UIImageView:0x7f8a00830910.height>
您已经对以 0 间距固定到 superview
的 ImageView
具有水平约束。
现在在更新纵横比约束时,您尝试将ImageView
的width
(保持宽度作为第一项)设置为其height
的比率,这将导致与它的前导/尾随冲突约束。
您需要通过交换第一项和第二项并反转纵横比因子来设置此 ImageView 的高度。
postImageAspectConstraint = NSLayoutConstraint(item: postImageView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: postImageView, attribute: NSLayoutAttribute.Width, multiplier: 1/CGFloat(aspectRatio), constant: 0)
【讨论】:
【参考方案2】:一旦你得到图像高度,你应该 更新您的数组,例如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 处返回具有行高的对象。
【讨论】:
单元格的行高设置为UITableViewAutomaticDimension
。通过自动布局约束,将自动定义行高。我遇到的问题是纵横比约束在上下滚动后被破坏。
您是否尝试向 UIImageView 添加约束?图像是否具有相同的宽度和高度或至少相同的比例?如果没有,你应该删除以前的约束并添加一个新的约束或检查 ScaleToFit 或 ScaleToFill...我真的不知道你这样做的方式。【参考方案3】:
要删除约束,请检查:
[aView setTranslatesAutoresizingMaskIntoConstraints:NO];
[aView removeConstraints:view.constraints];
或
for(NSLayoutConstraint *constraint in aView.constraints)
[aView.view removeConstraint:constraint];
【讨论】:
以上是关于异步加载图像的表格视图单元格的动态高度的主要内容,如果未能解决你的问题,请参考以下文章
具有异步图像加载的自调整 UICollectionView 单元格