그리드 뷰 새로고침하기
이제 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 *newCompareObjectIDArray 2 = [NSMutableArray arrayWithArray :newObjectIDArray];
NSMutableArray *oldCompareObjectIDArray = [NSMutableArray arrayWithArray :[standardUserDefaults objectForKey : @"objectIDArray" ]];
if ([standardUserDefaults objectForKey : @"objectIDArray" ]){
[newCompareObjectIDArray removeObjectsInArray :oldCompareObjectIDArray];
[oldCompareObjectIDArray removeObjectsInArray :newCompareObjectIDArray 2 ];
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 를 사용해서 사진을 다운로드 하는 것, 그리드에 사진을 배치하는 것을 다루었다. 또한, 새로운 이미지를 다운로드하고 누락된 것을 제거하는 간단한 캐싱을 했다. 뿐만 아니라 디테일 뷰 컨트롤러 생성도 하였다.