首頁 > 軟體

ios開發UITableViewCell圖片載入優化詳解

2022-07-19 22:02:19

前言

我們平時用UITableView用的很多,所以對列表的優化也是很關注的。很多時候,我們設定UIImageView,都是比例固定好寬高的,然後通過 scaleAspectFillclipsToBounds 保持圖片不變形,這樣子做開發的效率是很高的,畢竟圖片寬高我們都是固定好的了。

那如果產品要求圖片按真正的比例展示出來呢?如果伺服器有返回寬和高,那就好辦了,那如果沒有呢,我們應該怎麼去做呢?

下面就讓我們一起來探索吧。

圖片自適應比例

一般我們的做法都是用UITableViewAutomaticDimension來實現的。

以往的做法,我們都是直接 sd_setImageWithURL 來實現新增圖片,那現在也一樣,我們也是通過這個來獲取圖片寬和高。

- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                 completed:(nullable SDExternalCompletionBlock)completedBlock

完成後,我們可以拿到UIImage,從而知道圖片的 size 。然後我們就可以按比例來獲取圖片的高度,再通過更新約束來改變圖片高度。

大概做法如下:

[_imageView sd_setImageWithURL:[NSURL URLWithString:model.urlStr] completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL _Nullable imageURL) {
            if (image) {
                // 獲取圖片寬高
                CGSize imageSize = image.size;
                CGFloat maxWidth = kSCREEN_WIDTH - 32;
                // 按比例獲取當前的高度
                CGFloat height = imageSize.height * maxWidth / imageSize.width;
                [self.imageView mas_updateConstraints:^(MASConstraintMaker *make) {
                    make.height.mas_equalTo(height);
                }];
            }
        }];

然後我們一執行程式碼,發現圖片的高度並沒有展示我們想要的高度,可以說是連高度都沒有。

很明顯是圖片的高度沒有生效。想想也是,我們是非同步呼叫載入圖片的,這時候等非同步結果回來呼叫mas_updateConstraints,並不會觸發代理 heightForRowAtIndexPath,那怎麼會更新高度呢。

既然是這樣,那我們就重新載入該列表吧。

[cell setHeightBlock:^(CGFloat imageHeight){
    [tableView beginUpdates];
    [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
    [tableView endUpdates];
}];

我們更新完mas_updateConstraints後,這時候,直接重新整理該列表,就可以解決問題了。

這時候我們重新執行,效果還可以,但在來回捲動的時候,發現有點卡。很明顯,我們捲動的時候每次都要重新重新整理cell

如果我們有快取了,那就知道了圖片的高度,那我們是不是就不需要reloadRowsAtIndexPaths

所以我再次進行優化

// 是否有快取
BOOL hasCache = [[SDImageCache sharedImageCache] diskImageDataExistsWithKey:model.urlStr];
[_ImageView sd_setImageWithURL:[NSURL URLWithString:model.urlStr] completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
    if (image) {
        CGSize imageSize = image.size;
        CGFloat maxWidth = kSCREEN_WIDTH - 32;
        CGFloat height = imageSize.height * maxWidth / imageSize.width;
        [self.imageView mas_updateConstraints:^(MASConstraintMaker *make) {
            make.height.mas_equalTo(height);
        }];
         // 有快取就不去reloadRowsAtIndexPaths
         if (!hasCache && self.heightBlock) {
             self.heightBlock(height);
         }
    }
}];

如果發現是有快取圖片了,我們就不重新整理列表了。sd_setImageWithURL這時候也是從快取裡面讀取圖片,就不是非同步載入了,所以不用再次重新整理了當前cell了。

XHWebImageAutoSize

有的人說,這麼寫好像挺麻煩的,有沒有封裝好的寫法,的確有的。

那就是第三方庫 XHWebImageAutoSize,它的寫法其實也是用了 SDWebImage 來進行優化操作的。

[_imageView sd_setImageWithURL:[NSURL URLWithString:model.urlStr] completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
    if (image) {
        /** 快取image size */
        [XHWebImageAutoSize storeImageSize:image forURL:imageURL completed:^(BOOL result) {
            /** reload  */
            if(result && self.heightBlock) {
                self.heightBlock(0)
            }
        ];
    }
}];

快取圖片後,一樣是去重新整理cell。

[cell setHeightBlock:^(CGFloat imageHeight) {
    [tableView xh_reloadDataForURL:[NSURL URLWithString:model.urlStr]];
 }];

然後就是重新載入高度。

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    ImageModel *model = _dataArray[indexPath.row];
    return [XHWebImageAutoSize imageHeightForURL:[NSURL URLWithString:model.urlStr] layoutWidth:[UIScreen mainScreen].bounds.size.width-32 estimateHeight:200] + 50;
}

後面加的 50是其他的高度,例如cell裡面還有title,就是圖片+其他高度。

這樣也能實現圖片自適應高度。

僅載入當前螢幕的內容

圖片列表實在太多了,一直滑滑滑,圖片載入速度跟不上手速啊,感覺有點卡,我們可以僅載入當前螢幕的內容。滑動的時候,我們不載入,等列表停後,我們再次載入當前螢幕的內容。

這時候我們在模型model裡面,新增一個isLoad的引數,如果是true,我們才載入。

先新增一個當前螢幕載入cell的方法。

-(void)loadCurrentCells{
    NSArray * array = [self.tableView indexPathsForVisibleRows];
    for (NSIndexPath *indexPath in array) {
        JJTableViewCell * cell = (JJTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
        ImageModel *model = _dataArray[indexPath.row];
        //設定為可以載入
        model.isLoad = YES;
       //設定cell
        [cell configCellWithImageModel:model];
    }
}

cell裡面對modelisLoad進行判斷。

- (void)configCellWithImageModel:(ImageModel *)model
{
    if (model.isLoad) {
        [_imageView sd_setImageWithURL:[NSURL URLWithString:model.urlStr]];
    }else {
        _imageView.image = [UIImage imageNamed:@"default_images_icon"];
    }
}

如果暫不載入,我們可以先設定佔用圖片。

這樣一來,我們只要監聽停止滑動的時候,我們就設定載入當前頁面的內容即可。

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    if (!decelerate) {
        [self loadCurrentCells];
    }
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    [self loadCurrentCells];
}

當然一開始我們reloaData的時候,所有isload都為false,所以我們需要載入一次當前cell內容。

[self.tableView reloadData];
[self loadCurrentCells];

這樣,我們就可以做到一直滑動的時候,不非同步載入圖片,等滑動停止再載入當前螢幕的圖片。

當然除了這種寫法,我們還可以通過RunLoop來實現。

預載入

所謂預載入,就是一直滑動,我們翻頁的時候,提前載入資料出來,讓使用者的感覺就是一直有資料。就沒有出現上拉載入更多這種情況。

總體思路是:當滑動距離佔比到了總滑動距離的時候的%90(不固定),就觸發預載入。

這裡我就不寫了,直接把大佬的連結搞過來:iOS開發 TableView 網路請求/展示預載入 

以上就是ios開發UITableViewCell圖片載入優化詳解的詳細內容,更多關於ios UITableViewCell圖片載入的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com