MAR
10

iPhone Uygulamalarında UIScrollView Kullanımı

7 yorum | Kategori: Mobil Programlama | 10 Mart 2011 Perşembe

Bu yazımda, iPhone/iPad uygulamaları yazarken daha önceden bahsettiğim bileşenlerden sonra en çok kullanılan bileşen olan UIScrollView'in (Türkçesine kayar panel diyeceğim) nasıl kullanıldığını bir örnek üzerinde açıklayacağım. Temel amacı ekrana sığmayan herhangi bir şeyin kullanıcı tarafından swipe/pitch hareketleri yardımıyla görülüp kullanabilmesini sağlamak olan bu bileşenin piyasada bulabileceğiniz iPhone ve iPad uygulamalarının çoğunda kullanıldığından emin olabilirsiniz. Burada vereceğim örnekte ekrana sığmayan bir resmin ScrollView yardımıyla nasıl gösterilebileceğini ve ScrollView içeriğinin doğrudan kullanıcı müdahelesi olmadan kod içerisinden nasıl kontrol edilebileceğini anlatacağım. Anlatacaklarım basit gibi görünebilir, ancak yazmaya kalkacağınız uygulamaların %90'ında bundan fazlasını bilmeniz gerekmeyecek. Daha sonraki yazılarımdan birinde ScrollView kullanarak nasıl iPhone ve iPad'in ana ekranında olduğu gibi sayfalama yapılabileceğini de göstereceğim. Ancak o yazıyı anlayabilmeniz için önce burada anlatacaklarımı anlamış olmanız gerekiyor.

İşe her zamangi gibi View-Based Application taslağı ile yeni bir iPhone proje yaratarak başlayın. Ben projenin adını diğer iPhone ile ilgili yazılarımda yaptığım gibi gibi Ornek koydum. Proje yarattıktan sonra içerisine ekrana sığmayacak kadar büyük olan bir resim dosyasını ekleyin. Ben bu örnekte resim.jpg adlı bir resim ekledim. Daha sonra "OrnekViewController.xib" adlı arayüz dosyasını açıp içerisine 2 adet tuş (UIButton) ve bir adet kayar panel (UIScrollView) koyun. Bu 3 bileşeni aşağıdaki şekilde gösterildiği gibi yerleştirin ve tuşların üzerindeki yazıları da şekildekine benzer hale getirin.

UIScrollView - Kayar Panel Örneği Arayüzü

Soldaki tuş, ScrollView içeriğini belli bir miktar sağa kaydırmak için, sağdaki tuş ise belli bir miktar sola kaydırmak için kullanılacak. Bu cümle biraz ters gibi görünüyor ancak doğru. Çünkü ScrollView'ın içerdiği resmin ekrana sığmamış olan sağ bölümünü görmek için içeriği sola kaydırmak gerekiyor (ve tersi). 

Sonraki aşama "OrnekViewController.h" adlı objective-c header dosyasına gidip gerekli Outlet, özellik ve metod tanımlamalarını yapmak. ScrollView'a kod içerisinden erişmeniz gerekeceği açık, o yüzden onun için bir Outlet gerekiyor. Tuşlar ile ilgili bir şey değiştirmeyeceksiniz ancak her iki tuşa basma eylemini de aynı metodla karşılamak isterseniz (bu örnekte istiyoruz çünkü 2 metod gereksiz), metodun hangi tuşa basıldığını anlaması için bu tuşlara erişebilmesi gerekir. Yani  bunlar için de birer Outlet gerekiyor. Bunlar dışında bir de gösterilecek resmi içerecek olan UIImageView bileşenine ihtiyacınız var ancak bu bileşen .xib dosyasındaki arayüzde olmadığı için bunu bir Outlet değil, normal bir özellik olarak tanımlamanız gerekiyor. Ekleyeceğiniz tek metod tanımlaması ise her iki tuş için de kullanılacak olan "tusaBasildi" adlı metod. Bu tanımlamalar yapıldıktan sonra dosya aşağıdaki hali almakta:

#import <UIKit/UIKit.h>

@interface OrnekViewController : UIViewController
{}

@property (nonatomic,retain) IBOutlet UIScrollView *scroller;
@property (nonatomic,retain) IBOutlet UIButton *tusSol, *tusSag;
@property (nonatomic,retain) UIImageView *imageView;

- (IBAction)tusaBasildi:(id)sender;

@end 

Şimdi "OrnekViewController.m" adlı objective-c dosasına geçip gereksiz metodları silin, Outlet'ler ve özellik için gerekli olan @synthesize direktiflerini ve yine bunlar için ayrılmış olan bellek alanlarını iade eden satırları ilgili yerlere yazın. Daha sonra arayüz dosyasına dönüp tuşları ve ScrollView'ı yaratmış olduğunuz Outlet'lere bağlayın. Bundan sonra .xib dosyası ile işiniz yok, o yüzden tekrar .m dosyasına dönün. Şu noktada .m dosyasının içeriği şu halde olmalı.

#import "OrnekViewController.h"

@implementation OrnekViewController

@synthesize scroller, tusSol, tusSag, imageView;

- (void)viewDidUnload
{
    self.scroller = nil;
    self.tusSol = nil;
    self.tusSag = nil;
}

- (void)dealloc
 {
    [super dealloc];
    [scroller release];
    [tusSol release];
    [tusSag release];
    [imageView release];
}

@end

Şimdi uygulamanın asıl işlevselliğini yazmaya başlıyoruz. Öncelikle viewDidLoad metodunu aşağıdaki hale getirin.

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // Resmi aç
    UIImage* resim = [UIImage imageNamed:@"resim.jpg"];
    
    // Resmi imageView içerisine koy
    self.imageView = [[UIImageView alloc] initWithImage:resim];
    [imageView release];
    
    // imageView'ı kayar panele ekle
    [scroller addSubview:imageView];   
    
    // imageView'in kayar panel içerisindeki pozisyonunu ayarla
    imageView.frame = CGRectMake(0, 0, resim.size.width, resim.size.height);

    // kayar panelin içerik boyutunu ayarla
    scroller.contentSize = resim.size;
    
    // Kayar panelin izin vereceği en küçük ve en büyük ölçekleri ayarla
    scroller.minimumZoomScale = scroller.frame.size.width/resim.size.width;
    scroller.maximumZoomScale = 2;
}
Burada ilk olarak projeye daha önceden eklemiş olduğumuz resmi açtık ve resmi ekranda göstermek için gerekli olan UIImageView türünden bir bileşen içerisine koyduk. Daha sonra yarattığımız imageView'ı kayar panelin içerisine ekledik. Sonrasında imageView'ın boyutunu resmin boyutuna eşitledik çünkü imageView bu işi ne yazık ki kendisi yapmıyor. Burada en çok dikkat edilmesi gereken satır şüphesiz panelin içerik boyutunun ayarlandığı satır. Eğer bunu yapmazsanız ekrandaki resmi kaydıramadığınızı görürsünüz. Kayar paneli ve içeriğini 2 farklı nesne olarak düşünmek gerekiyor çünkü öyleler. İçeriğin boyu panelin kendi boyundan büyük olmalı ki kayar panel kullanmanın bir anlamı olsun. En son yaptığımız şey ise panelin minimum ve maksimum zoom miktarını ayarlamak. Burada ayarladığımız değerler kullanıcının pinch hareketi yaparak içeriği kaç kat büyütüp küçültebileceğini belirliyor. Maksimum büyütme miktarını 2 kat yapmış olmamın özel bir sebebi yok, herhangi bir değer olabilirdi. Ancak minimum büyütme miktarını (aslında verdiğimiz resim için küçültme oluyor bu çünkü 1'den küçük) resmin genişliği ekran genişliğine eşit olacak şekilde ayarladım.

Uygulamayı bu haliyle çalıştırırsanız parmağınızı panel üzerinde kaydırarak resmi de kaydırabildiğinizi, ancak pinch hareketi ile büyütme/küçültme yapamadığınızı görürsünüz. Zoom işlevinin çalışması için UIScrollViewDelegate protokolü içerisinde tanımlanmış olan viewForZoomingInScrollView: metodunun implementasyonunu yapmanız gerekiyor. ScrollView bize zoom yaparken hangi bileşenin boyunun değiştirileceğini bizden bu metod aracılığı ile öğreniyor. Bu metodu yazmadan önce .h dosyasına dönüp @interface satırının hemen sonrasına <> içerisinde protokol ismini yazarak ViewController'ınızın bu protokole uyumlu olduğunu belirtmeniz gerekiyor. Tam olarak nereyi kasdettiğimi aşağıda görebilirsiniz.

#import 

@interface OrnekViewController : UIViewController
{

}

Bu değişikliği yaptıktan sonra tekrar .xib dosyasını açıp arayüzdeki UIScrollView'in delegate özelliğini  File's Owner ikonuna sürükleyerek bağlamanız gerekiyor. Buna alternatif olarak viewDidLoad: metoduna aşağıdaki satırı ekleyebilirsiniz.

scroller.delegate = self; 

Bu bağlantı, arayüzünüzdeki UIScrollView bileşeninin, zoom yapıldığı zaman içerdiği hangi bileşenin boyutunu değiştirmesi gerektiğini OrnekViewController adlı sınıfın viewForZoomingInScrollView: adlı metodunu çağırarak öğrenmesi gerektiği anlamına geliyor.

Sonraki aşama metodun implementasyonunu yapmak.

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    // Zoom işlemi sırasında resmi içeren imageView'ın boyu değişsin
    return imageView;
}

Gördüğünüz üzere metod oldukça basit. Tek yaptığımız şey zoom in/out yapıldığında resmin büyütülüp küçültülmesini sağlamak için metod'un resmimizi içeren imageView'i geri döndürmesini sağlamak. Uygulamayı bu haliyle tekrar çalıştırırsanız artık kaydırma işlemine ek olarak zoom işlevinin de düzgün çalıştığını görürüsünüz. Eğer resmin kenarlarına geldiğinizde oluşan ve geçici olarak arayüzün arkasındaki gri rengin görünmesine sebep olan sekme efektini istemiyorsanız bunu da arayüz editöründe ScrollView'ın bouncing ile ilgili özelliklerini kapatarak sağlayabilirsiniz. Aşağıdaki ekran görüntüsünde uygulamanın son halini ve kullandığım örnek resmi görebilirsiniz.

UIScrollView - Kayar Panel Örneği Son hali

Son olarak yukarıya koymuş olduğunuz tuşların çağıracağı metodun implementasyonunu aşağıdaki şekilde yapın. (Arayüz editöründen de tuşun TouchUpInside eventini yine File's Owner'a sürükleyerek bu metoda bağlamayı unutmayın.)

- (IBAction)tusaBasildi:(id)sender
{
    // Kayar panel içeriğinin şu anda ne kadar kaydırılmış ve büyütülmüş olduğu öğren
    float oX = scroller.contentOffset.x;
    float olcek = scroller.zoomScale;
    
    // Şu anki ölçekte en fazla ne kadar kaydırma yapılabileceğini hesapla
    float xLimit = imageView.frame.size.width - scroller.frame.size.width;
    
    // Panel içeriğini sola veya sağa kaydır
    if (sender == tusSol)
        oX -= 50*olcek;
    else
        oX += 50*olcek;
    
    // Kayma miktarının limitler dahilinde olduğundan emin ol
    if (oX < 0) oX = 0;
    if (oX > xLimit) oX = xLimit;
    
    // Panel içeriğini animasyonlu olarak kaydır
    [scroller setContentOffset:CGPointMake(oX, scroller.contentOffset.y) animated:YES];
}

Burada kayar panel içeriğinin ne kadar kaydırılmış olduğunu ve ne kadar büyütülmüş ya da küçültülmüş olduğunu, yani ölçeğini öğrendik. Bu değerleri kullanarak, o anki ölçekte resmin en sağ noktası ekranın en sağ noktasına hangi kayma değerinde geldiğini, yani arkaplanı göstermeden resmin en fazla ne kadar kaydırılabileceğini hesapladık. Daha sonra metodun "sender" adlı parametresi yardımıyla  hangi tuşa basıldığını öğrendik ve ona göre yeni kayma miktarını daha az veya fazla olacak şekilde hesapladık. Hesaplamayı yaparken eklenen veya çıkarılan miktarın ölçekle orantılı olması için işlemi yapmadan önce her ikisini de o anki ölçek ile çarptık. Daha sonra yeni kayma miktarların sınırlar dahilinde olup olmadığına, yani içerik bu yeni miktar kadar kaydırılırsa resmin sol veya sağ kısmındaki boşlukların görünüp görünmeyeceğine baktık. Görünecek ise kayma miktarını görünmemesini sağlayacak şekilde limitledik. Son olarak da panelin içeriğini yeni hesaplamış olduğumuz miktar kadar animasyonlu şekilde kaydırdık.

Uygulamayı bu son haliyle çalıştırırsanız, resmin panele dokunmadan, sadece yukarıdaki tuşlar aracılığı ile sola veya sağa kaydırılabildiğini ve kaydırma işleminin asla resmin sol veya sağ sınırını aşmadığını görürsünüz.

Bu örnekte ScrollView'i içerisine sadece tek bir bileşen varken kullanmayı öğrendiniz. Ancak durum 100 tane bileşen koysanız da aynı. Tek yapmanız gereken şey o 100 tane bileşeni bir container view içerisine koymak ve o panelin içerisine sadece o view'ı eklemek. Zoom sırasında çağrılan delegate metodunu yazarken de resim yerine o view'ı geri döndürdünüz mü her şey kusursuz şekilde çalışacaktır.

Konuyla alakasız bir not düşeyim: Eğer bu uygulamayı retina display'e sahip olan bir iPhone'da, yani iPhone 4'te çalıştırırsanız resmin bulanık göründüğünü farkedeceksiniz. iPod Touch ve  normal iPhone, iPhone 4 ve iPad gibi farklı çözünürlüğe sahip cihazların hepsinde düzgün ve net görünen bir uygulama yazmak için neler yapılması gerektiğini bu konuya özel yazacağım bir yazıda detaylı olarak anlatacağım. Ancak şu anki problemi çözmeniz için projeye, kullandığınız resmin her iki eksende de tam 2 katı daha büyük boyuta sahip olan halini resim@2x.jpg adıyla eklemeniz yeterli. Tabi bu 2 kat büyük resim küçük olanın upscale edilmiş versiyonu olmamalı, net olmalı. Elinizdeki resmi resim@2x.jpg olarak isimlendirip onu her iki eksende 1/2 oranında küçülterek elde ettiğiniz resmi resim.jpg die ekleyebilirsiniz mesela projeye.

Yorumlar

Bu yazı hakkında toplam 7 yorum bulunmaktadır. Siz de yorum ekleyebilirsiniz.
Yorum Cihan Tek | 24 Eylül 2011 Cumartesi 16:46:27
viewForZoomingInScrollView delegate metodunda hangi nesneyi gösterdiğinize dikkat edin. Sorununuz büyük ihtimalle oradan kaynaklanıyordur.
Yorum Deniz Korkmaz | 24 Eylül 2011 Cumartesi 16:35:21
ben de buna benzer bir uygulama yapıyorum fakat benim uygulamamda 16 veya 17 resim var. resimleri internetten alıyorum ve bir döngü içerisinde UIImageView üzerine atıyorum onu da UIScrollView içerisinde gösteriyorum. buraya kadar sorun yok fakat kullanıcı pinch etmek istediğinde resim pinch oluyor fakat o resmin genişliği aşıldığında diğer resimler gelmiyor. onu nasıl yapabilirim ?
Yorum Cihan Tek | 22 Eylül 2011 Perşembe 10:13:43
Aynı sayfaya istediğiniz kadar ScrollView koyabilirsiniz ancak her biri için farklı outlet tanımlamanız gerekiyor. Delegate metodları içerisinde metodların hangi ScrollView tarafından çağrıldığını parametre olarak gelen scrollView değişkeni ile sizin yaratmış olduğunuz outlet'leri kıyaslayarak anlayabilir, ona göre farklı işlemler yaptırabilirsiniz.
Yorum Gökhan Gültekin | 22 Eylül 2011 Perşembe 00:11:47
Merhabalar Cihan Bey,

Bir View Controller içerisinde 2 adet Scroll View çalıştırmak istiyorum ve bunların farklı çalışması gerekiyor. Bu nasıl yapılabilir bir bilginiz var mı?
Yorum uğur çakmaklı | 05 Nisan 2011 Salı 13:16:25
Çok teşekkürler harika olmuş. Retina ekranlı (iphone-4) için vereceğiniz bilgileri de sabrsızlıkla bekliyorum.

Selam ve saygılar..
Yorum Cihan Tek | 25 Mart 2011 Cuma 16:25:28
Bundan sonra yazacağım yazı ScrollView kullanarak sayfalama yapmakla ilgili olacak. Ondan sonrakilerde de çok ekranlı uygulamaların nası yapıldığından bahsedeceğim. Sorunuzun cevabı için siteyi takip etmenizi öneririm.
Yorum YalçınKüçük | 25 Mart 2011 Cuma 10:14:43
Çok başarılı bir anlatım olmuş ancak bunu uygularken aklıma bi soru takıldı ve bulamadım cvabını. ben sayfalama yapmak istesem bunu nasıl başarabilirim peki. yani butonlarım olcak sizin yöntemdeki gibi butona basınca bir başka sayfaya gidecek bunu nasıl uygulayabilrim peki yardımcı olursanız çok sevirnirm. İlginiz için tşk ederim.

Yorum Yazın

İsim ve soyadınız : E-Posta adresiniz : Güvenlik kodu : Güvenlik Kodu Yorumunuz : Gönder