我有一个显示Twitter帐户Feed的应用。所以我有FeedView内容的ImageView,textLabel和detailLabel。问题是当加载所有数据时,uiimage不会出现。当我单击单元格或向上滚动时,会设置图像。这是我的一些代码。
-(void)getImageFromUrl:(NSString*)imageUrl asynchronouslyForImageView:(UIImageView*)imageView andKey:(NSString*)key{
dispatch_async(dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *url = [NSURL URLWithString:imageUrl];
__block NSData *imageData;
dispatch_sync(dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
imageData =[NSData dataWithContentsOfURL:url];
if(imageData){
[self.imagesDictionary setObject:[UIImage imageWithData:imageData] forKey:key];
dispatch_sync(dispatch_get_main_queue(), ^{
imageView.image = self.imagesDictionary[key];
});
}
});
});
}
- (void)refreshTwitterHomeFeedWithCompletion {
// Request access to the Twitter accounts
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[accountStore requestAccessToAccountsWithType:accountType options:nil completion:^(BOOL granted, NSError *error){
if (granted) {
NSArray *accounts = [accountStore accountsWithAccountType:accountType];
// Check if the users has setup at least one Twitter account
if (accounts.count > 0)
{
ACAccount *twitterAccount = [accounts objectAtIndex:0];
NSLog(@"request.account ...%@",twitterAccount.username);
NSURL* url = [NSURL URLWithString:@"https://api.twitter.com/1.1/statuses/home_timeline.json"];
NSDictionary* params = @{@"count" : @"50", @"screen_name" : twitterAccount.username};
SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeTwitter
requestMethod:SLRequestMethodGET
URL:url parameters:params];
request.account = twitterAccount;
[request performRequestWithHandler:^(NSData *responseData,
NSHTTPURLResponse *urlResponse, NSError *error) {
if (error)
{
NSString* errorMessage = [NSString stringWithFormat:@"There was an error reading your Twitter feed. %@",
[error localizedDescription]];
NSLog(@"%@",errorMessage);
}
else
{
NSError *jsonError;
NSArray *responseJSON = [NSJSONSerialization
JSONObjectWithData:responseData
options:NSJSONReadingAllowFragments
error:&jsonError];
if (jsonError)
{
NSString* errorMessage = [NSString stringWithFormat:@"There was an error reading your Twitter feed. %@",
[jsonError localizedDescription]];
NSLog(@"%@",errorMessage);
}
else
{
NSLog(@"Home responseJSON..%@",(NSDictionary*)responseJSON.description);
dispatch_async(dispatch_get_main_queue(), ^{
[self reloadData:responseJSON];
});
}
}
}];
}
}
}];
}
-(void)reloadData:(NSArray*)jsonResponse
{
self.tweets = jsonResponse;
[self.tableView reloadData];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return self.tweets.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
SNTwitterCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(!cell)
{
cell = [[SNTwitterCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
NSDictionary *tweetDictionary = self.tweets[indexPath.row];
NSDictionary *user = tweetDictionary[@"user"];
NSString *userName = user[@"name"];
NSString *tweetContaint = tweetDictionary[@"text"];
NSString* imageUrl = [user objectForKey:@"profile_image_url"];
[self getImageFromUrl:imageUrl asynchronouslyForImageView:cell.imageView andKey:userName];
cell.profileImage.image = [UIImage imageNamed:@"images.png"];
NSArray *days = [NSArray arrayWithObjects:@"Mon ", @"Tue ", @"Wed ", @"Thu ", @"Fri ", @"Sat ", @"Sun ", nil];
NSArray *calendarMonths = [NSArray arrayWithObjects:@"Jan", @"Feb", @"Mar",@"Apr", @"May", @"Jun", @"Jul", @"Aug", @"Sep", @"Oct", @"Nov", @"Dec", nil];
NSString *dateStr = [tweetDictionary objectForKey:@"created_at"];
for (NSString *day in days) {
if ([dateStr rangeOfString:day].location == 0) {
dateStr = [dateStr stringByReplacingOccurrencesOfString:day withString:@""];
break;
}
}
NSArray *dateArray = [dateStr componentsSeparatedByString:@" "];
NSArray *hourArray = [[dateArray objectAtIndex:2] componentsSeparatedByString:@":"];
NSDateComponents *components = [[NSDateComponents alloc] init];
NSString *aux = [dateArray objectAtIndex:0];
int month = 0;
for (NSString *m in calendarMonths) {
month++;
if ([m isEqualToString:aux]) {
break;
}
}
components.month = month;
components.day = [[dateArray objectAtIndex:1] intValue];
components.hour = [[hourArray objectAtIndex:0] intValue];
components.minute = [[hourArray objectAtIndex:1] intValue];
components.second = [[hourArray objectAtIndex:2] intValue];
components.year = [[dateArray objectAtIndex:4] intValue];
NSTimeZone *gmt = [NSTimeZone timeZoneForSecondsFromGMT:2];
[components setTimeZone:gmt];
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
[calendar setTimeZone:[NSTimeZone systemTimeZone]];
NSDate *date = [calendar dateFromComponents:components];
NSString *tweetDate = [self getTimeAsString:date];
NSString *tweetValues = [NSString stringWithFormat:@"%@ :%@",userName,tweetDate];
cell.textLabel.text = [NSString stringWithFormat:@"%@",tweetValues];
cell.detailTextLabel.text = [NSString stringWithFormat:@"%@",tweetContaint];
[cell.detailTextLabel setFont:[UIFont fontWithName:@"Helvetica" size:20]];
return cell;
}
- (NSString*)getTimeAsString:(NSDate *)lastDate {
NSTimeInterval dateDiff = [[NSDate date] timeIntervalSinceDate:lastDate];
int nrSeconds = dateDiff;//components.second;
int nrMinutes = nrSeconds / 60;
int nrHours = nrSeconds / 3600;
int nrDays = dateDiff / 86400; //components.day;
NSString *time;
if (nrDays > 5){
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateStyle:NSDateFormatterShortStyle];
[dateFormat setTimeStyle:NSDateFormatterNoStyle];
time = [NSString stringWithFormat:@"%@", [dateFormat stringFromDate:lastDate]];
} else {
// days=1-5
if (nrDays > 0) {
if (nrDays == 1) {
time = @"1 day ago";
} else {
time = [NSString stringWithFormat:@"%d days ago", nrDays];
}
} else {
if (nrHours == 0) {
if (nrMinutes < 2) {
time = @"just now";
} else {
time = [NSString stringWithFormat:@"%d minutes ago", nrMinutes];
}
} else { // days=0 hours!=0
if (nrHours == 1) {
time = @"1 hour ago";
} else {
time = [NSString stringWithFormat:@"%d hours ago", nrHours];
}
}
}
}
return [NSString stringWithFormat:NSLocalizedString(@"%@", @"label"), time];
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 100;
}
答案 0 :(得分:1)
基本问题是标准表格视图单元格的标准imageView
属性将根据cellForRowAtIndexPath
完成时存在的图像自动调整大小。但是,由于当你第一次出示桌子时还没有图像,所以单元格的布局就好像没有图像一样。当您异步更新图像视图image
时,它不会调整图像视图的大小。
有几种方法可以解决这个问题:
不要使用imageView
提供的默认UITableViewCell
,而是定义自己的自定义单元子类,IBOutlet
为自己的UIImageView
属性。确保此UIImageView
具有固定的布局(即,它不使用从基础图像派生的内在大小)。
如果这样做,您可以异步更新自定义image
插座的UIImageView
属性,并且因为布局不依赖于图像的存在,所以{{1}的任何异步更新1}}应该正确显示。
收到图片后,请不要只设置图片视图的image
属性,而是使用{{重新加载与image
相关联的整行。 1}}。
如果这样做,假设您正确地从缓存中检索图像,则会正确布置单元格,并在NSIndexPath
完成之前执行此操作。
注意,如果你这样做,你将需要修复你的reloadRowsAtIndexPaths
以实际尝试从缓存中首先检索图像(并在发送到后台队列之前从主队列执行此操作),否则你将陷入无休止的循环。
话虽如此,这里有更深层次的问题。
如上所述,您正在缓存图片,但在检索图片时从不使用缓存。
您正在异步更新图像视图。
在启动新的异步提取之前,您应该初始化cellForRowAtIndexPath
的{{1}}属性,否则当重用某个单元格时,您会在那里看到旧图像,直到检索新图像。
如果在调用getImageFromUrl
和异步请求完成之间的间隔期间重用该单元怎么办?您将更新错误单元格的图像视图。 (当通过慢速连接执行此操作时,此问题将更加明显。使用网络链接调节器运行代码以模拟慢速连接,并且您将看到我正在描述的问题。)
如果用户快速向下滚动到表格中的第100行怎么办?对可见单元的网络请求将在其他99个图像请求后面进行积压。你甚至可以在慢速连接上获得超时错误。
image
中有许多战术小问题。
为什么要从全局队列同步调度到另一个全局队列?这是不必要的。为什么同步调度UI更新到主线程?那效率很低。
为什么将UIImageView
定义为块外的getImageFromUrl
;只需在块中定义它,您就不需要getImageFromUrl
限定符。
如果您没有从网络请求中收到有效的imageData
该怎么办(例如,您收到了404错误消息);现有代码会崩溃。服务器可能提供的各种响应都不是有效的图像,您必须确定这种情况(即确保不仅__block
您收到的不是__block
,而且您从中创建的UIImage
也不是NSData
。
我可能会使用nil
而不是UIImage
来缓存。此外,无论您使用nil
还是NSCache
,都需要确保响应内存压力事件并在需要时清空该缓存。
我们可以解决所有这些个别问题,但要解决所有问题,这是一项非常重要的工作。因此,我建议您考虑SDWebImage或AFNetworking的NSMutableDictionary
类别。他们负责处理大多数这些问题以及其他问题。它会让你的生活变得更加轻松。