Ios 使用光标加载UICollectionView

Ios 使用光标加载UICollectionView,ios,objective-c,uicollectionview,cloudkit,Ios,Objective C,Uicollectionview,Cloudkit,我有一个应用程序,其中包含从CloudKit中提取的图像的collectionview。我有一个CKManager类,它执行所有与CK相关的方法。在viewcontroller中,我调用了CKManager中的一个方法来从CK中检索初始数据,这一切都非常有效。我使用的是CKQueryOperation,因此我可以在块中提取数据,尽管到目前为止,我只为测试设置了CKQueryOperation.resultsLimit=CKQueryOperationMaximumResults。因此,滚动col

我有一个应用程序,其中包含从CloudKit中提取的图像的collectionview。我有一个CKManager类,它执行所有与CK相关的方法。在viewcontroller中,我调用了CKManager中的一个方法来从CK中检索初始数据,这一切都非常有效。我使用的是CKQueryOperation,因此我可以在块中提取数据,尽管到目前为止,我只为测试设置了CKQueryOperation.resultsLimit=CKQueryOperationMaximumResults。因此,滚动collectionview时,图像/单元格不会在滚动时“淡入”。我假设这是因为所有的数据都是在渲染单元格之前检索到的。目前大约有50条记录,加载速度相当快,但当我将结果限制为25条时,加载速度肯定会更快

我的问题是,我不完全理解如何使用游标来实现这一点,尽管我已经计划在代码中实现游标。我发现这一点我大部分都理解,但它是用Swift写的,它也不能回答我所有的问题。我根据Edwin在那篇文章中的回答修改了我的代码,但我确信我在Swift到OB-C的翻译中遗漏了一些东西

下面是我在CKManager类中调用的代码。我可以从日志中看到它工作正常,并且可以识别光标。我不明白的是如何/何时再次调用它以从该光标点获取下一个结果块?如果resultsLimit没有像最初那样设置为最大值,那么我将获得指定的结果量(20),而它不会检索剩余的结果。所以我不知道如何在光标离开的地方得到剩余的结果。我知道,因为我使用的是collectionview,所以每次获得下一个结果块时,我都需要更新节中的项目数

非常感谢

更新:更改了loadCloudKitDataWithCompletionHandler以添加对接受游标的新方法的调用-loadCloudKitDataFromCursor:withCompletionHandler:。唯一缺少的是找出ViewController中的何处来处理方法返回的结果,使用光标更新numberOfItemsInSection,然后重新加载CollectionView

从CKManager

- (void)loadCloudKitDataFromCursor:(CKQueryCursor *)cursor withCompletionHandler:(void (^)(NSArray *, CKQueryCursor *, NSError *))completionHandler {
    NSMutableArray *cursorResultSet = [[NSMutableArray alloc] init];
    __block NSArray *results;

    if (cursor) { // make sure we have a cursor to continue from
        NSLog(@"INFO: Preparing to load records from cursor...");
        CKQueryOperation *cursorOperation = [[CKQueryOperation alloc] initWithCursor:cursor];
        cursorOperation.resultsLimit = 20;

        // processes for each record returned
        cursorOperation.recordFetchedBlock = ^(CKRecord *record) {
            NSLog(@"RecordFetchBlock returned from cursor CID record: %@", record.recordID.recordName);
            [cursorResultSet addObject:record];
        };
        // query has completed
        cursorOperation.queryCompletionBlock = ^(CKQueryCursor *cursor, NSError *error) {
            results = [cursorResultSet copy];
            [cursorResultSet removeAllObjects]; // get rid of the temp results array
            completionHandler(results, cursor, error);
            if (cursor) {
                NSLog(@"INFO: Calling self to fetch more data from cursor point...");
                [self loadCloudKitDataFromCursor:cursor withCompletionHandler:^(NSArray *results, CKQueryCursor *cursor, NSError *error) {
                    results = [cursorResultSet copy];
                    [cursorResultSet removeAllObjects]; // get rid of the temp results array
                    completionHandler(results, cursor, error);
                }];
            }
        };

        [self.publicDatabase addOperation:cursorOperation];
    }

}

- (void)loadCloudKitDataFromCursor:(CKQueryCursor *)cursor withCompletionHandler:(void (^)(NSArray *, CKQueryCursor *, NSError *))completionHandler {
    NSMutableArray *cursorResultSet = [[NSMutableArray alloc] init];
    __block NSArray *results;

    if (cursor) { // make sure we have a cursor to continue from
        NSLog(@"INFO: Preparing to load records from cursor...");
        CKQueryOperation *cursorOperation = [[CKQueryOperation alloc] initWithCursor:cursor];
        cursorOperation.resultsLimit = 20;

        // processes for each record returned
        cursorOperation.recordFetchedBlock = ^(CKRecord *record) {
            NSLog(@"RecordFetchBlock returned from cursor CID record: %@", record.recordID.recordName);
            [cursorResultSet addObject:record];
        };
        // query has completed
        cursorOperation.queryCompletionBlock = ^(CKQueryCursor *cursor, NSError *error) {
            results = [cursorResultSet copy];
            [cursorResultSet removeAllObjects]; // get rid of the temp results array
            completionHandler(results, cursor, error);
            if (cursor) {
                NSLog(@"INFO: Calling self to fetch more data from cursor point...");
                [self loadCloudKitDataFromCursor:cursor withCompletionHandler:^(NSArray *results, CKQueryCursor *cursor, NSError *error) {
                    results = [cursorResultSet copy];
                    [cursorResultSet removeAllObjects]; // get rid of the temp results array
                    completionHandler(results, cursor, error);
                }];
            }
        };

        [self.publicDatabase addOperation:cursorOperation];
    }

}
从调用CKManager以获取数据的内部ViewController方法

dispatch_async(queue, ^{
        [self.ckManager loadCloudKitDataWithCompletionHandler:^(NSArray *results, CKQueryCursor *cursor, NSError *error) {
            if (!error) {
                if ([results count] > 0) {
                    self.numberOfItemsInSection = [results count];
                    NSLog(@"INFO: Success querying the cloud for %lu results!!!", (unsigned long)[results count]);
                    [self loadRecipeDataFromCloudKit]; // fetch the recipe images from CloudKit
                    // parse the records in the results array
                    for (CKRecord *record in results) {
                        ImageData *imageData = [[ImageData alloc] init];
                        CKAsset *imageAsset = record[IMAGE];
                        imageData.imageURL = imageAsset.fileURL;
                        imageData.imageName = record[IMAGE_NAME];
                        imageData.imageDescription = record[IMAGE_DESCRIPTION];
                        imageData.userID = record[USER_ID];
                        imageData.imageBelongsToCurrentUser = [record[IMAGE_BELONGS_TO_USER] boolValue];
                        imageData.recipe = [record[RECIPE] boolValue];
                        imageData.liked = [record[LIKED] boolValue]; // 0 = No, 1 = Yes
                        imageData.recordID = record.recordID.recordName;
                        // check to see if the recordID of the current CID is userActivityDictionary. If so, it's in the user's private
                        // data so set liked value = YES
                        if ([self.imageLoadManager lookupRecordIDInUserData:imageData.recordID]) {
                            imageData.liked = YES;
                        }
                        // add the CID object to the array
                        [self.imageLoadManager.imageDataArray addObject:imageData];

                        // cache the image with the string representation of the absolute URL as the cache key
                        if (imageData.imageURL) { // make sure there's an image URL to cache
                            if (self.imageCache) {
                                [self.imageCache storeImage:[UIImage imageWithContentsOfFile:imageData.imageURL.path] forKey:imageData.imageURL.absoluteString toDisk:YES];
                            }
                        } else {
                            NSLog(@"WARN: CID imageURL is nil...cannot cache.");
                            dispatch_async(dispatch_get_main_queue(), ^{
                                //[self alertWithTitle:@"Yikes!" andMessage:@"There was an error trying to load the images from the Cloud. Please try again."];
                                UIAlertView *reloadAlert = [[UIAlertView alloc] initWithTitle:YIKES_TITLE message:ERROR_LOADING_CK_DATA_MSG delegate:nil cancelButtonTitle:CANCEL_BUTTON otherButtonTitles:TRY_AGAIN_BUTTON, nil];
                                reloadAlert.delegate = self;
                                [reloadAlert show];
                            });
                        }
                    }
                    // update the UI on the main queue
                    dispatch_async(dispatch_get_main_queue(), ^{
                        // enable buttons once data has loaded...
                        self.userBarButtonItem.enabled = YES;
                        self.cameraBarButton.enabled = YES;
                        self.reloadBarButton.enabled = YES;

                        if (self.userBarButtonSelected) {
                            self.userBarButtonSelected = !self.userBarButtonSelected;
                            [self.userBarButtonItem setImage:[UIImage imageNamed:USER_MALE_25]];
                        }
                        [self updateUI]; // reload the collectionview after getting all the data from CK
                    });
                }
                // load the keys to be used for cache look up
                [self getCIDCacheKeys];
            } else {
                NSLog(@"Error: there was an error fetching cloud data... %@", error.localizedDescription);
                dispatch_async(dispatch_get_main_queue(), ^{
                    //[self alertWithTitle:@"Yikes!" andMessage:@"There was an error trying to load the images from the Cloud. Please try again."];
                    UIAlertView *reloadAlert = [[UIAlertView alloc] initWithTitle:YIKES_TITLE message:ERROR_LOADING_CK_DATA_MSG delegate:nil cancelButtonTitle:CANCEL_BUTTON otherButtonTitles:TRY_AGAIN_BUTTON, nil];
                    reloadAlert.delegate = self;
                    [reloadAlert show];
                });
            }
        }];
    }

你很接近。对于newOperation,还必须设置recordFetchedBlock和queryCompletionBlock。当您将新操作分配给该操作并执行该操作时,您将不会丢失引用,并且代码将继续运行。 替换一行[self.publicDatabase addOperation:newOperation];与:

newOperation.recordFetchedBlock = operation.recordFetchedBlock
newOperation.queryCompletionBlock = operation.queryCompletionBlock
operation = newOperation
[self.publicDatabase addOperation:operation];

再次感谢埃德温!你帮了我很多次!但是,您的代码有一个问题。“行动”从何而来?它以前在你的代码或我的代码中都没有定义。我想也许你指的是我上面代码中的ckQueryOperation,但那也不起作用。因为我对这一点还不太清楚,所以我自己无法正确地修改它。谢谢啊,对不起,复制粘贴错误。在你的情况下,它应该是ckqueryOperation,我想这可能就是你的意思,所以我试了一下,但没有成功。在newOperation.recordFetchedBlock=ckQueryOperation.recordFetchedBlock上获得警告;(可能导致保留周期)和ckQueryOperation=newOperation上的错误;因为在街区内不可分配。我通过向上面的ckQueryOperation声明中添加_块解决了此错误,但不确定如何处理retain cycle警告。抱歉,我只有用于使用光标的Swift代码。在Swift中,将新操作分配给该操作的执行者,以强制保留该操作。否则,在执行回调之前,您的代码可能会被丢弃。我不知道如何解决这个问题,我会解决的。谢谢