그리드 뷰 새로고침하기
이제 ViewController.m 에서, 새로고침 IBAction 메소드를 구현을 해야 한다.
우선, 새로고침을 위한 다른 HUD를 만든다. 그래서 이전에 업로딩을 위해서 사용한 다른 HUD를 방해하지 않는다.
refreshHUD = [[MBProgressHUD alloc] initWithView:self.view];
[self.view addSubview:refreshHUD];
refreshHUD.delegate = self;
[refreshHUD show:YES];
|
다운로드 쿼리 생성하기
대량의 코드가 있다. 한 단계, 한 단계씩 진행하겠다.
이미지를 다운로드하려면 PFQuery 를 사용 해야한다. 쿼리는 Parse에서 검색 범위를 좁힐 수 있는 조건으로 객체를 찾을 때 사용된다.
업로드 한 모든 이미지를 추출하기 위해서 이전에 설정한 클래스 이름에 해당하는 PFQuery 를 만들어야 한다. 또한, 검색 결과를 현재 속해 있는 사용자로 제한해야한다.
쿼리가 생성되면, 결과를 findObjectsInBackgroundWithBlock: 메소드로 검색할 수 있다.
- (void)downloadAllImages
{
PFQuery *query = [PFQuery queryWithClassName:@"UserPhoto"];
PFUser *user = [PFUser currentUser];
[query whereKey:@"user" equalTo:user];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
NSMutableArray *newObjectIDArray = [NSMutableArray array];
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
if (objects.count > 0) {
for (PFObject *eachObject in objects) {
[newObjectIDArray addObject:[eachObject objectId]];
}
}
|
다운로드를 빠르게 하기위해, NSUserDefaults에서 각 사진의 objectID 를 저장할 것이다. 쿼리 결과가 반환 될 때, 새로운 objectID 가 있는 사진만 다운로드 한다.
누락된 objectID는 photoScrollView 에서 그것들을 삭제 할 것이다. 데이터베이스에서 항목이 제거되는 경우 발생한다. 예를 들어 Parse의 웹 인터페이스에서 제거 될 때. 삭제하기 위해, 각각 버튼(나중에 설정방법을 보여준다.)의 태그를 확인한다.
NSMutableArray *newCompareObjectIDArray = [NSMutableArray arrayWithArray:newObjectIDArray];
NSMutableArray *newCompareObjectIDArray2 = [NSMutableArray arrayWithArray:newObjectIDArray];
NSMutableArray *oldCompareObjectIDArray = [NSMutableArray arrayWithArray:[standardUserDefaults objectForKey:@"objectIDArray"]];
if ([standardUserDefaults objectForKey:@"objectIDArray"]){
[newCompareObjectIDArray removeObjectsInArray:oldCompareObjectIDArray];
[oldCompareObjectIDArray removeObjectsInArray:newCompareObjectIDArray2];
if (oldCompareObjectIDArray.count > 0){
NSMutableArray *listOfToRemove = [[NSMutableArray alloc] init];
for (NSString *objectID in oldCompareObjectIDArray){
int i = 0;
for (NSString *oldObjectID in [standardUserDefaults objectForKey:@"objectIDArray"]){
if ([objectID isEqualToString:oldObjectID]){
for (UIView *view in [photoScrollView subviews]) {
if ([view isKindOfClass:[UIButton class]]) {
if (view.tag == i){
[view removeFromSuperview];
NSLog(@"Removing picture at position %i",i);
}
}
}
[listOfToRemove addObject:[NSNumber numberWithInt:i]];
}
i++;
}
}
|
이미지가 지나치지 않도록 뒤에서 부터 다시 제거한다.
NSSortDescriptor *highestToLowest = [NSSortDescriptor sortDescriptorWithKey:@"self" ascending:NO];
[listOfToRemove sortUsingDescriptors:[NSArray arrayWithObject:highestToLowest]];
for (NSNumber *index in listOfToRemove){
[allImages removeObjectAtIndex:[index intValue]];
[self setUpImages:allImages];
}
|
누락된 객체 제거 외에, 또한 새로운 객체를 추가해야한다. 거꾸로 변환하는 것은 쉽다. 결과에서 각각 PFObject 를 통하여 NSData 로 변환하게 getData 를 호출한다. 다음 로드에 다시 로드 할 수 있도록 NSUserDefautls에 objectIDArrary를 저장하는 것을 잊지마라.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSMutableArray *imageDataArray = [NSMutableArray array];
for (int i = 0; i < images.count; i++) {
PFObject *eachObject = [images objectAtIndex:i];
PFFile *theImage = [eachObject objectForKey:@"imageFile"];
NSData *imageData = [theImage getData];
UIImage *image = [UIImage imageWithData:imageData];
[imageDataArray addObject:image];
}
dispatch_async(dispatch_get_main_queue(), ^{
for (UIView *view in [photoScrollView subviews]) {
if ([view isKindOfClass:[UIButton class]]) {
[view removeFromSuperview];
}
}
for (int i = 0; i < [imageDataArray count]; i++) {
PFObject *eachObject = [images objectAtIndex:i];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *image = [imageDataArray objectAtIndex:i];
[button setImage:image forState:UIControlStateNormal];
button.showsTouchWhenHighlighted = YES;
[button addTarget:self action:@selector(buttonTouched:) forControlEvents:UIControlEventTouchUpInside];
button.tag = i;
button.frame = CGRectMake(THUMBNAIL_WIDTH * (i % THUMBNAIL_COLS) + PADDING * (i % THUMBNAIL_COLS) + PADDING,
THUMBNAIL_HEIGHT * (i / THUMBNAIL_COLS) + PADDING * (i / THUMBNAIL_COLS) + PADDING + PADDING_TOP,
THUMBNAIL_WIDTH,
THUMBNAIL_HEIGHT);
button.imageView.contentMode = UIViewContentModeScaleAspectFill;
[button setTitle:[eachObject objectId] forState:UIControlStateReserved];
[photoScrollView addSubview:button];
}
int rows = images.count / THUMBNAIL_COLS;
if (((float)images.count / THUMBNAIL_COLS) - rows != 0) {
rows++;
}
int height = THUMBNAIL_HEIGHT * rows + PADDING * rows + PADDING + PADDING_TOP;
photoScrollView.contentSize = CGSizeMake(self.view.frame.size.width, height);
photoScrollView.clipsToBounds = YES;
});
});
|
이번 상황에서는 Grand Central Dispatch를 사용하여 getData 를 처리 할 것이다. 이렇게하려면 디스페치 큐를 생성하고 비동기적으로 호출을 수행한다. 완료되면, 전체 photoArray 에 추가한다. 그리고 메인 쓰레드에서 이미지를 깔끔하게 정리하기 위해 setUpImages 를 호출한다.
그리드 설정하기
setUpImages 메소드에서 이미지가 저장되는 것을 보장하기 위해 이미지를 allImages로 복사한다. 그 후에, 모든 현재 버튼을 삭제하고 새로운 것을 배치시킨다.
allImages = [images mutableCopy];
for (UIView *view in [photoScrollView subviews]) {
if ([view isKindOfClass:[UIButton class]]) {
[view removeFromSuperview];
}
}
|
각각의 이미지를 위해서 UIButton 을 만들어서 그리드에 배치시킨다. 나중에 UIButton 을 탭을 했을 때 참조하기 위해 각각의 UIButton 에 태그한다. 또한, 각각의 UIButton 에 이미지를 보여주는 디테일 뷰 컨트롤러를 여는 타겟 메소드를 연결한다.
마지막으로 photoScrollView 에 알맞은 contentSize 를 계산하고 clipsToBounds 에 YES로 설정한다. 그리드에 올바르게 설정한다. 이미지 사이의 패딩값은 4px이다. 썸네일 크기는 75px * 75px이다. 그리고 4개의 컬럼으로 되어있다.
UIButton *button;
UIImage *thumbnail;
for (int i=0; i<images.count; i++) {
thumbnail = [images objectAtIndex:i];
button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setImage:thumbnail forState:UIControlStateNormal];
button.showsTouchWhenHighlighted = YES;
[button addTarget:self action:@selector(buttonTouched:) forControlEvents:UIControlEventTouchUpInside];
button.tag = i;
button.frame = CGRectMake(THUMBNAIL_WIDTH * (i % THUMBNAIL_COLS) + PADDING * (i % THUMBNAIL_COLS) + PADDING, THUMBNAIL_HEIGHT * (i / THUMBNAIL_COLS) + PADDING * (i / THUMBNAIL_COLS) + PADDING + PADDING_TOP, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT);
button.imageView.contentMode = UIViewContentModeScaleAspectFill;
[photoScrollView addSubview:button];
}
int rows = images.count / THUMBNAIL_COLS;
if (((float)images.count / THUMBNAIL_COLS) - rows != 0) {
rows++;
}
int height = THUMBNAIL_HEIGHT * rows + PADDING * rows + PADDING + PADDING_TOP;
photoScrollView.contentSize = CGSizeMake(self.view.frame.size.width, height);
photoScrollView.clipsToBounds = YES;
|
디테일 뷰 컨트롤러 열기
별도의 모달 뷰 컨트롤러에 자세한 사진을 열 수 있다. 여기 할수 있는 방법이 있다.
우선 새로운 UIViewController (File > New > New File > UIViewController 서브클래스)를 PhotoDetailViewController 으로 생성한다. 완료되면, nib을 설정하고 ViewController.m 의 상단에 “PhotoDetailViewController.h”를 import 한다.
UINavigationBar, UIBarButtonItem 그리고 자세한 이미지를 보여주는 UIImageView 을 포함하는 nib는 다음과 비슷할 것이다.

UIButton에 해당하는 태그가 있기 때문에, 이전에 저장한 allImages 배열에서 정확한 사진을 얻을 수 있다. 이 이미지로 새로운 디테일 뷰 컨트롤러에 전달하고 컨트롤러를 제시한다.
- (void)buttonTouched:(id)sender{
UIImage *selectedPhoto = [allImages objectAtIndex:[sender tag]];
PhotoDetailViewController *pdvc = [[PhotoDetailViewController alloc] init];
pdvc.selectedImage = selectedPhoto;
[self presentModalViewController:pdvc animated:YES];
}
|
이 튜토리얼에서 사진을 PFObjects 로 파일을 랩핑하여 클라우드로 업로드하는 것, PFQueries 를 사용해서 사진을 다운로드 하는 것, 그리드에 사진을 배치하는 것을 다루었다. 또한, 새로운 이미지를 다운로드하고 누락된 것을 제거하는 간단한 캐싱을 했다. 뿐만 아니라 디테일 뷰 컨트롤러 생성도 하였다.