Objective c 在NSTableView上加载许多图像时速度缓慢
有一个“基于视图”的NSTableView,文本输入“URL”以输入图像URL和“添加按钮”。用Objective-C编码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
- (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。我还发布了一个不使用块的替代解决方案。我还不确定哪一个更合适。