加快表在 Swift 加载 JSON

发布时间: 2017/3/5 13:42:10
注意事项: 本文中文内容可能为机器翻译,如要查看英文原文请点击上面连接.

我正在从我的数据库 (一些产品的形象和他们的名字) 开发 JSON 表的信息。一切都很好,但它是非常缓慢的滚动 (向上或向下) 时。我已经做了大量关于这个主题的研究,但我用过他们的代码和仍然不知如何将图像存储在缓存中以加快表。

这里是我的代码︰

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! BuscarCellTableViewCell

    if searchController.active {
        cell.nombre.text = searchResults[indexPath.row].nombre

        cell.marca.text = searchResults[indexPath.row].marca

        if let url = NSURL(string: searchResults[indexPath.row].imagen) {
            if let data = NSData(contentsOfURL: url) {
                cell.imagen.image = UIImage(data: data)
            }
        }
    }
    else {
        cell.nombre.text = productos[indexPath.row].nombre

        cell.marca.text = productos[indexPath.row].marca

        if let url = NSURL(string: productos[indexPath.row].imagen) {
            if let data = NSData(contentsOfURL: url) {
                cell.imagen.image = UIImage(data: data)
            }
        }
    }

    return cell
}

override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {

    // Define the initial state (Before the animation)
    cell.alpha = 0.25

    // Define the final state (After the animation)
    UIView.animateWithDuration(1.0, animations: { cell.alpha = 1 })
}

func getLatestLoans() {
    let request = NSURLRequest(URL: NSURL(string: LoadURL)!)
    let urlSession = NSURLSession.sharedSession()
    let task = urlSession.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in

        let res = response as! NSHTTPURLResponse!
        var err: NSError?

        if error != nil {
            println(error.localizedDescription)
        }

        // Parse JSON data
        self.productos = self.parseJsonData(data)

        // Reload table view
        dispatch_async(dispatch_get_main_queue(), {
            self.tableView.reloadData()
        })
    })

    task.resume()
}

func parseJsonData(data: NSData) -> [Producto] {
    var productos = [Producto]()
    var error:NSError?

    let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error) as? NSDictionary

    // Return nil if there is any error
    if error != nil {
        println(error?.localizedDescription)
    }

    // Parse JSON data
    let jsonProductos = jsonResult?["lista_productos"] as! [AnyObject]
    for jsonProducto in jsonProductos {

        let producto = Producto()
        producto.nombre = jsonProducto["nombre"] as! String
        producto.imagen = jsonProducto["imagen"] as! String

        productos.append(producto)
    }

    return productos
}

向下的"产品"从我的 JSON 文件滚动的时候,我可以如何加快我表?

提前谢谢,

问候。

解决方法 1:

cellForRowAtIndexPath:获取调用用于每个新或重用单元格呈现。你不应该在此方法中调用的服务器。这是你应该怎么做︰

  1. 你的数据对象"productos"填充后,调用一个新 fetchImages 就可以引发 NSOperations 中获取的图像的背景。
  2. 虽然正获取的图像,你可以在每个单元格图像视图中显示加载覆盖。
  3. 一次图像反应回来,删除加载覆盖并刷新该单元格。
  4. 缓存文件系统,以避免重新提取他们再次同时用户向上和向下滚动您的表格视图中的图像。

遵循这个,您应该看到所需的改善滚动行为的应用程序中。

编辑︰ 对 OP 请求︰

第 1 步︰期待你的来电低于 fetchImages 方法尽快从服务器加载模型数据。如果您正在加载本地数据然后调用 fetchImagesloadView :

- (void)fetchImages {
    NSMutableArray *anImageURLsList = [NSMutableArray new]; // Assuming this contains your image URL list

    if ([anImageURLsList count] > 0) {
        __weak MyController *aBlockSelf = self;

        [[MyImageFetchController sharedImageFetchController] addURLs:anImageURLsList withDescription:[self description] andCompletionBlock:^(NSMutableArray *iFailedURLs) {
            dispatch_async(dispatch_get_main_queue(), ^{[UIApplication sharedApplication].networkActivityIndicatorVisible = NO; });

            if (aBlockSelf) {
                [[MyImageFetchController sharedImageFetchController].objectTokens seValue:[NSString stringWithFormat:@"%d", NO] forKey:[aBlockSelf description]];
                [aBlockSelf performSelectorOnMainThread:@selector(refresh) withObject:nil waitUntilDone:YES];
            }
        }];

        [[MyImageFetchController sharedImageFetchController].objectTokens setValue:[NSString stringWithFormat:@"%d", YES] forKey:[self description]];
        [MyImageFetchController sharedImageFetchController].delegate = self;
        [[MyImageFetchController sharedImageFetchController] startOperations];

    dispatch_async(dispatch_get_main_queue(), ^{[UIApplication sharedApplication].networkActivityIndicatorVisible = YES; });
    }
}


- (void)refresh {
    [self.tableView reloadData];
}

第 2 步︰现在,让我们写 NSOperation 子类通过操作队列获取的图像。下面的代码覆盖只重要方面对此处的讨论。在这里,我已经还给委托调用图像缓存实现。

- (void)addURLs:(NSMutableArray *)iURLs withDescription:(NSString *)iObjectDescription andCompletionBlock:(ImageFetchCompletionBlock)iImageFetchCompletionBlock {
    self.urls = iURLs;
    [self.objectTokens removeAllObjects];
    self.imageFetchCompletionBlock = iImageFetchCompletionBlock;
    self.objectDescription = iObjectDescription;

    if (self.failedURLs) {
        [self.failedURLs removeAllObjects];
    }
}


- (void)urlFailed:(NSString *)iFailedURL {
    @synchronized(self) {
        if (iFailedURL) {
            [self.failedURLs addObject:iFailedURL];
        }
    }
}


- (void)startOperations {
    MyImageFetchOperation *anImageFetchOperation = nil;
    NSMutableArray *anOperationList = [[NSMutableArray alloc] initWithCapacity:self.urls.count];
    self.queue = [NSOperationQueue new];

    for (NSString *aURL in [self.urls copy]) {
        anImageFetchOperation = [[MyImageFetchOperation alloc] initWithImageURL:aURL delegate:self];
        [anOperationList addObject:anImageFetchOperation];
    }

    [self.queue setMaxConcurrentOperationCount:3];
    [self.queue addOperations:anOperationList waitUntilFinished:NO];
}


- (void)operationCompletedForURL:(NSString *)iImageURL {
    @synchronized(self) {
        [self.urls removeObject:iImageURL];

        if ([self.urls count] == 0) {
            if ([[self.objectTokens valueForKey:self.objectDescription] boolValue]) {
                self.imageFetchCompletionBlock([self.failedURLs mutableCopy]);
            } else {
                dispatch_async(dispatch_get_main_queue(), ^{[UIApplication sharedApplication].networkActivityIndicatorVisible = NO; });
            }

            if (self.failedURLs && [self.failedURLs count] > 0) {
                [self.failedURLs removeAllObjects];
            }

            self.queue = nil;
        }
    }
}


- (NSString *)cacheDirectoryForImages {
    NSString *anImageCacheDirectory = kImageCacheDirectoryKey; // Your image cache directory 

    if (self.delegate && [self.delegate respondsToSelector:@selector(cacheDirectoryForImages)]) {
        anImageCacheDirectory = [self.delegate cacheDirectoryForImages];
    }

    return anImageCacheDirectory;
}

第 3 步︰现在,让我们写 cellForRowAtIndexPath 。在这里,我使用自定义的单元格,实现 imageView,也期待着一个自定义加载覆盖。在这里,你可以把你加载覆盖。

- (UITableViewCell *)tableView:(UITableView *)iTableView cellForRowAtIndexPath:(NSIndexPath *)iIndexPath {
    NSString *cellType = @"defaultCell";
    MyCustomTableViewCell *cell = (MyCustomTableViewCell *)[iTableView dequeueReusableCellWithIdentifier:cellType];

    if (!cell) {
        cell = [[MyCustomTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:aCellType];
    }

    cell.textLabel.text = @“Dummy”; // Set your cell data

    NSString *anImageURL = self.productos[iIndexPath.row][@“image"];

    [cell.loadingOverlay removeView];
    cell.loadingOverlay = nil;

    // If image is present in Cache
    if (anImageURL && [anImageURL existsInCache]) {
        cell.imageView.image = [UIImage imageNamed:<Image from cache>];
    } else if ([[[MyImageFetchController sharedImageFetchController].objectTokens valueForKey:[self description]] boolValue]) {
        cell.imageView.image = [UIImage imageNamed:defaultImage];
        cell.loadingOverlay = [MyLoadingOverlay loadingOverlayInView:aCell.imageView];
    } else {
        cell.imageView.image = [UIImage imageNamed:defaultImage];
    }

    return cell;
}
赞助商