簡體   English   中英

UITableView不連貫滾動沒有圖像或內容提取

[英]UITableView choppy scrolling WITHOUT images or content fetching

所以我有這個UITableView從內存中獲取數據(已經預加載,滾動時沒有請求,所有內容都在視圖布局之前加載)。 每個單元格的高度根據UITextView和Autolayout中的文本量動態計算。 從Nib加載細胞並重復使用細胞正常工作(至少我希望如此)。 我在計算行高時使用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是從Nib加載的,目前不包含任何圖像或加載任何東西,只是一些Autolayout。

因此,主要的問題是即使整個tableView被加載並且每個單元格至少顯示一次(因此應該有足夠的單元格可以重復使用),在緩慢地向上或向下滾動單元格邊框之后,我得到一個小跳躍(如5像素上下)。 這種情況發生在每台設備上,即使在6 Plus上也是如此。

問題可能是什么想法? 我希望它不是我在xib中的約束,至少Interface Builder不會在那里發出警告......

我不太確定UITableViewAutomaticDimension是用於表格單元格。 從文檔......

當您希望UITableView選擇默認值時,從UITableViewDelegate方法返回此值,該方法請求維度指標。 例如,如果在tableView:heightForHeaderInSection:或tableView:heightForFooterInSection:中返回此常量,則UITableView使用的高度適合從tableView返回的值:titleForHeaderInSection:或tableView:titleForFooterInSection :(如果標題不是nil)。

沒有提到tableview單元格。

所以我做了一個搜索,發現了這個...... 關於UITableViewAutomaticDimension的更多討論......

在哪里說..

不起作用。 UITableViewAutomaticDimension不用於設置行高。 使用rowHeight並指定您的值或實現:

所以我認為你可能有錯。

OK之前的代碼,原則。 我有一個列中有4個標簽的自定義單元格。 頂部標簽(label1)始終具有文本,底部標簽(label4)也始終具有文本。 標簽2和3可以包含文本,可以是一個,也可以是兩個。 為了實現調整大小,我們使用部分自動布局和部分委托方法(距離您的所有地方不遠)

在“接口”構建器中,我們設置原型單元的約束

標簽1:前導,尾隨,頂部,高度,寬度

Label2:前導,尾隨,頂部,底部,高度,寬度

Label3:前導,尾隨,頂部,底部,高度,寬度

Label4:前導,尾隨,頂部,底部,高度,寬度

對於標簽1和4(頂部和底部),我們將內容壓縮電阻優先級垂直設置為“必需”(1000)對於標簽2和3, 我們將內容壓縮電阻優先級垂直設置為“低”(250)

這基本上意味着如果高度應該減少,首先折疊標簽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;

}

這是我向你展示工作方法的最好方法。 您必須進行調整,以便邏輯地滿足您擁有的所有不同類型的自定義單元格。 但除此之外,我認為這應該有所幫助。 讓我知道你是怎么辦的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM