Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/cocoa/3.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
Objective c 在NSTableView上加载许多图像时速度缓慢_Objective C_Cocoa - Fatal编程技术网

Objective c 在NSTableView上加载许多图像时速度缓慢

Objective c 在NSTableView上加载许多图像时速度缓慢,objective-c,cocoa,Objective C,Cocoa,有一个“基于视图”的NSTableView,文本输入“URL”以输入图像URL和“添加按钮”。用Objective-C编码 - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { return [self.items count]; } - (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableCo

有一个“基于视图”的NSTableView,文本输入“URL”以输入图像URL和“添加按钮”。用Objective-C编码

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
    return [self.items count];
}

- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    Item *item = [self.items objectAtIndex:row];
    NSString *identifier = [tableColumn identifier];

    NSView *result = nil;

    if ([identifier isEqualToString:@"thumb"]) {
        NSImageView *thisCell = [tableView makeViewWithIdentifier:identifier owner:self];
        NSString *url = [item valueForKey:@"url"];
        NSImage *image = [[NSImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:url]]];
        [thisCell setImage:image];
        result = thisCell;
    } else {
        NSString *value = [item valueForKey:identifier];
        NSTableCellView *thisCell = [tableView makeViewWithIdentifier:identifier owner:self];
        thisCell.textField.stringValue = value;
        result = thisCell;
    }

    return result;
}

-(IBAction)add:(id)sender {
    Item *testItem = [[Item alloc] init];
    testItem.title = @"Some Title";
    testItem.url = @"http://filmdash.files.wordpress.com/2010/02/sayhello_logo_300dpi_rgb.jpg";
    [self.items addObject:testItem];
    [self.tableView reloadData];
}
一切正常,您填写URL,按add按钮,将包含URL和图像预览列的新行添加到加载的表视图中

正在从提供的URL同步加载图像

当许多行被添加到表视图中,并且您添加了下一条记录时,就会出现问题。每次刷新表视图时,它都会再次从其URL加载所有图像

是否有标准方法避免在每次表视图刷新时再次加载所有图像

或者更好地避免在每次表视图刷新时再次加载所有图像,并在第一次异步加载它们


谢谢

有几种方法可以做到这一点

更简单的版本是只重新加载tableView的新索引,而不是调用
reloadData
。通过这样做,其他细胞保持不变

另一个解决方案稍微复杂一些,但更好。创建一个图像字典。用一些独特的东西作为钥匙。我认为您的
项标识符可能会起作用。下载图像后,用键将其保存到dict。重新加载单元格时,请检查图像是否可用,否则请下载。
我在这里看到了几个优势:

  • 您有一本很好的词典,可以随时保存(
    NSKeyedArchiver
    )供以后使用
  • 无需重新下载-不仅
    重新加载数据
    加载CellView。如果一个单元格离开屏幕,它将被卸载并在可见时重新加载。这样,您可以再次平滑滚动
  • 离题:
    我认为
    NSURLConnection
    dataWithContentsOfURL:
    工作得更好,因为它是异步工作的,不会冻结UI。。照片可能会立即加载到您的计算机上,但互联网连接速度慢的用户将获得糟糕的用户体验,因为他们在几秒钟内无法使用您的应用程序。您还可以添加一个
    nsprogressingindicator
    以指示图像正在加载

    这是对我第一次回答中出现的问题的单独回答。这是关于使用
    NSURLSession
    加载图像的

    您不需要委托方法。我现在就试过了,效果很好:执行常规设置:将progressIndicator添加到tableCell中,并将其连接到自定义tableCell类,以便您可以在代码中访问它。将NSPROGESSINDICATOR样式设置为“旋转”和“中间”。不要忘记取消选中“停止时显示”

    您可以在
    -(NSView*)tableView:(NSTableView*)tableView viewForTableColumn:(NSTableColumn*)tableColumn行:(NSInteger)行中加载图像委托方法。代码如下所示:

    - (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
        if ([tableColumn.identifier isEqualToString:@"MainCell"]) {
            TableCellView* tableCell = [tableView makeViewWithIdentifier:@"cellID" owner:self];
    
            //Set up session
            NSURLSession* session = [NSURLSession sharedSession];
            NSURLSessionDownloadTask* task = [session downloadTaskWithURL:[NSURL URLWithString:@"YOUR URL"] completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
                if (!location) { ** error ** } else {
                    NSData* downloadData = [NSData dataWithContentsOfURL:location];
                    NSImage* image = [[NSImage alloc] initWithData:downloadData];
                    //Check if actual image was created..
                    tableCell.imageView.image = image;
    
                    //Stop the spinner
                    [tableCell.progressIndicator stopAnimation:nil];
                    }    
                }];
    
                //Start the spinner
                [tableCell.progressIndicator startAnimation:nil];
    
                //Start the task
                [task resume];
    
                //Other cell setup
                [tableCell.textField setStringValue: "Foo Bar"];
                return tableCell;
        }
        return nil;
    }
    
    结果看起来像这样。。

    作为Max关于跟踪进度的好答案的替代方案,我们了解了如何使用NSURLConnectionDataDelegate跟踪下载进度。初始化每个列的连接委托并在连接委托的每个实例上设置单元格,然后当连接委托执行其任务时,它有权访问该单元格以更新其中的组件

    TableViewDelegate.m

    - (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
        Item *item = [self.items objectAtIndex:row];
        NSString *identifier = [tableColumn identifier];
    
        NSView *result = nil;
    
        if ([identifier isEqualToString:@"thumb"]) {
            NSImageView *thisCell = [tableView makeViewWithIdentifier:identifier owner:self];
            NSURL *urlReal = [NSURL URLWithString:item.url];
    
            NSURLRequest *request = [NSURLRequest requestWithURL:urlReal];
    
            ThumbWithProgressDelegate *thumbConnectionDelegate = [[ThumbWithProgressDelegate alloc]init];
            thumbConnectionDelegate.thisCell = thisCell;  // set cell in delegate
    
            NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:thumbConnectionDelegate];
            result = thisCell;
        } else {
            NSString *value = [item valueForKey:identifier];
            NSTableCellView *thisCell = [tableView makeViewWithIdentifier:identifier owner:self];
            thisCell.textField.stringValue = value;
            result = thisCell;
        }
    
        return result;
    }
    
    ThumbWithProgressDelegate.h

    #import <Foundation/Foundation.h>
    #import <AppKit/AppKit.h>
    
    @interface ThumbWithProgressDelegate : NSObject<NSURLConnectionDataDelegate>
    
    @property (nonatomic) NSMutableData *imageData;
    @property (nonatomic) NSUInteger totalBytes;
    @property (nonatomic) NSUInteger receivedBytes;
    
    @property (nonatomic) NSImage *image;
    @property (weak,nonatomic) NSImageView *thisCell;
    
    @end
    
    #import "ThumbWithProgressDelegate.h"
    
    @implementation ThumbWithProgressDelegate
    
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    {
        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
        NSDictionary *dict = httpResponse.allHeaderFields;
        NSString *lengthString = [dict valueForKey:@"Content-Length"];
        NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
        NSNumber *length = [formatter numberFromString:lengthString];
        self.totalBytes = length.unsignedIntegerValue;
    
        self.imageData = [[NSMutableData alloc] initWithCapacity:self.totalBytes];
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    {
        [self.imageData appendData:data];
        self.receivedBytes += data.length;
    
        // Actual progress is self.receivedBytes / self.totalBytes
    }
    
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
        NSImage *image = [[NSImage alloc] initWithData:self.imageData];
        [self.thisCell setImage:image];
    }
    
    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    {
        //handle error
    }
    
    @end
    

    进度条是一个好主意,也是一个连接。我创建了NSURLConnectionDataDelegate以获取图像数据并跟踪内部进度,但是如何将连接委托内部的进度值/最终图像设置为特定的表视图单元格?对于这样的问题,很抱歉,谷歌搜索了答案,但还没有成功…只是不确定如何从单独的类连接委托内部访问NSImageView*此单元格,它是表视图的一部分。添加了另一个答案以显示如何加载图像。很好,感谢log Max。我还发布了一个不使用块的替代解决方案。我还不确定哪一个更合适。