我有一个应用程序,其中包含从 CloudKit 中提取的图像 Collection View 。我有一个 CKManager 类,它执行所有与 CK 相关的方法。在 viewcontroller 中,我调用了 CKManager 中的一个方法来从 CK 中检索初始数据,这一切都很完美。我正在使用 CKQueryOperation,所以我可以分块提取数据,尽管到目前为止我设置 ckQueryOperation.resultsLimit = CKQueryOperationMaximumResults 只是为了测试。因此,当滚动 collectionview 时,图像/单元格不会在您滚动时“淡入”。我假设这是因为在渲染单元格之前已经检索了所有数据。目前大约有 50 条记录,加载速度相当快,但当我将结果限制设置为 25 条时,加载速度肯定更快。
我的问题是我不完全理解如何使用游标来执行此操作,即使我已经计划通过在我的代码中实现游标来实现它。我找到了这个 thread我大部分都理解,但它是在 Swift 中,它也没有回答我所有的问题。我根据该线程中 Edwin 的回答修改了我的代码,但我确定我在 Swift 到 OB-C 的转换中遗漏了一些东西。
下面是我在 CKManager 类中调用的代码。我可以从日志中看到它工作正常并且可以识别光标。我不明白的是我如何/何时再次调用它以从该光标点获取下一个结果 block ?如果 resultsLimit 没有像最初那样设置为最大值,我会得到指定的结果数量 (20),并且不会检索剩余的结果。所以我不知道如何在光标停止的地方获得剩余的结果。我确实知道,因为我使用的是 collectionview,所以每次获得下一个结果 block 时,我都需要更新部分中的项目数。
非常感谢!
更新:更改了 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];
关于ios - 使用游标加载 UICollectionView,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31230105/