< Previous: Bir detay ekranı oluşturma | Next: Son düzenlemeler: hidesBarsOnTap, güvenli bölge sınırları > |
Bu noktada, storyboard'umuzda görselleri seçebileceğimiz orjinal bir tablo ekranı denetleyicimiz ve bir de detay ekranı denetleyicimiz var. Sonraki amacımız, herhangi bir tablo satırına dokunduğumuzda, seçtiğimiz görseli içeren detay ekranını göstermek olacak.
Bunu yapmak için, ViewController
'a başka bir ismi özel olarak verilmiş bir metod eklememiz gerekiyor. Bunun adı tableView(_, didSelectRowAt:)
. Hangi satırla çalıştığımızı söyleyen cellForRowAt
gibi bir IndexPath
değeri alır. Bu kez biraz daha fazla işimiz var:
DetailViewController
içinde bir özellik oluşturmamız gerekiyor.didSelectRowAt
metodunu uygulayacağız, böylece storyboard'tan bir DetailViewController
yüklenecek.DetailViewController
'daki viewDidLoad()
metodunun içini dolduracağız. Öyle ki bu da, daha önce verdiğimiz isme göre görsel ekranının içine bir görsel yükleyecek.Şimdi bunları ilkinden başlayarak, sırasıyla çözelim: DetailViewController
içinde, yüklenecek görsellerin adını tutan bir özellik oluşturalım.
Bu özellik bir string olacak –yüklenecek görselin adı– ama bu bir optional olmak zorunda, çünkü ekran denetleyicisi ilk oluşturulduğunda, henüz varolmuş değil. Onu doğrudan kuracağız, ama yine de hayata boş olarak başlayacak.
Şimdi bu özelliği DetailViewController
içine, tam @IBOutlet
satırının altına ekleyin:
var selectedImage: String?
İlk görev tamamlandı; sıra ikincisinde: didSelectRowAt
özelliğini uygulayalım, böylece o da storyboard'tan bir DetailViewController
yüklesin.
Detay ekranı denetleyicisini oluşturduğumuzda, instantiateViewController(withIdentifier:)
adlı metodu kullanarak storyboard'tan onu yüklememize izin verecek olan “Detail” storyboard ID'si vermiştiniz. Her ekran denetleyicisi storyboard
adında bir özelliğe sahiptir; ya yüklendiği storyboard'tur veya nil değerindedir. ViewController
durumunda ise, bu özellik Main.storyboard olacaktır. Bu da, detay ekranı denetleyicisini içeren storyboard'la aynıdır; dolayısıyla oradan yüklüyor olacağız.
Bu görevi, üç küçük görev haline parçalayabiliriz ki, bunlardan ikisi yeni:
pictures
dizisinden doğru elemanı, selectedImage
özelliği olacak şekilde ayarla.Bunların ilki, instantiateViewController
çağrısı ile yapılıyor, ama iki küçük zorluğu var. Birincisi, Apple'ın UIViewController
tipinden edindiğimiz storyboard
özelliği üzerinde onu çağırıyoruz. Ama o bir optional'dır, çünkü Swift, bir storyboard'dan geldiğimizi bilmiyor. Bu yüzden, ?
kullanmamız gerekiyor; tıpkı hücremizin metin etiketini ayarladığımızdaki gibi: "Bunu yapmayı dene, ama eğer bir sorun varsa hiçbir şey yapma".
İkincisi, instantiateViewController()
metodunun her şey doğru bir şekilde çalışırsa bize geri bir DetailViewController
gönderecek olmasına rağmen, Swift onun geriye bir UIViewController
döndüreceğini düşünecek, çünkü neyin ne olduğunu bilen storyboard'ın içinde göremeyecek.
Eğer programlama konusunda yeniyseniz, bu biraz kafa karıştırıcı görünebilir, o yüzden bir örnek kullanarak açıklamaya çalışayım. Diyelim ki, bu gece bir randevunuz var ve benden bir etkinliğe bir çift bilet ayarlamamı istiyorsunuz. Gidiyorum ve biletleri buluyorum. Ardından onları bir zarfla size veriyorum. Üzerime düşen kısmını yerine getirdim: Bilet istediniz, biletleri temin ettim. Peki ya biletler ne içindi; bir spor etkinliği için mi? Veya bir opera? Ya da tren bileti mi? Bunu anlamanın tek yolu, zarfı açıp bakmak.
Swift'te de aynı sorun var: instantiateViewController()
metodu, UIViewController
dönüş tipine sahip; dolayısıyla Swift bununla herhangi bir ekran denetleyicisi yarattığı sürece, aslında o bir UIViewController
olur. Bu bizim için bir sorun çıkarır, çünkü DetailViewController
içinde oluşturduğumuz özelliği ayarlamak istiyoruz. Çözüm: Sahip olduğu şeyin, sandığı şey olmadığını Swift'e söylememiz gerekiyor.
Bunun teknik terim karşılığı "typecasting"dir (tipleme): Yani Swift'ten, bir değere başka bir tipmiş gibi davranmasını istemek. Swift dili birçok yolla bunu yapabilir, ama biz en güvenli versiyonunu kullanacağız: Bu fiilen, "Lütfen bunu DetailViewController'mış gibi işle, ama eğer başarısız olursa, hiçbir şey yapma ve devam et." anlamına gelir.
Detay ekranı denetleyicisini bir kez ele aldığımızda, onun selectedImage
özelliğini pictures[indexPath.row]
eşit olacak şekilde ayarlayabiliriz; tam da cellForRowAt
içinde yaptığımız gibi. Bu iş, kolay olanıydı.
Üçüncü küçük adım, kendini gösterecek olan yeni bir ekran yapmak. Ekran denetleyicilerinin, çağırılacağı storyboard'u içereceği veya nil olacağı bir optional storyboard
özelliğine sahip olduklarını zaten görmüştünüz. İşte onlar aynı zamanda, eğer varsalar içlerinde olacakları bir kılavuz denetleyicisini, ama eğer yoksalar nil değerini içeren bir optional navigationController
özelliğine de sahiptirler.
Bu bizim için mükemmel bir şeydir, çünkü kılavuz denetleyicileri ekranların gösteriminden sorumludurlar. Tabi bir çok uygulamanın en üstünde görebileceğiniz hoş gri çubuğu da sağlarlar, ama onlar aynı zamanda kullanıcıların aralarında geçiş yapabileceği büyük bir ekran yığınının sağlanmasından da sorumludurlar.
Varsayılan olarak, storyboard'da onlar için oluşturduğunuz ilk ekran denetleyicisini barındırırlar, ama yeni ekranlar yaratılırken, Settings'de gördüğünüz gibi yumuşak bir şekilde kaydırılabilen kılavuz denetleyicisi yığınının üzerine onları sürebilirsiniz. Daha fazla ekran sürülmeye devam ederken, kaymaya devam ederler. Kullanıcılar bir ekran geri gittiklerinde –örneğin Back (Geri) butonuna basarak veya soldan sağa doğru kaydırarak– kılavuz denetleyicisi otomatik olarak eski ekran denetleyisini yok eder ve hafızada yer açar.
Bu üç küçük adım, yeni metodu tamamlar; o zaman kod yazma zamanı geldi. Lütfen ViewController.swift dosyasına şu metodu ekleyin; kolayca anlaşılabilmesi için yorumlar ekledim:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// 1: try loading the "Detail" view controller and typecasting it to be DetailViewController
if let vc = storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewController {
// 2: success! Set its selectedImage property
vc.selectedImage = pictures[indexPath.row]
// 3: now push it onto the navigation controller
navigationController?.pushViewController(vc, animated: true)
}
}
Bir an için, if let
satırına biraz daha yakından bakalım. Başarısız olma ihtimali olan üç parça içeriyor: storyboard
özelliği bir ihtimal nil olabilir (?
durumu, satırın geri kalanının çalıştırılmasını durduracaktır). instantiateViewController()
çağrısı başarısız olabilir, eğer biz “Fzzzzz” veya başka bir geçersiz storyboard ID'si istemişsek. Tipleme de –as?
kısmı– hata verebilir, çünkü geriye farklı tipte bir ekran denetleyicisi almış olabiliriz.
Dolayısıyla bu bir satırdaki üç şey, potansiyel olarak başarısızlık ihtimali taşıyor. Eğer tüm adımları doğru bir şekilde izlediyseniz, hiçbiri başarısız olmayacak, ama potansiyel olarak bu ihtimale sahipler. if let
yapısının akıllıca olduğu yer burası işte: Eğer bunların herhangi birisi nil değeri döndürürse (yani başarısız olursa), if let
parantezlerinin içindeki kod çalıştırılmayacak. Bu, herhangi bir eyleme geçmeden programınızın güvende olmasını garantiler.
Sonuçlara bir göz atmadan önce, geriye yapılması gereken sadece küçük bir şey kaldı: DetailViewController
içindeki görsel ekranına, görselin gerçekten yüklenmesini sağlamamız gerekiyor.
Bu yeni kod, UIImage
olarak adlandırılan yeni bir veri tipinden yararlanacak. Bu, UIImageView
in çağrıştırdığı gibi, isminde "View" barındırmıyor. Yani görüntüleyebileceğin bir şey değil; aslında kullanıcılara görünür olan birşey değil. UIImage
daha çok, PNG veya JPEG gibi görsel verileri yükleyebileceğiniz bir veri tipidir.
Bir UIImage
oluşturduğunuzda, yükleyeceğiniz görselin adını belirlemenizi sağlayan named
adında bir parametre alır. UIImage
, uygulamanızın içinde bu dosya adını arar ve onu yükler. Burada ViewController
dan gönderilen selectedImage
özelliğine aktararak, kullanıcı tarafından seçilen görseli yükler.
Yine de selectedImage
özelliğini doğrudan kullanamayız. Hatırlarsanız, aşağıdaki gibi onu oluşturduk:
var selectedImage: String?
Bu ?
, onun bir değer içerebileceği veya içermeyeceği anlamına gelir ve Swift dili, onları kontrol etmeden önce, bu "belkileri" kullanmanıza izin vermez. if let
için bu bir başka fırsattır: selectedImage
özelliğinin bir değere sahip olduğunu kontrol edebilir ve eğer sahipse, kullanım için onu çekebiliriz; aksi durumda ise, hiçbir şey yapmaz.
DetailViewController
içindeki viewDidLoad()
metoduna, super.viewDidLoad()
çağrısından sonra şu kodu ekleyin:
if let imageToLoad = selectedImage {
imageView.image = UIImage(named: imageToLoad)
}
İlk satır, selectedImage
içindeki seçeneği kontrol edip açar. Bir şekilde selectedImage
özelliği nil ise (teorik olarak asla öyle olmaması gerekiyor), o zaman imageView.image
satırı hiçbir zaman çalıştırılmayacaktır. Ama eğer bir değere sahipse, imageToLoad
içine yerleştirilecek, ardından UIImage
'e aktarılacak ve yüklenecektir.
Tamam, hepsi bu kadar: Uygulamayı çalıştırmak için Play butonuna veya Cmd+R tuşlarına basın şimdi ve deneyin bakalım! Kaydırılabilen görsellerin herhangi birini seçebilmeniz ve tam ekran görüntüleyebilmeniz gerekiyor.
Kılavuz çubuğunda, ViewController
'a geri dönmemizi sağlayan bir Back butonu olduğu dikkatinizi çekti mi? Dikkatli bir şekilde tıklayıp sürüklerseniz, ekranı kaydırma hareketini de oluşturduğunuzu göreceksiniz. Ekranın en sol köşesine tıklayıp, sağa çekin; tıpkı telefonunuzda parmaklarınızla yaptığınız gibi.
SAVE 50% All our books and bundles are half price for Black Friday, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.
Link copied to your pasteboard.