UITableView断断续续不滚动图像或内容获取

所以我有这个UITableView从内存中获取它的数据(已经预先加载,没有请求正在滚动,所有的东西都被加载之前视图正在布局)。 每个单元格根据UITextView和Autolayout中的文本数量dynamic计算高度。 细胞是从一个笔尖加载和重用细胞工作正常(至less我希望如此)。 在计算行高时,我使用了UITableViewAutomaticDimension,所以我不强制单元格布局两次,就像在iOS 8之前必须这样做。

这里是我填充单元格和计算高度的相关方法:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { NSString *cellType = [self reuseIdentifierForIndexPath:indexPath]; if ([cellType isEqualToString:kLoadingCell]) return kLoadingCellHeight; else if ([cellType isEqualToString:kOfflineCell]) return kOfflineCellHeight; else if ([cellType isEqualToString:kFootprintListHeaderCell]) return kHeaderCellHeight; else if ([cellType isEqualToString:kFootprintCellUnsynced]) return kUnsyncedCellHeight; else if ([cellType isEqualToString:kShowFullTripCell]) return kShowFullTripCellHeight; else if ([cellType isEqualToString:kFootprintOnMapCell]) return kFootprintOnMapCellHeight; else { return UITableViewAutomaticDimension; } } - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { NSString *cellType = [self reuseIdentifierForIndexPath:indexPath]; if ([cellType isEqualToString:kLoadingCell]) return kLoadingCellHeight; else if ([cellType isEqualToString:kOfflineCell]) return kOfflineCellHeight; else if ([cellType isEqualToString:kFootprintListHeaderCell]) return kHeaderCellHeight; else if ([cellType isEqualToString:kFootprintCellUnsynced]) return kUnsyncedCellHeight; else if ([cellType isEqualToString:kShowFullTripCell]) return kShowFullTripCellHeight; else if ([cellType isEqualToString:kFootprintOnMapCell]) return kFootprintOnMapCellHeight; else { return UITableViewAutomaticDimension; } } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSString *cellType = [self reuseIdentifierForIndexPath:indexPath]; if ([cellType isEqualToString:kLoadingCell]) { UITableViewCell *loadingCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil]; loadingCell.tag = kLoadingCellTag; loadingCell.selectionStyle = UITableViewCellSelectionStyleNone; loadingCell.backgroundColor = loadingCell.contentView.backgroundColor = [UIColor clearColor]; UIActivityIndicatorView *activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]; activityIndicatorView.center = CGPointMake(tableView.frame.size.width / 2, 20); [loadingCell.contentView addSubview:activityIndicatorView]; [activityIndicatorView startAnimating]; return loadingCell; } else if ([cellType isEqualToString:kOfflineCell]) { FPOfflineCell *offlineCell = [tableView dequeueReusableCellWithIdentifier:kOfflineCell]; return offlineCell; } else if ([cellType isEqualToString:kFootprintListHeaderCell]) { FPFootprintListHeaderCell *headerCell = [tableView dequeueReusableCellWithIdentifier:kFootprintListHeaderCell]; [headerCell.syncButton addTarget:self action:@selector(syncButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; return headerCell; } else if ([cellType isEqualToString:kFootprintCellUnsynced]) { FPFootprintCellUnsynced *unsyncedCell = [tableView dequeueReusableCellWithIdentifier:kFootprintCellUnsynced]; unsyncedCell.footprint = self.map.footprintsNonSynced[[self unsyncedFootprintIndexForIndexPath:indexPath]]; return unsyncedCell; } else if ([cellType isEqualToString:kShowFullTripCell]) { FPShowFullTripCell *showFullTripCell = [tableView dequeueReusableCellWithIdentifier:kShowFullTripCell]; return showFullTripCell; } else if ([cellType isEqualToString:kFootprintOnMapCell]) { FPFootprintOnMapCell *footprintOnMapCell = [tableView dequeueReusableCellWithIdentifier:kFootprintOnMapCell]; footprintOnMapCell.footprint = self.map.footprints[0]; return footprintOnMapCell; } else { FPFootprint *footprint = self.map.footprints[[self footprintIndexForIndexPath:indexPath]]; FootprintCell *cell = [tableView dequeueReusableCellWithIdentifier:kFootprintCell]; cell.titleLabel.text = footprint.name; cell.dateLabel.text = footprint.displayDate; cell.textView.text = nil; if (footprint.text && footprint.text.length > 0) { if ([self.readmoreCache[@(footprint.hash)] boolValue]) { cell.textView.text = footprint.text; } else { cell.textView.text = [footprint.text stringByAppendingReadMoreAndLimitingToCharacterCount:300 screenWidth:tableView.frame.size.width]; } } else { cell.hasText = NO; } cell.textView.markdownLinkTextViewDelegate = self; [cell.textView setNeedsDisplay]; cell.isPrivate = footprint.isPrivate; [cell.likesAndCommentsView setLikesCount:footprint.likes andCommentsCount:footprint.comments]; [cell.likesAndCommentsView setLiked:footprint.liked]; [cell.likesAndCommentsView.likeButton addTarget:self action:@selector(likeButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; [cell.likesAndCommentsView.likesTextButton addTarget:self action:@selector(likesTextButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; [cell.likesAndCommentsView.commentButton addTarget:self action:@selector(commentButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; [cell.likesAndCommentsView.commentsTextButton addTarget:self action:@selector(commentsTextButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; [cell.detailButton addTarget:self action:@selector(detailButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; [cell.translateButton addTarget:self action:@selector(translateButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; if (footprint.canBeTranslated) { cell.translationStatus = footprint.translationState; if (footprint.translationState == FPFootprintTranslationStateTranslated) { cell.translatedTextView.text = footprint.translatedText; } } else { cell.translationStatus = FPFootprintTranslationStateNotAvailible; } cell.numberOfImages = 2; return cell; } } 

这是我的手机:

 import UIKit @objc class FootprintCell: UITableViewCell { var translationStatus: FPFootprintTranslationState = .NotTranslated { didSet { translateButton.hidden = true translateLoader.stopAnimating() translatedTextView.hidden = true translatedTextView.text = nil translatedTextView.addConstraint(translatedTextViewHeightConstraint) translationButtonHeightConstraint.constant = 0 loaderHeightConstraint.constant = 0 switch translationStatus { case .NotAvailible: break case .NotTranslated: translateButton.hidden = false translationButtonHeightConstraint.constant = translationButtonHeightConstraintConstant case .Translating: translateLoader.startAnimating() loaderHeightConstraint.constant = loaderHeightConstraintConstant translatedTextView.text = nil case .Translated: translatedTextView.hidden = false translatedTextView.removeConstraint(translatedTextViewHeightConstraint) } } } var isPrivate: Bool = false { didSet { privacyBar.hidden = !isPrivate privacyIcon.image = UIImage(named: isPrivate ? "ic_lock" : "ic_globe") } } var hasText: Bool = true { didSet { if hasText { textView.removeConstraint(textViewHeightConstraint) } else { textView.addConstraint(textViewHeightConstraint) } } } var numberOfImages: Int = 0 { didSet { if numberOfImages == 0 { imagesContainer.subviews.map { $0.removeFromSuperview() } } else if numberOfImages == 2 { twoImagesContainer = NSBundle.mainBundle().loadNibNamed("FootprintCellTwoImagesContainer", owner: nil, options: nil)[0] as? FootprintCellTwoImagesContainer twoImagesContainer?.setTranslatesAutoresizingMaskIntoConstraints(false) imagesContainer.addSubview(twoImagesContainer!) let views = ["foo" : twoImagesContainer!] as [NSString : AnyObject] imagesContainer.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[foo]|", options: .allZeros, metrics: nil, views: views)) imagesContainer.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[foo]|", options: .allZeros, metrics: nil, views: views)) } } } @IBOutlet private(set) weak var titleLabel: UILabel! @IBOutlet private(set) weak var dateLabel: UILabel! @IBOutlet private(set) weak var textView: FPForwardingTextView! @IBOutlet private(set) weak var likesAndCommentsView: FPLikesAndCommentsView! @IBOutlet private weak var privacyBar: UIView! @IBOutlet private weak var privacyIcon: UIImageView! @IBOutlet private(set) weak var detailButton: UIButton! @IBOutlet private(set) weak var translateButton: UIButton! @IBOutlet private weak var translateLoader: UIActivityIndicatorView! @IBOutlet private(set) weak var translatedTextView: FPForwardingTextView! @IBOutlet private(set) weak var imagesContainer: UIView! private(set) var twoImagesContainer: FootprintCellTwoImagesContainer? @IBOutlet private weak var translationButtonHeightConstraint: NSLayoutConstraint! @IBOutlet private weak var loaderHeightConstraint: NSLayoutConstraint! @IBOutlet private var translatedTextViewHeightConstraint: NSLayoutConstraint! @IBOutlet private var textViewHeightConstraint: NSLayoutConstraint! private var translationButtonHeightConstraintConstant: CGFloat! private var loaderHeightConstraintConstant: CGFloat! override func awakeFromNib() { super.awakeFromNib() textView.contentInset = UIEdgeInsets(top: -10, left: -5, bottom: 0, right: 0) textView.linkColor = UIColor(fromHexString: "0088CC") translatedTextView.contentInset = UIEdgeInsets(top: -10, left: -5, bottom: 0, right: 0) translatedTextView.linkColor = UIColor(fromHexString: "0088CC") privacyBar.backgroundColor = UIColor(patternImage: UIImage(named: "ic_privacy_bar")) translatedTextView.text = nil translatedTextView.hidden = true translateButton.hidden = true translationButtonHeightConstraintConstant = translationButtonHeightConstraint.constant loaderHeightConstraintConstant = loaderHeightConstraint.constant hasText = true } func layoutMargins() -> UIEdgeInsets { return UIEdgeInsetsZero } override func prepareForReuse() { super.prepareForReuse() numberOfImages = 0 translationStatus = .NotAvailible hasText = true } } 

FootprintCellTwoImagesContainerFPLikesAndCommentsView从Nibs加载,目前不包含任何图像或加载任何东西,只是一些Autolayout。

所以主要的问题是,即使当整个tableView被加载,并且每个单元格至less被显示一次(所以应该有足够的单元格重用),慢慢滚动单元格边界上或下,我得到一个小跳跃(如5像素上下)。 这发生在每台设备上,即使在6 Plus上也是如此。

任何想法的问题可能是? 我希望它不是在我的约束xibs的东西,至lessInterface Builder不会在那里发出警告…

我不太确定UITableViewAutomaticDimension是表格单元格。 从文档…

当你想让UITableViewselect一个默认值时,你从UITableViewDelegate方法返回这个请求维度指标的值。 例如,如果在tableView:heightForHeaderInSection:或tableView:heightForFooterInSection:中返回此常量,则UITableView将使用适合从tableView:titleForHeaderInSection:或tableView:titleForFooterInSection:(如果标题不为零)返回的值的高度。

没有提到tableview单元格。

所以我做了一个search,发现了这个… 关于UITableViewAutomaticDimension的更多讨论…

说的地方..

不起作用。 UITableViewAutomaticDimension不打算用于设置行高度。 使用rowHeight并指定您的值或实现:

所以我认为你可能有这个错误。

确定之前的代码,原则。 我有一个列中有4个标签的自定义单元格。 顶部标签(label1)总是具有文本,底部标签(label4)也总是具有文本。 标签2和3可能包含文本,这是可能的,或两者都可能。 为了实现resize,我们使用部分自动布局和部分委托方法(离你所在的地方不远)

在“接口”构build器中,我们为原型单元格设置约束

标签1:领导,尾随,顶部,高度,宽度

标签2:前导,尾随,顶部,底部,高度,宽度

标签3:前导,尾随,顶部,底部,高度,宽度

标签4:领导,尾随,顶部,底部,高度,宽度

对于标签1和4(顶部和底部),我们将内容压缩阻力优先级垂直设置为“必需”(1000)。对于标签2和3, 我们将内容压缩阻力优先级垂直设置为“

这基本上意味着如果高度应该减小,首先折叠标签2和3,然后折叠标签1和4.(您可能已经知道这一切)您应该没有任何警告,并且您的限制都已正确添加。 (除非你知道它是什么,否则不要使用约束边界)

现在的代码。

 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { // Calls the sizing method to return a calculated height. return [self heightForBasicCellAtIndexPath:indexPath]; 

}

 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { //taken from interface builder. If all 4 labels have strings and not collapsed, this is the height the cell will be. return 123.0f; 

}

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // Yours has lots of case logic - // Mine is similar but I configure the properties of the custom cell elsewhere mainly so it can be used for sizing. [self configureCell:cell atIndexPath:indexPath]; return cell; 

}

 - (void)configureCell:(MyJobCell *)cell atIndexPath:(NSIndexPath *)indexPath { // my data source MyCase *aCase = [_fetchedResultsController objectAtIndexPath:indexPath]; // setting the labels to match the case from data cell.label1.text = aCase.name; cell.label2.text = aCase.address; cell.label3.text = aCase.postcode; cell.label4.text = aCase.caseDescription; 

}

 - (CGFloat)heightForBasicCellAtIndexPath:(NSIndexPath *)indexPath { // In here I create a cell and configure it with a cell identifier static MyJobCell *sizingCell = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sizingCell = [self.tableView dequeueReusableCellWithIdentifier:MyJobCellIdentifier]; }); // This configures the sizing cell labels with text values [self configureCell:sizingCell atIndexPath:indexPath]; // This line calls the calculation. It fires the Auto Layout constraints on the cell, // If label 2 and / or label 3 are empty, they will be collapsed to 0 height. return [self calculateHeightForConfiguredSizingCell:sizingCell]; 

}

 - (CGFloat)calculateHeightForConfiguredSizingCell:(MYJobCell *)sizingCell { //Force the cell to update its constraints [sizingCell setNeedsLayout]; [sizingCell layoutIfNeeded]; // Get the size of the 'squashed' cell and return it to caller CGSize size = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; return size.height; 

}

这是我能做的最好的工作方法。 你将不得不做出调整,以适应所有不同types的自定义单元格。 但除此之外,我认为这应该有所帮助。 让我知道你是怎么办的。