【问题标题】:Using UICollectionReusableView to group Cells使用 UICollectionReusableView 对 Cells 进行分组
【发布时间】:2013-11-29 04:46:33
【问题描述】:

我无法将我的旧代码更新为使用 AFNetworking 进行异步调用的新代码,该代码将同步 JSOn 调用。

在我的旧代码中,我使用日期字符串(“release_date”)将单元格与 UICollectionReusableView 进行分组,所有这些都是在 viewDidLoad 中完成的。现在使用 AFNetworking,我将所有内容都移出 viewDidLoad,所以我一直在努力弄清楚如何将旧代码与新代码合并。

这是我必须用 AFNetworking 解析我的 JSON 的新代码:

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.upcomingReleases = [[NSMutableArray alloc] init];

    [self makeReleasesRequests];

    [self.collectionView registerClass:[ReleaseCell class] forCellWithReuseIdentifier:@"ReleaseCell"];
}

-(void)makeReleasesRequests  //AFNetworking Call
{
    NSURL *url = [NSURL URLWithString:@"http://www.soleresource.com/upcoming.json"];

    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];

    operation.responseSerializer = [AFJSONResponseSerializer serializer];

    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {

        NSLog(@"@");

        self.upcomingReleases = [responseObject objectForKey:@"upcoming_releases"];

        [self.collectionView reloadData];

    } failure:nil];

    [operation start];
}

在我开始使用 AFNetworking 进行 JSON 调用和“分组”我的单元之前,我在 viewDidLoad 中的代码:

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSURL *upcomingReleaseURL = [NSURL URLWithString:@"http://www.soleresource.com/upcoming.json"];

    NSData *jsonData = [NSData dataWithContentsOfURL:upcomingReleaseURL];

    NSError *error = nil;

    NSDictionary *dataDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];

    NSArray *upcomingReleasesArray = [dataDictionary objectForKey:@"upcoming_releases"];

    //This is the dateFormatter we'll need to parse the release dates
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
    NSTimeZone *est = [NSTimeZone timeZoneWithAbbreviation:@"EST"];
    [dateFormatter setTimeZone:est];
    [dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]]; //A bit of an overkill to avoid bugs on different locales

    //Temp array where we'll store the unsorted bucket dates
    NSMutableArray *unsortedReleaseWeek = [[NSMutableArray alloc] init];
    NSMutableDictionary *tmpDict = [[NSMutableDictionary alloc] init];

    for (NSDictionary *upcomingReleaseDictionary in upcomingReleasesArray) {

        //We find the release date from the string
        NSDate *releaseDate = [dateFormatter dateFromString:[upcomingReleaseDictionary objectForKey:@"release_date"]];

        //We create a new date that ignores everything that is not the actual day (ignoring stuff like the time of the day)
        NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
        NSDateComponents *components =
        [gregorian components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:releaseDate];

        //This will represent our releases "bucket"
        NSDate *bucket = [gregorian dateFromComponents:components];

        //We get the existing objects in the bucket and update it with the latest addition
        NSMutableArray *releasesInBucket = [tmpDict objectForKey:bucket];
        if (!releasesInBucket){
            releasesInBucket = [NSMutableArray array];
            [unsortedReleaseWeek addObject:bucket];
        }

        UpcomingRelease *upcomingRelease = [UpcomingRelease upcomingReleaseWithName:[upcomingReleaseDictionary objectForKey:@"release_name"]];
        upcomingRelease.release_date = [upcomingReleaseDictionary objectForKey:@"release_date"];
        upcomingRelease.release_date = [upcomingReleaseDictionary objectForKey:@"release_date"];
        [releasesInBucket addObject:upcomingRelease];
        [tmpDict setObject:releasesInBucket forKey:bucket];
    }

    [unsortedReleaseWeek sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        NSDate* date1 = obj1;
        NSDate* date2 = obj2;
        //This will sort the dates in ascending order (earlier dates first)
        return [date1 compare:date2];
        //Use [date2 compare:date1] if you want an descending order
    }];

    self.releaseWeekDictionary = [NSDictionary dictionaryWithDictionary:tmpDict];
    self.releaseWeek = [NSArray arrayWithArray:unsortedReleaseWeek];

    [self.collectionView registerClass:[ReleaseCell class] forCellWithReuseIdentifier:@"ReleaseCell"];
}

CollectionViewCell

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *identifier = @"Cell";

    ReleaseCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];

    // Part of my new code AFNetworking
    NSDictionary *upcomingReleaseDictionary = [self.upcomingReleases objectAtIndex:indexPath.row];
    //

    // I had this in my old code
    UpcomingRelease *upcomingRelease = [self.releaseWeekDictionary objectForKey:self.releaseWeek[indexPath.section]][indexPath.row];
    //
    cell.release_name.text = upcomingRelease.release_name;

    return cell;
}

这是剩下的:

-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return [self.releaseWeek count];
}

-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return [[self.releaseWeekDictionary objectForKey:self.releaseWeek[section]] count];
}

-(UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
    ReleaseWeek *releaseWeek = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"releaseWeek" forIndexPath:indexPath];

    //We tell the formatter to produce a date in the format "Name-of-the-month day"
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"MMMM dd"];
    NSTimeZone *est = [NSTimeZone timeZoneWithAbbreviation:@"EST"];
    [dateFormatter setTimeZone:est];
    [dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]];

    //We read the bucket date and feed it to the date formatter
    NSDate *releaseWeekDate = self.releaseWeek[indexPath.section];

    releaseWeek.releaseDate.text = [[dateFormatter stringFromDate:releaseWeekDate] uppercaseString];
    return releaseWeek;
}

我基本上是想弄清楚如何将分组我的单元格的代码作为日期字符串并将其与我的新代码集成。

谢谢。

【问题讨论】:

    标签: ios objective-c json afnetworking uicollectionviewcell


    【解决方案1】:

    你比你想象的更接近。

    只需将您以前在 viewDidLoad 中执行的所有操作:(从分配 jsonData 到注册集合视图类之间的所有操作)放入 AFNetworking 调用的回调块(其中 jsonData 现在称为 responseObject)。

    在回调块结束时,只需调用 [self.collectionView reloadData],您的集合视图将自行重新加载(即为每个项目调用 numberOfItems 和 cellForItemAtIndexPath)。

    在返回部分和项目数量的 UICollectionViewDataSource 方法中,如果保存模型的属性为 nil 或为空,则只需返回 0。

    -(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
    {
        // Correctly returns 0 if nil or empty.
        return [self.releaseWeek count];
    }
    
    -(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
    {
        if(!self.releaseWeekDictionary[section] || !self.releaseWeek[section]) {
            return 0;
        }else{
             return [self.releaseWeekDictionary[self.releaseWeek[section]] count];
        }
    }
    

    下面应该是完成块中的肉汁代码。

    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    
            NSLog(@"@");
    
            NSArray *upcomingReleasesArray = [dataDictionary objectForKey:@"upcoming_releases"];
    
            //This is the dateFormatter we'll need to parse the release dates
            NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
            [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
            NSTimeZone *est = [NSTimeZone timeZoneWithAbbreviation:@"EST"];
            [dateFormatter setTimeZone:est];
            [dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]]; //A bit of an overkill to avoid bugs on different locales
    
            //Temp array where we'll store the unsorted bucket dates
            NSMutableArray *unsortedReleaseWeek = [[NSMutableArray alloc] init];
            NSMutableDictionary *tmpDict = [[NSMutableDictionary alloc] init];
    
            for (NSDictionary *upcomingReleaseDictionary in upcomingReleasesArray) {
    
                //We find the release date from the string
                NSDate *releaseDate = [dateFormatter dateFromString:[upcomingReleaseDictionary objectForKey:@"release_date"]];
    
                //We create a new date that ignores everything that is not the actual day (ignoring stuff like the time of the day)
                NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
                NSDateComponents *components =
                [gregorian components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:releaseDate];
    
                //This will represent our releases "bucket"
                NSDate *bucket = [gregorian dateFromComponents:components];
    
                //We get the existing objects in the bucket and update it with the latest addition
                NSMutableArray *releasesInBucket = [tmpDict objectForKey:bucket];
                if (!releasesInBucket){
                    releasesInBucket = [NSMutableArray array];
                    [unsortedReleaseWeek addObject:bucket];
                }
    
                UpcomingRelease *upcomingRelease = [UpcomingRelease upcomingReleaseWithName:[upcomingReleaseDictionary objectForKey:@"release_name"]];
                upcomingRelease.release_date = [upcomingReleaseDictionary objectForKey:@"release_date"];
                upcomingRelease.release_date = [upcomingReleaseDictionary objectForKey:@"release_date"];
                [releasesInBucket addObject:upcomingRelease];
                [tmpDict setObject:releasesInBucket forKey:bucket];
            }
    
            [unsortedReleaseWeek sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
                NSDate* date1 = obj1;
                NSDate* date2 = obj2;
                //This will sort the dates in ascending order (earlier dates first)
                return [date1 compare:date2];
                //Use [date2 compare:date1] if you want an descending order
            }];
    
            self.releaseWeekDictionary = [NSDictionary dictionaryWithDictionary:tmpDict];
            self.releaseWeek = [NSArray arrayWithArray:unsortedReleaseWeek];
    
            [self.collectionView reloadData];
    
        } failure:nil];
    

    编辑:在您的代码中,您不再为即将发布的版本分配任何内容(注释代码行 91-102 块),并且您在引用数组中不存在的索引时崩溃了。修复很简单:

    109: self.upcomingReleases = [dataDictionary objectForKey:@"upcoming_releases"];
    
    122: for (NSDictionary *upcomingReleaseDictionary in self.upcomingReleases) {
    

    【讨论】:

    • 应用程序加载但随后崩溃,错误指向我的 cellForItemAtIndexPath: NSDictionary *upcomingReleaseDictionary = [self.upcomingReleases objectAtIndex:indexPath.row];我的整个项目都在 github 上,以防你想查看我的整个代码:github.com/chrisbedoya/solearchives。感谢您的帮助。
    • 1.) git add 。 2.) git push origin master :) 除了一个空项目,你什么都没有。 @ChrisBedoya
    • 现在你可以在 Github 上看到我的应用了(查看“Releases”文件夹下的“UpcomingReleasesViewController”。很抱歉给您带来不便,再次感谢。
    • @ChrisBedoya 查看编辑。通过打开异常断点,您可能更容易找到这个确切的问题。看到这个问题:stackoverflow.com/questions/4961770/…
    • 现在我的 ReusableViews 显示正确,日期正确,单元数正确。但是由于某种原因,每次有一个新的 ReusableView 时,单元格都会继续回到第一个。如果一个视图中有两个单元格,我的应用程序会显示前两个单元格(发布日期最早的单元格)。在下一个可重复使用的视图中,它不再显示属于该日期的单元格,而是再次显示日期最早的单元格。
    【解决方案2】:

    想想 MVC。将您的模型(来自网络的东西)分离到一个单独的类中,该类执行网络操作,并将事物分组到数组和字典中。您的视图控制器应该简单地观察模型。您可以使用委托或(我最喜欢的)KVO 来了解模型何时更新了可用数据。然后你只需更新你的收藏视图。您的视图控制器应该只是模型和视图之间的接口。如果你以这种方式将事物分开,你会发现它更自然,而且你不会与系统作斗争。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-21
      • 2011-09-14
      • 2021-05-04
      • 2015-10-19
      相关资源
      最近更新 更多