iOS 6 UICollectionView – Bölüm 2/2

Birinci bölümde UICollectionView’den bahsetmiştik. Bir örnek yapmıştık. Yazımızın 2. bölümünde biraz daha detaylara ineceğiz. CollectionView’a header ekleme, sonuçları seçip mail atacağız.

Header Eklemek

1. yazımızda oluşturduğumuz uygulamamızı dahada geliştirelim. Her bir aramadan sonra bir header olması veya fotoğraf hakkında kullanıcıya biraz daha detay vermek gibi.

Header oluştururken UICollectionReusableView adında yeni bir class kullanacağız. Bunu bir collection view cell gibi de düşünebiliriz. Bu view’i storyboard üzerinde ve kendi class’ına bağlantılı olarak oluşturabiliriz.  Yeni bir class ekleyerek başlayalım:
Menuden FileNewFile…,  iOSCocoa TouchObjective-C class template‘i seçelim. Adını FlickrPhotoHeaderView koyalım ve UICollectionReusableView’in subclass’ı olarak atayalım. Yeni class’ı oluşturmuş olduk.

Öncelikle oluşturmamız gereken 2 tane outlet var. FlickrPhotoHeaderView.m’i açıp aşağıdaki kodu ekleyelim:

@interface FlickrPhotoHeaderView ()
@property(weak) IBOutlet UIImageView *backgroundImageView; 
@property(weak) IBOutlet UILabel *searchLabel;
@end

Böylece class için 2 tane tanımlama yaptık. UILabel arama kriterini gösterirken, image view’da arkaplanı oluşturacak. Image view’in bir outlet üzerinden bağlantısının olması gerekiyor çünkü boyutu dinamik olarak UILabel’a göre uyarlanacak.

MainStoryboard’ı açıp içindeki collection view’a tıklayalım. Attributes Inspector’i açıp Accessories altındaki Section Header’a tıklayın.

Eğer scene inspector’a bakarsanız, otomatik olarak UICollectionReusableView’in Collection View altına eklendiğini göreceksiniz. UICollectionReusableView’i seçelim ve subview eklemeye başlayalım. Daha rahat çalışmak için view’i 90 pixel genişletelim.
Object Library’den bir image view’i UICollectionReusableView’e sürükleyip bırakalım. Image view’in boyutları çok önemli değil. Ancak merkezde konumlandırılması önemli. Bunu ister guide çizgileri ile ister Editor menusundan yapabilirsiniz. ImageView’in mode ayarını da center yapalım.

Şimdi bir imageView’in üstünde olacak şekilde bir label sürükleyip bırakalım. Font’u 32 birim, merkeze hizalı ve font rengini  mavi seçelim. Görünüm aşağıdaki gibi olmalı:

UICollectionReusableView’in FlickrPhotoHeaderView’in subclass’ı olduğunu belirtip, daha önce oluşturduğumu outlet’leri bağlayacağız.

Scene Inspector’dan Collection Reusable View’a tıklayıp ve Identity Inspector’ü açalım. Class’ı FlickrPhotoHeaderView olarak belirleyelim. Attributes Inspector’ü açıp Identifier değerini FlickrPhotoHeaderView olarak değiştirelim. Bu identifier, view’leri kullanırken işimize yarayacak. Buna ek olarak Attributes Inspector’daki Reuse Identifier değerini de FlickrPhotoHeaderView olarak atayalım. Bu kod tarafında header’ı tanıtmamızı sağlayacak. Şimdi Outlet Inspector’u açıp teker teker backgroundImageView ve searchLabel için uygun outletlere sürükleyip bırakalım.
Uygulamayı çalıştırırsanız header’ı göremeyeceksiniz. Çünkü kod tarafında header için yazdığımız kodu yorum satırı yapmıştık. Bunu düzeltelim:
ViewController.m’i açıp aşağıdaki kodu ekleyelim.

#import "FlickrPhotoHeaderView.h"

Yorum satırlarını kaldıralım ve  collectionView:viewForSupplementaryElementOfKind:atIndexPath: metodunu aşağıdaki gibi değiştirelim.

- (UICollectionReusableView *)collectionView: (UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
    FlickrPhotoHeaderView *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:
UICollectionElementKindSectionHeader withReuseIdentifier:@"FlickrPhotoHeaderView" forIndexPath:indexPath];
    NSString *searchTerm = self.searches[indexPath.section]; [headerView setSearchText:searchTerm];
    return headerView;
}

Yukarıdaki kod ile her bir arama için bir header oluşmasını sağladık. Aynı zamanda header’in text’i de arama sözcüklerimiz olacak. setSearchText metodunu oluşturmadığımızdan hata alacağız.
In the above code, you dequeue the header view for each section and set the search text for that cell. This tells the collection view which header to display for each section. The setSearchText method is obviously one that you haven’t written yet, so you will see an error. Time to implement it!
FlickrPhotoHeaderView.h ‘i açıp aşağıdaki kodu ekleyerek sorunu giderelim:

-(void)setSearchText:(NSString *)text;

FlickrPhotoHeaderView.m:

-(void)setSearchText:(NSString *)text { 
    self.searchLabel.text = text;
    UIImage *shareButtonImage = [[UIImage imageNamed:@"header_bg.png"] resizableImageWithCapInsets:
    UIEdgeInsetsMake(68, 68, 68, 68)];
    self.backgroundImageView.image = shareButtonImage;
    self.backgroundImageView.center = self.center; 
}

setSearchText metodu arkaplan için UIImage oluştururken label’in text’ini de belirleyecek. Uygulamamızı çalıştıralım.

Cell ile Etkileşim

Makalemizin son bölümünde cell’in parmak hareketlerimize nasıl yanıt verdiğine değineceğiz. Bu noktada 2 türlü sonuç oluşturabiliriz. İlki tıklanan resmin daha büyük bir halini gösteren bir popup açılması, ikincisi ise çoklu seçim ile resimleri email üzerinden paylaşmak.

Tek Seçim

İlk olarak tıklanan resmin daha büyük bir halini gösteren bir popup yapacağız. Bunun için File/New/file menusundan iOS/Cocoa touch/ Objective-c class’ı seçerek yeni bir dosya ekleyelim. Adını FlickrPhotoViewControlker koyalım ve UIViewController’ın subclass’ı olarak atayalım. Cihaz tipi iPad olsun. Bu arada dosyayla birlikte xib’i ekleyen checkbox’u boş bırakalım. Class’ı oluşturup açalım ve içine aşağıdaki kodu ekleyelim (FlickrPhotoViewController.h):

@class FlickrPhoto;
@interface FlickrPhotoViewController : UIViewController @property(nonatomic, strong) 
FlickrPhoto *flickrPhoto; 
@end

Bu property’i popup için kullanacağız. FlickrPhotoViewController.m’i açıp en üste ilgili dosyaları import edelim:

#import "Flickr.h" 
#import "FlickrPhoto.h"

@Interface’de deklarasyonumuzu yapalım:

@property (weak) IBOutlet UIImageView *imageView; 
-(IBAction)done:(id) sender;

Bu outlet image’i görüntüleyecek ve kullanıcı “Done” buttonuna bastığında kapatacak.
Ayrıca kapama işlemi için aşağıdaki metodu ekleyelim:

- (IBAction)done:(id)sender {
    // TODO
}

Şimdi MainStoryboard.storyboard’ı açalım ve Object Library’den bir view controller ekleyelim. Yeni controller’i seçip Identity Inspector’den ilgili class adını FlickrPhotoViewController olarak atayalım. Segue’yi oluşturabilmemiz için main view ile yeni view controller’i bağlamamız gerekiyor. Bunun için main view’i ctrl’ye basılı tutup FlickrPhotoViewController’e bırakın. Açılan popup’tan modal’ı seçip segue işlemini tamamlıyoruz.

bundan sonraki adım ise Segue’yi ayarlamamız gerekiyor. 2 view arasınaki segue’ye tıklayıp Attributes Inspector’ü açalım. Identifier değerini ShowFlickrPhoto, Presentation değerini de Form Sheet olarak atayalım.

Şimdi bir toolbar ve image view’i Flickr Photo view controller’a sürükleyip bırakalım. Toolbar’da yer alan buttonun text’ini “Done” olarak  değiştirelim. Ctrl ile sürükleyip Scene Inspector’daki Flickr Photo View Controller’a bırakalım. Açılan popup’tan from the popup’ı seçelim. Aynı işlemi image view için yapalım ve açılan popup’tan imageView’i seçip daha önce oluşturduğumuz outlet ile bağlantımızı tamamlayalım.

ViewController.m’i açıp aşağıdaki gibi bir property tanımlayalım:

@property (nonatomic) BOOL sharing;

Bu bool değeri ile paylaşım veya büyük görüntüden hangisini istediğimizi ayarlayacağız. true değeri paylaşım, false değeri diğer işlemi sağlayacak. Hangi satıra tıklandığını anlamak için aşağıdaki kodu  collectionView:didSelectItemAtIndexPath: metoduna ekleyelim:

if (!self.sharing) {
    NSString *searchTerm = self.searches[indexPath.section]; 
    FlickrPhoto *photo = self.searchResults[searchTerm][indexPath.row]; 
    [self performSegueWithIdentifier:@"ShowFlickrPhoto"
sender:photo]; 
    [self.collectionView
deselectItemAtIndexPath:indexPath animated:YES]; 
} else {
    // Todo: Multi-Selection
}

Eğer paylaşım (yani koddaki sharing) modu aktif değilse, ShowFlickrPhoto metodu çalışıp ayarlardığımız segue üzerinden resmin büyük halini görüntüleyecek. Ayrıca tıklanan fotoğrafıda sender üzerinden gönderiğimizi de belirtmeliyim. Bu hangi fotoğrafı istediğimizi görüntüleyecek view’e söyleyecek.
Kodun sonunda ise cell seçili olmaktan çıktığında belirginliği kaybolacak (yani highlighted olmayacak).

Son olarak oluşturmamız gereken bir class daha var. Hatırlarsanız segue’nin oluşabilmesi için prepareForSegue metodunun çağırılması gerek. ViewController.m’e aşağıdaki kodu ekleyelim.

#import "FlickrPhotoViewController.h"

Segue’yi oluşturmak için aşağıdaki kodu eklemek yeterli olacak.

#pragma mark - Segue
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"ShowFlickrPhoto"]) { 
        FlickrPhotoViewController *flickrPhotoViewController = segue.destinationViewController;         
        flickrPhotoViewController.flickrPhoto = sender;
    } 
}

Özetlersek bu metod FlickrPhotoViewController’in bir instance’ını oluşturup, içinde tanımladığımız FlickrPhoto property’sine gelen fotoğrafı atayacak (sender üzerinden). Bağlantılarımız tamm. Uygulamayı çalıştırıp arama yapalım ve bir resime tıklayalım. Popup açıldı ama resim görüntülenmedi.

FlickrPhotoViewController’e açılan popup’taki image view’e seçilen fotoğrafla ilgili ne yapacağını söylemedik. Bunu gerçekleştirmek için FlickrPhotoViewController.m’i açıp aşağıdaki kodu ekliyoruz.

-(void)viewDidAppear:(BOOL)animated { 
    // 1
    if(self.flickrPhoto.largeImage) {
        self.imageView.image = self.flickrPhoto.largeImage; 
    } else {
        // 2
        self.imageView.image = self.flickrPhoto.thumbnail;
        // 3
        [Flickr loadImageForPhoto:self.flickrPhoto thumbnail:NO completionBlock:^(UIImage *photoImage, NSError *error) {
            if(!error) { // 4
                dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.image =
                    self.flickrPhoto.largeImage;
                }); 
            }
        }]; 
    }
}


Koda bakarsak:

  1. Eğer fotoğraf geldiyse bunu imageView’e ata ve göster.
  2. Eğer fotoğraf gelmediyse ufak halini imageView’e ata ve boyutunu ona uyarla (Facebook uygulamasıda bunu yapıyor).
  3. Flickra seçilen fotoğrafın büyük halini almasını söyle.
  4. Eğer bir sorun çıkmadıysa image view’i güncelle.

Uygulamayı tekrar çalıştıralım. Arama yapıp sonuçlara tıkladığınızda bu sefer resimimiz görüntüleniyor.

Not: Eğer resimin görüntülenmesiyle ilgili sorun yaşıyorsanız UIImageView’in şu özelliklerine örneğin 1 gibi ufak bir değer verin: Content Hugging Priority ve Content Compression Resistance Priority.

Bu noktaya kadar herşey tamam. Sadece bir şey popup’taki button hiçbir işe yaramıyor : ) Done: metodunu değiştirmemiz gerek.
FlickrPhotoViewController.m‘i açıp daha önce boş bıraktığımız metodu değiştirelim:

-(void)done:(id)sender { 
    [self.presentingViewController dismissViewControllerAnimated:YES completion:^{}];
}

Böylece button’ımız işlerlik kazanmış oldu.

Çoklu Seçim

Son olarak kullanıcıya birden fazla fotoğraf seçtirip, seçtiklerini mail ile paylaşmasını sağlayacağız. Süreç UITableView’dakine çok benzer. Tek fark aslında collection view’a çoklu seçime izin vermesini söyleyeceğiz.
İşlemi özetlersek:

  1. Kullanıcı Paylaş (örnekte Share) buttonuna bastığında UICollectionView’a çoklu seçimi açmasını söyle ve belirlediğimiz sharing property’sini (BOOL değeri) YES olarak değiştir.
  2. Seçilen fotoğrafları bir diziye at.
  3. Done button’una basıldığında mail gönderme arayüzünü aç.
  4. Seçilen fotoğrafları HTML olarak gösteren email oluştur..
  5. Kullanıcı göndere veya iptal’e bastığında fotoğrafların seçimi kaldır ve collection view’i tek seçim moduna döndür.

Uygulamamızın hafızasını oluşturacak  diziyi oluşturarak başlayalım. ViewController.m’i açıp property’imizi tanımlayalım:

@property(nonatomic, strong) NSMutableArray *selectedPhotos;

ViewDidLoad metodunun altına bu kodu ekleyelim:

self.selectedPhotos = [@[] mutableCopy];

Diziyi oluşturmuş olduk. Şimdi çoklu seçimi aktive edelim. collectionView:didSelectItemAtIndexPath: metodundaki “Todo: Multi-Selection”  yorum satırını silip aşağıdaki kodu ekleyelim:

NSString *searchTerm = self.searches[indexPath.section]; 
FlickrPhoto *photo = self.searchResults[searchTerm][indexPath.row]; 
[self.selectedPhotos addObject:photo];

Bu kod seçilen kodu belirleyip oluşturduğumuz diziye atayacak. collectionView:didDeselectItemAtIndexPath: daki yorum satırının yerine şu kodları ekleyelim:

if (self.sharing) {
    NSString *searchTerm = self.searches[indexPath.section]; 
    FlickrPhoto *photo = self.searchResults[searchTerm][indexPath.row];
    [self.selectedPhotos removeObject:photo]; 
}

bu arada kullanıcı kazara bir fotoğraf seçebileceğinden bunu değiştirmesini yukarıdaki kod ile sağladık.

Şimdi paylaş buttonu (ShareButton) tıklandığında ne olacağını belirtelim. Button’un metodunu aşağıdaki gibi değiştirelim:

-(IBAction)shareButtonTapped:(id)sender {
    UIBarButtonItem *shareButton = (UIBarButtonItem *)sender; 
    // 1
    if (!self.sharing) {
        self.sharing = YES;
        [shareButton setStyle:UIBarButtonItemStyleDone]; 
        [shareButton setTitle:@"Done"];
        [self.collectionView setAllowsMultipleSelection:YES];
    } else { 
        // 2
        self.sharing = NO;
        [shareButton setStyle:UIBarButtonItemStyleBordered]; 
        [shareButton setTitle:@"Share"];     
        [self.collectionView setAllowsMultipleSelection:NO];
        // 3
        if ([self.selectedPhotos count] > 0) { 
            [self showMailComposerAndSend];
        }
        // 4
        for(NSIndexPath *indexPath in self.collectionView.indexPathsForSelectedItems) { 
            [self.collectionView deselectItemAtIndexPath:indexPath animated:NO]; 
        }
        [self.selectedPhotos removeAllObjects]; 
    }
}

Yukarıdaki kodlara bakalım:

  1. Kullanıcı paylaşım modunu açmadıysa (yani bool değeri false iken) bu kod ile UICollectionView’a çoklu seçimi açmasını ve ilk örnekteki done buttonunu share buttonu olarak değiştirmesini söyledik.
  2. Paylaşım modu zaten açılmışsa done buttonunu share olarak değiştir ve çoklu seçimi kapa (if-else döngüsü nedeniyle 1. veya 2. aşamaya geliyoruz).
  3. Kullanıcı herhangi bir fotoğraf seçmişmi diye bak. Seçtiyse showMailComposerAndSend metodunu çağır.
  4. Seçili tüm fotoğrafların seçimini kaldır ve diziyi temizle.

Mail gönderimi için MFMailComposeViewController’ı yani MessageUIFramework’u projeye eklememiz gerekiyor. Bunu yapmadan uygulamamızı çalıştıramayız. Bunu yapmak için projeye  tıklayıp Project Navigator’u açalım. Flickr Search’ü seçelim Build Phases tabına geçelim. Binary With Libraries’i genişletip (+) buttonuna basalım ve MessageUI framework’ü aratıp ekleyelim. (add)

Projemize eklediğimiz MessageUI framework’ü kullanmak için ViewController.m’e gerekli dosyayyı import edelim.

#import <MessageUI/MessageUI.h>

ViewController’i MFMailComposeViewControllerDelegate protokolünü destekleyecek şekilde değiştirelim.

@interface ViewController ()<UITextFieldDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, MFMailComposeViewControllerDelegate>

En alta aşağıdaki metodu ekleyelim:

-(void)showMailComposerAndSend {
    if ([MFMailComposeViewController canSendMail]) {
        MFMailComposeViewController *mailer = [[MFMailComposeViewController alloc] init];
        mailer.mailComposeDelegate = self;
        [mailer setSubject:@"Check out these Flickr Photos"];
        NSMutableString *emailBody = [NSMutableString string]; 
        for(FlickrPhoto *flickrPhoto in self.selectedPhotos)
        {
            NSString *url = [Flickr flickrPhotoURLForFlickrPhoto: flickrPhoto size:@"m"];
            [emailBody appendFormat:@"<div><img src='%@'></div><br>",url];
        }
        [mailer setMessageBody:emailBody isHTML:YES];
        [self presentViewController:mailer animated:YES completion:^{}];
    } else {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Mail Failure" message:@"Your device doesn't support in-app email" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alert show]; 
    }
}

bu metodda ilk kod bloğu kullanıcının mail atabilirliğini kontrol edecek. Eğer kullanıcı cihazında bir mail hesabı oluşturmadıysa hata verip kullanıcıyı uyaracak. Mail’in içeriği temel html kodları ile görüntülenecek. Mail içeriği ve başlığı oluşunca kullanıcıya mail gönderme ekranı açılır. Burada kullanıcının maili göndermesi veya iptal etmesinde ne olacağını yönetmemiz gerekiyor. Delegate metodu ViewController.m’de mail gönderimi için oluşturalım.

- (void)mailComposeController: (MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
    [controller dismissViewControllerAnimated:YES completion:^{}];
}

Artık mail gönderim ekranı işlem bittiğinde kendini kapatacak. Artık uygulamayı çalıştırabiliriz.

Geriye bir tek şey kaldı. Kullanıcı fotoğrafı seçtiğini nasıl anlayacak? Görsel herhangi bir şey olmadığından anlaması imkansız. Bunu da selectedBackgroundView özelliğini değiştirerek sağlayabiliriz.
FlickrPhotoCell.m‘i açalım ve initWithFrame: metodunu silip aşağıdakini ekleyelim:

-(id)initWithCoder:(NSCoder *)aDecoder { 
    self = [super initWithCoder:aDecoder]; 
    if (self) {
        UIView *bgView = [[UIView alloc] initWithFrame:self.backgroundView.frame]; 
        bgView.backgroundColor = [UIColor blueColor]; 
        bgView.layer.borderColor = [[UIColor whiteColor] CGColor]; 
        bgView.layer.borderWidth = 4;
        self.selectedBackgroundView = bgView; 
    }
    return self; 
}

Bu kod ile arkaplanı mavi ve beyaz kenarlığı olan bir view oluşup bunu selectedBackgroundView olarak atayacağız. Ne zaman bir cell seçili hale gelse bu metod tetiklenip arkaplanı değiştirecek.
Uygulamayı çalıştıralım. tüm özellikleri deneyelim. Seçili fotoğraflar aşağıdaki gibi olmalı:

Kusursuz çalışıyor! Tebrikler Flickr Fotoğraf tarayıcınızı UICollectionView ile oluşturmuş oldunuz.

Örnek projeyi burayadan indirebilirsiniz.

Leave a Reply

Your email address will not be published. Required fields are marked *