TableView没有显示uiimage

时间:2015-03-04 14:39:29

标签: objective-c uitableview twitter cell

我有一个显示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;
}

1 个答案:

答案 0 :(得分:1)

基本问题是标准表格视图单元格的标准imageView属性将根据cellForRowAtIndexPath完成时存在的图像自动调整大小。但是,由于当你第一次出示桌子时还没有图像,所以单元格的布局就好像没有图像一样。当您异步更新图像视图image时,它不会调整图像视图的大小。

有几种方法可以解决这个问题:

  1. 不要使用imageView提供的默认UITableViewCell,而是定义自己的自定义单元子类,IBOutlet为自己的UIImageView属性。确保此UIImageView具有固定的布局(即,它不使用从基础图像派生的内在大小)。

    如果这样做,您可以异步更新自定义image插座的UIImageView属性,并且因为布局不依赖于图像的存在,所以{{1}的任何异步更新1}}应该正确显示。

  2. 收到图片后,请不要只设置图片视图的image属性,而是使用{{重新加载与image相关联的整行。 1}}。

    如果这样做,假设您正确地从缓存中检索图像,则会正确布置单元格,并在NSIndexPath完成之前执行此操作。

    注意,如果你这样做,你将需要修复你的reloadRowsAtIndexPaths以实际尝试从缓存中首先检索图像(并在发送到后台队列之前从主队列执行此操作),否则你将陷入无休止的循环。


  3. 话虽如此,这里有更深层次的问题。

    1. 如上所述,您正在缓存图片,但在检索图片时从不使用缓存。

    2. 您正在异步更新图像视图。

      • 在启动新的异步提取之前,您应该初始化cellForRowAtIndexPath的{​​{1}}属性,否则当重用某个单元格时,您会在那里看到旧图像,直到检索新图像。

      • 如果在调用getImageFromUrl和异步请求完成之间的间隔期间重用该单元怎么办?您将更新错误单元格的图像视图。 (当通过慢速连接执行此操作时,此问题将更加明显。使用网络链接调节器运行代码以模拟慢速连接,并且您将看到我正在描述的问题。)

      • 如果用户快速向下滚动到表格中的第100行怎么办?对可见单元的网络请求将在其他99个图像请求后面进行积压。你甚至可以在慢速连接上获得超时错误。

    3. image中有许多战术小问题。

      • 为什么要从全局队列同步调度到另一个全局队列?这是不必要的。为什么同步调度UI更新到主线程?那效率很低。

      • 为什么将UIImageView定义为块外的getImageFromUrl;只需在块中定义它,您就不需要getImageFromUrl限定符。

      • 如果您没有从网络请求中收到有效的imageData该怎么办(例如,您收到了404错误消息);现有代码会崩溃。服务器可能提供的各种响应都不是有效的图像,您必须确定这种情况(即确保不仅__block您收到的不是__block,而且您从中创建的UIImage也不是NSData

    4. 我可能会使用nil而不是UIImage来缓存。此外,无论您使用nil还是NSCache,都需要确保响应内存压力事件并在需要时清空该缓存。

    5. 我们可以解决所有这些个别问题,但要解决所有问题,这是一项非常重要的工作。因此,我建议您考虑SDWebImageAFNetworkingNSMutableDictionary类别。他们负责处理大多数这些问题以及其他问题。它会让你的生活变得更加轻松。