Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios UICollection视图与SDWebImage的滚动延迟_Ios_Objective C_Ipad_Uicollectionview_Uicollectionviewcell - Fatal编程技术网

Ios UICollection视图与SDWebImage的滚动延迟

Ios UICollection视图与SDWebImage的滚动延迟,ios,objective-c,ipad,uicollectionview,uicollectionviewcell,Ios,Objective C,Ipad,Uicollectionview,Uicollectionviewcell,背景 - (void)setProduct:(Product *)product { _product = product; _spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; _spinner.center = CGPointMake(CGRectGetMidX(self.bounds), CGRec

背景

- (void)setProduct:(Product *)product
{

    _product = product;

    _spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    _spinner.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
    [self addSubview:_spinner];
    [_spinner startAnimating];
    _spinner.hidesWhenStopped = YES;

    // Add a spinner

    __block UIActivityIndicatorView *tmpSpinner = _spinner;
    __block UIImageView *tmpImgView = _thumbnailImgView;
    ProductImage *thumbnailImage = _product.images[0];


    [_thumbnailImgView sd_setImageWithURL:[NSURL URLWithString:thumbnailImage.mediumURL]
                                completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
                                    // dismiss the spinner
                                    [tmpSpinner stopAnimating];
                                    [tmpSpinner removeFromSuperview];
                                    tmpSpinner = nil;
                                    if (nil == error) {

                                        // Resize the incoming images
                                        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                                            CGFloat imageHeight = image.size.height;
                                            CGFloat imageWidth = image.size.width;

                                            CGSize newSize = tmpImgView.bounds.size;
                                            CGFloat scaleFactor = newSize.width / imageWidth;
                                            newSize.height = imageHeight * scaleFactor;

                                            UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
                                            [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
                                            UIImage *small = UIGraphicsGetImageFromCurrentImageContext();
                                            UIGraphicsEndImageContext();

                                            dispatch_async(dispatch_get_main_queue(),^{
                                                tmpImgView.image = small;
                                            });

                                        });


                                        if (cacheType == SDImageCacheTypeNone) {
                                            tmpImgView.alpha = 0.0;

                                            [UIView animateWithDuration:0.2
                                                                  delay:0
                                                                options:(UIViewAnimationOptionCurveLinear | UIViewAnimationOptionAllowUserInteraction)
                                                             animations:^{
                                                                 tmpImgView.alpha = 1.0;
                                                             } completion:nil];
                                        }

                                    } else {
                                        // loading error
                                        [tmpImgView setImage:[UIImage imageNamed:@"broken_image_small"]];
                                    }
                                }];

    _brandLbl.text = [_product.brand.name uppercaseString];

    _nameLbl.text = _product.name;
    [_nameLbl sizeToFit];


    // Format the price
    NSNumberFormatter * floatFormatter = [[NSNumberFormatter alloc] init];
    [floatFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
    [floatFormatter setDecimalSeparator:@"."];
    [floatFormatter setMaximumFractionDigits:2];
    [floatFormatter setMinimumFractionDigits:0];
    [floatFormatter setGroupingSeparator:@","];

    _priceLbl.text = [NSString stringWithFormat:@"$%@ USD", [floatFormatter stringFromNumber:_product.price]];

    if (_product.salePrice.intValue > 0) {
        NSString *rawStr = [NSString stringWithFormat:@"$%@ $%@ USD", [floatFormatter stringFromNumber:_product.price], [floatFormatter stringFromNumber:_product.salePrice]];

        NSMutableAttributedString * string = [[NSMutableAttributedString alloc] initWithString:rawStr];
        // Change all the text to red first
        [string addAttribute:NSForegroundColorAttributeName
                       value:[UIColor colorWithRed:157/255.0 green:38/255.0 blue:29/255.0 alpha:1.0]
                       range:NSMakeRange(0,rawStr.length)];

        // find the first space
        NSRange firstSpace = [rawStr rangeOfString:@" "];

        // Change from zero to space to gray color
        [string addAttribute:NSForegroundColorAttributeName
                       value:_priceLbl.textColor
                       range:NSMakeRange(0, firstSpace.location)];

        [string addAttribute:NSStrikethroughStyleAttributeName
                       value:@2
                       range:NSMakeRange(0, firstSpace.location)];

        _priceLbl.attributedText = string;
    }

}
我已经在苹果论坛和苹果搜索过了。很多人都在谈论带有图像的集合视图单元格的性能。他们中的大多数人说,自从在主线程中加载图像以来,它在滚动上是滞后的

通过使用
SDWebImage
,应在单独的线程中加载图像。然而,它仅在iPad模拟器的横向模式下滞后

问题描述

在纵向模式下,集合视图为每行加载3个单元。它没有滞后或不显著的延迟。 在横向模式下,集合视图为每行加载4个单元格。并且在帧速率上有明显的滞后和下降

我已经检查了与核心动画仪器工具。当新单元出现时,帧速率下降到8fps左右。我不确定是哪一个动作使我在collection视图中的性能如此低下

希望有人知道其中的诀窍

以下是相关代码

在视图控制器中

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    ProductCollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:@"ProductViewCell" forIndexPath:indexPath];

    Product *tmpProduct = (Product*)_ploader.loadedProduct[indexPath.row];

    cell.product = tmpProduct;

    if (cellShouldAnimate) {
        cell.alpha = 0.0;
        [UIView animateWithDuration:0.2
                              delay:0
                            options:(UIViewAnimationOptionCurveLinear | UIViewAnimationOptionAllowUserInteraction)
                         animations:^{
                            cell.alpha = 1.0;
                         } completion:nil];
    }

    if(indexPath.row >= _ploader.loadedProduct.count - ceil((LIMIT_COUNT * 0.3)))
    {

        [_ploader loadProductsWithCompleteBlock:^(NSError *error){
            if (nil == error) {

                cellShouldAnimate = NO;
                [_collectionView reloadData];
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
                    cellShouldAnimate = YES;
                });
            } else if (error.code != 1){
                #ifdef DEBUG_MODE
                    ULog(@"Error.des : %@", error.description);
                #else
                    CustomAlertView *alertView = [[CustomAlertView alloc]
                                                  initWithTitle:@"Connection Error"
                                                        message:@"Please retry."
                                                   buttonTitles:@[@"OK"]];
                    [alertView show];
                #endif
            }
        }];
    }
    return cell;

}
在collectionViewCell中预先准备

- (void)prepareForReuse
{
    [super prepareForReuse];
    CGRect bounds = self.bounds;

    [_thumbnailImgView sd_cancelCurrentImageLoad];

    CGFloat labelsTotalHeight = bounds.size.height - _thumbnailImgView.frame.size.height;

    CGFloat brandToImageOffset = 2.0;
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        brandToImageOffset = 53.0;
    }

    CGFloat labelStartY = _thumbnailImgView.frame.size.height + _thumbnailImgView.frame.origin.y + brandToImageOffset;

    CGFloat nameLblHeight = labelsTotalHeight * 0.46;
    CGFloat priceLblHeight = labelsTotalHeight * 0.18;




    _brandLbl.frame = (CGRect){{15, labelStartY}, {bounds.size.width - 30, nameLblHeight}};


    CGFloat priceToNameOffset = 8.0;

    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        priceToNameOffset = 18.0;
    }

    _priceLbl.frame = (CGRect){{5, labelStartY + nameLblHeight  - priceToNameOffset}, {bounds.size.width-10, priceLblHeight}};

    [_spinner stopAnimating];
    [_spinner removeFromSuperview];
    _spinner = nil;

}
覆盖setProduct方法

- (void)setProduct:(Product *)product
{

    _product = product;

    _spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    _spinner.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
    [self addSubview:_spinner];
    [_spinner startAnimating];
    _spinner.hidesWhenStopped = YES;

    // Add a spinner

    __block UIActivityIndicatorView *tmpSpinner = _spinner;
    __block UIImageView *tmpImgView = _thumbnailImgView;
    ProductImage *thumbnailImage = _product.images[0];


    [_thumbnailImgView sd_setImageWithURL:[NSURL URLWithString:thumbnailImage.mediumURL]
                                completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
                                    // dismiss the spinner
                                    [tmpSpinner stopAnimating];
                                    [tmpSpinner removeFromSuperview];
                                    tmpSpinner = nil;
                                    if (nil == error) {

                                        // Resize the incoming images
                                        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                                            CGFloat imageHeight = image.size.height;
                                            CGFloat imageWidth = image.size.width;

                                            CGSize newSize = tmpImgView.bounds.size;
                                            CGFloat scaleFactor = newSize.width / imageWidth;
                                            newSize.height = imageHeight * scaleFactor;

                                            UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
                                            [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
                                            UIImage *small = UIGraphicsGetImageFromCurrentImageContext();
                                            UIGraphicsEndImageContext();

                                            dispatch_async(dispatch_get_main_queue(),^{
                                                tmpImgView.image = small;
                                            });

                                        });


                                        if (cacheType == SDImageCacheTypeNone) {
                                            tmpImgView.alpha = 0.0;

                                            [UIView animateWithDuration:0.2
                                                                  delay:0
                                                                options:(UIViewAnimationOptionCurveLinear | UIViewAnimationOptionAllowUserInteraction)
                                                             animations:^{
                                                                 tmpImgView.alpha = 1.0;
                                                             } completion:nil];
                                        }

                                    } else {
                                        // loading error
                                        [tmpImgView setImage:[UIImage imageNamed:@"broken_image_small"]];
                                    }
                                }];

    _brandLbl.text = [_product.brand.name uppercaseString];

    _nameLbl.text = _product.name;
    [_nameLbl sizeToFit];


    // Format the price
    NSNumberFormatter * floatFormatter = [[NSNumberFormatter alloc] init];
    [floatFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
    [floatFormatter setDecimalSeparator:@"."];
    [floatFormatter setMaximumFractionDigits:2];
    [floatFormatter setMinimumFractionDigits:0];
    [floatFormatter setGroupingSeparator:@","];

    _priceLbl.text = [NSString stringWithFormat:@"$%@ USD", [floatFormatter stringFromNumber:_product.price]];

    if (_product.salePrice.intValue > 0) {
        NSString *rawStr = [NSString stringWithFormat:@"$%@ $%@ USD", [floatFormatter stringFromNumber:_product.price], [floatFormatter stringFromNumber:_product.salePrice]];

        NSMutableAttributedString * string = [[NSMutableAttributedString alloc] initWithString:rawStr];
        // Change all the text to red first
        [string addAttribute:NSForegroundColorAttributeName
                       value:[UIColor colorWithRed:157/255.0 green:38/255.0 blue:29/255.0 alpha:1.0]
                       range:NSMakeRange(0,rawStr.length)];

        // find the first space
        NSRange firstSpace = [rawStr rangeOfString:@" "];

        // Change from zero to space to gray color
        [string addAttribute:NSForegroundColorAttributeName
                       value:_priceLbl.textColor
                       range:NSMakeRange(0, firstSpace.location)];

        [string addAttribute:NSStrikethroughStyleAttributeName
                       value:@2
                       range:NSMakeRange(0, firstSpace.location)];

        _priceLbl.attributedText = string;
    }

}

SDWebImage非常令人钦佩,但DLImageLoader绝对令人难以置信,是许多大型生产应用程序的关键部分

它非常容易使用

为了避免略读问题,基本上只需在开始下载图像之前引入延迟。所以,本质上是这样的……就是这么简单

dispatch_after_secs_on_main(0.4, ^
    {
    if ( ! [urlWasThen isEqualToString:self.currentImage] )
        {
        // so in other words, in fact, after a short period of time,
        // the user has indeed scrolled away from that item.
        // (ie, the user is skimming)
        // this item is now some "new" item so of course we don't
        // bother loading "that old" item
        // ie, we now know the user was simply skimming over that item.
        // (just TBC in the preliminary clause above,
        // since the image is already in cache,
        // we'd just instantly load the image - even if the user is skimming)
        // NSLog(@"   --- --- --- --- --- --- too quick!");
        return;
        }

    // a short time has passed, and indeed this cell is still "that" item
    // the user is NOT skimming, SO we start loading the image.
    //NSLog(@"   --- not too quick  ");

    [DLImageLoader loadImageFromURL:urlWasThen
            completed:^(NSError *error, NSData *imgData)
        {
        if (self == nil) return;

        // some time has passed while the image was loading from the internet...

        if ( ! [urlWasThen isEqualToString:self.currentImage] )
            {
            // note that this is the "normal" situation where the user has
            // moved on from the image, so no need toload.
            //
            // in other words: in this case, not due to skimming,
            // but because SO much time has passed,
            // the user has moved on to some other part of the table.
            // we pointlessly loaded the image from the internet!  doh!

            //NSLog(@"  === === 'too late!' image load!");
            return;
            }

        UIImage *image = [UIImage imageWithData:imgData];
        self.someImage.image = image;
        }];

    });
这是一个“极其简单”的解决方案

IMO,经过大量实验后,它实际上比更复杂的解决方案(滚动浏览时的跟踪)工作得好得多

再一次,DLImageLoader使这一切变得极其简单


请注意,上面的代码部分只是在单元格中加载图像的“常用”方式

下面是实现这一点的典型代码:

-(void)imageIsNow:(NSString *)imUrl
    {
    // call this routine o "set the image" on this cell.
    // note that "image is now" is a better name than "set the image"
    // Don't forget that cells very rapidly change contents, due to
    // the cell reuse paradigm on iOS.

    // this cell is being told that, the image to be displayed is now this image
    // being aware of scrolling/skimming issues, cache issues, etc,
    // utilise this information to apprporiately load/whatever the image.

    self.someImage.image = nil; // that's UIImageView
    self.currentImage = imUrl; // you need that string property

    [self loadImageInASecIfItsTheSameAs:imUrl];
    }


-(void)loadImageInASecIfItsTheSameAs:(NSString *)urlWasThen
    {

    // (note - at this point here the image may already be available
    // in cache.  if so, just display it. I have omitted that
    // code for simplicity here.)

    // so, right here, "possibly load with delay" the image
    // exactly as shown in the code above .....

    dispatch_after_secs_on_main(0.4, ^
       ...etc....
       ...etc....
    }

同样,由于DLImageLoader,这一切都很容易实现,这是令人惊讶的。这是一个非常可靠的库。

类似的问题。。。(在Android中也是如此):)我有点搞不懂如何更新“UrlWasThen”?非常感谢。虽然代码看起来像imUrl,但URLWASSEN应该在cellForItemAtIndexPath运行时更新。然而,块中的URLWASSEN似乎不会受到这种更新的影响。。。。。我相信这是我最困惑的部分。非常感谢。好的。别忘了,手机经常被“更换”。比如,有时“每秒多次”。因此,self.currentImage总是“它现在应该是什么”。这意味着LoadImageInAsecifitsSameas经常被调用,每秒调用多次。但是在每次调用LoadImageInAsecifitsSameas时,urlWasThen将是“几秒钟前”的值,当时进行了特定的调用。只有当它仍然是“那个价值”时,它才会做任何事情——其他明智的人什么也不做。干杯我发现如果在[self imageIsNow]加载缓存图像而不是将其设置为零,效果会更好,这样可以避免在[collectionView重新加载数据]时出现闪烁图像。嗨,Ken,听起来不错。也请注意我的评论,它说“注意——在这一点上……”