Site icon Mobilhanem

iOS Sayfalar (ViewController) arası veri aktarımı (Segue, Delegation, Closure)

ios dersleri

Merhaba Arkadaşlar,
mobilhanem.com da iOS Uygulama Geliştirme eğitim serimize kaldığımız yerden sayfalar arası veri aktarımı (Segue, Delegation, Closure) dersi ile devam ediyoruz. Bir önceki derste Xcode Auto Layout dersleri ve StackView kullanımı dersi ile Xcode düzen kısıtlamalarına detaylı bir şekilde baktık. Bu dersimizde çok ekranlı iOS uygulama geliştirirken sık sık karşılaşacağınız bir durum olan sayfalar arası veri aktarımını anlatacağım. Eğer uygulamanızın birden fazla ekranı varsa, bunları bir şekilde bağlamanız gerekiyorsa veya sayfalar arasında veri aktarımı yapmak istiyorsanız bu yazımız tam size göre. Sayfalar arası veri aktarımı yapmanın birden fazla yolu var o nedenle her birini sırasıyla anlatacağım. Uygulamanızın mimarisine uygun olanı seçip kullanmanız ve uygulamanın her yerinde seçtiğiniz aynı metodu kullanmanız bence kodu daha anlaşılır kılacaktır. Tabii ki birbirinden farklı olanları da seçip kullanabilirsiniz bunda bir engel yok ancak yapının benzer kurulması bakımının da kolay olmasını sağlayacaktır. Eğer hazırsanız başlayalım.

Projenin tamamlanmış halinin kodlarına buradan ulaşabilirsiniz.

Değişken Kullanarak Veri Aktarımı ( 1 -> 2 )

Birinci view controller’dan ikinci view controller’a değişken kullanarak istediğimiz verileri aktaracağız. Bildiğimiz üzere her sınıfın sahip olduğu değişkenler var, view controllerımız da aslında bir sınıf. Bu sınıfta tanımladığımız bir değişkeni kullanarak elimizdeki veriyi aktaracağız. Dilerseniz aşağıdaki kod parçasında ilk view controller’ı görebilirsiniz.

class FirstViewController: UIViewController {

    var textToTransfer: String = "Hello World!"

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func goToSecondVC(_ sender: Any) {
    }
}

İlk view controller’ın içerisinde aktarmak istediğimiz değişkeni ve buton aksiyonumuzu tanımladık. Birazdan goToSecondVC() metodunun içini dolduracağız. Ama öncesinde ikinci view controller’ımızı File -> New diyerek ve resimdeki gibi Also Create XIB file seçeceğini seçerek ikinci controller’ımızı oluşturalım.

Birinci controller’a tıkladığımızda bizi ikinci controller’a götürecek bir buton ve yukarıda yeni oluşturduğumuz ikinci controller’a birinciden aktardığımız veriyi göstereceğimiz bir label ekledim.

İlk controller’ın içindeki goToSecond() metodunun içine aşağıdaki kod parçasını ekleyelim.

@IBAction func goToSecondVC(_ sender: Any) {
        let secondVC = SecondViewController() // 1
        secondVC.transferredText = textToTransfer // 2
        present(secondVC, animated: true, completion: nil) // 3
    }
  1. Bu kod parçasında temel olarak ikinci controllerımızı oluşturup bunu secondVC adında bir değişkene atadık
  2. Ardından ikinci controllera göndermek istediğimiz text’i geçirdik
  3. Bu satırda ikinci controllerımızı birincinin üzerinde bir popup olarak açtık.

Dilerseniz ikinci controller’ın içini inceleyelim.

class SecondViewController: UIViewController {
    @IBOutlet weak var transferredTextLabel: UILabel! // 1
    var transferredText: String = “” // 1
    override func viewDidLoad() {
        super.viewDidLoad()
        transferredTextLabel.text = transferredText // 2
    }
}
  1. İkinci controller’ın içerisinde bir UILabel ve aktardığımız verinin değişkenini boş olarak tanımladık
  2. Oluşturduğumuz UILabel’a viewDidLoad metodu içerisinde değişkenimizi verdik.

Not 1:

Aklınızda şöyle bir soru oluşmuş olabilir. Neden goToSecondVC() aksiyonu içinde direk olarak transferredTextLabel’a atama yapmadık? Bu sorunun cevabını ise şöyle açıklayabilirim ilerideki derslerimizde değineceğimiz iOS yaşam döngüsünde UILabel, UITextField gibi UI elementleri present() metodunu çağırdığımız anda henüz oluşturulmamış durumda. Bu nedenle present() i çağırdığımız yerde UI elementlerinden önce henüz oluşmuş transferredText değişkenine atama yapıyoruz ardından bu değişkeni UI elementimizin oluştuğu viewDidLoad() metodu içinde transferredTextLabel’ımıza atıyoruz.

Not 2:

Eğer aktacağınız veri buradaki gibi bir tane değil de birden fazlaysa mesela 10 taneyse bu verileri Swift derslerinde anlatılan struct, class  kullanıp 10 veriyi ayrı ayrı iletmek yerine oluşturduğunuz struct ya da class ı ikinci view controller içerisine gönderip veriyi direk struct aracılığıyla kullanmanız daha doğru ve kolay olacaktır.

Gifte göreceğiniz gibi ilk controller’daki veriyi ikinciye aktardık.

Segue kullanarak veri aktarımı ( 1 -> 2 )

Eğer storyboard kullanarak uygulamanızı geliştiriyorsanız ve controller’lar arasında veri aktarımı yapmak istiyorsanız kullanabileceğiniz bir diğer yöntem segue. Biraz önce oluşturduğumuz XIB ve storyboard arasında pek fazla farklılık yok. Bu yöntem için yeni bir controller oluşturacağım ve bunun da adı DetailViewController olacak. Hadi şimdi gelin segue kullanarak verimizi nasıl aktaracağız görelim.

  1.  İlk önce storyboard’a yeni bir view controller sürükleyelim. Örnek projede de göreceğiniz üzere ilk controller üzerinde segue adında yeni bir buton ve bu butona ait IBAction tanımlayalım.
  2. Ardından aşağıda da gördüğünüz gibi butona basıp control tuşuna basılı tutarak yeni oluşturduğumuz controller’a sürükleyip show seçeneğini işaretleyelim

Uygulamayı derleyip çalıştırdığınızda segue butonuna tıklayınca DetailViewController’a gittiğimizi göreceksiniz. (DetailViewController’ın içinde herhangi bir değişiklik yapmanıza gerek yok.) Sayfalar arasında veriyi nasıl aktaracağımıza gelirsek iOS SDK’sı bize prepare(for:sender:) isminde bir metod tanımlamış. Bu metodu aşağıdaki gibi birinci view contoller’ın içinde tanımlayalım.

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let detailVC = segue.destination as? DetailViewController { // 1
            detailVC.email = "atalayasa@windowslive.com"               // 2
        }
    }
  1. İlk olarak varış noktamız olan DetailViewController’ı güvenli bir şekilde sarıyoruz.
  2. İletmek istediğimiz veriyi detailVC içerisindeki veriye gönderiyoruz.

Kimi durumlarda bir sayfada birden fazla segue olabilir. Bu tip durumlarda aşağıdaki gibi segue’ye tıklayıp bir tanımlayıcı vermemiz gerekiyor. Biz bu örnekte “detailSegue” adını veriyoruz.

Bu tip durumlarda prepare(for:sender:) metodumuza ufak bir if else ekliyoruz.

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "detailSegue" {
            if let detailVC = segue.destination as? DetailViewController {
               detailVC.email = "atalayasa@windowslive.com"
            }
        }
    }

Bu sayede sayfamızda birden fazla sayıda segue tanımlamış olsak da onları birbirinden ayırt edebiliriz.

Delegation kullanarak geriye veri aktarımı ( 1 <- 2 )

Delegation iOS uygulama geliştirmede çok önemli ve bir o kadar da sık kullanılan bir tasarım desenidir. Öncelikle delegate nedir onu gerçek hayattan bir örnekle açıklayalım.

Örneğin iki ebeveyn akşam yemeğinde dışarı çıkacaklar ve akşam çocuklarına bakması için bir bebek bakıcısı tutuyorlar. Peki bebek hasta olduğunda ne olacak? Ebeveynler buzdolabının üzerine ‘Acil bir durumda Ayşe’yi 555-555-55-55 numarasından arayın’ notunu bırakmışlar. Burada ebeveynler Ayşe’yi delege olarak yetkilendirmişlerdir.

İşte birazdan örneğini göstereceğim iOS Delegation kullanarak ikinci view controller’dan birinciye istediğiniz veriyi taşıyıp çeşitli işlemler yapabilirsiniz. Buradan sonrası için Swift dilindeki protocol kavramı hakkında bilgi sahibi olmanız gerekiyor. Eğer okumadıysanız Swift protocol dersine buradan ulaşabilirsiniz.

Öncelikle uygulamamızda aşağıdaki kod parçacığındaki gibi DataTransferable adında bir protocol oluşturalım.

protocol DataTransferable {
    func onEmergencyStatus(phoneNumber: String) 
}.

Ardından birinci view controller’ımızın protokole uymasını sağlıyoruz. Bunu extension ile yapıyoruz.  Extension nedir diyorsanız buyurun yazımıza 🙂

extension FirstViewController: DataTransferable {
    func onEmergencyStatus(phoneNumber: String) {
        print("Acil bir durumda \(phoneNumber) arayın.")
    }
}

Ardından ikinci view controller’ın içine resimde gördüğünüz gibi acil durum numarasının yazılı olduğu bir buton, bu butonun aksiyonunu, biraz önce oluşturduğumuz protokolün emergencyDelegate adında değişkenini tanımladım. Ve birinci view controller’dan bir segue oluşturalım ve bu segue’nin tanımlayıcısını delegationSegue yapalım.

İkinci view controller içindeki prepare metodu içerisine

 if segue.identifier == "delegationSegue" {
            if let detailVC = segue.destination as? DetailViewController {
                detailVC.emergencyDelegate = self // 1
            }
        }
  1. Birinci view controller ve ikinci view controller arasında delegation’u bağladık.
    @IBAction func emergencyBtnPressed(_ sender: UIButton) {
        dismiss(animated: true) {
            self.emergencyDelegate?.onEmergencyStatus(phoneNumber: sender.titleLabel!.text!)
        }
    }

Yukarıdaki tanımlı ikinci view controller içindeki metod sayesinde üzerinde numara yazılı butona tıklandığı anda tanımladığımız protokol metodu olan onEmergencyStatus çalışacak ve birinci view controller’a buton üzerinde yazılı olan acil durum numarasını print edeceğiz. Gelin hep beraber deneyelim.

Closure kullanarak geriye veri aktarımı ( 1 <- 2 )

Closure kullanarak önceki sayfaya veri aktarmanın delegation’dan pek bir farkı yok. Ancak bunun en büyük avantajı herhangi bir protokol oluşturmaya, o protokole uymaya ya da ekstra bir metod yazmanıza gerek olmaması. Eğer closure konusuna hakim değilseniz daha önce mobilhanem.com da bulunan Swift derslerinden konuyla ilgili yazıyı okumanızı tavsiye ederim.

İkinci view controller’a aşağıdaki gibi bir closure tanımlayarak başlayalım.

var dataTransferClosure: ((String) -> Void)?

Yukarıdaki closure’u tanımladık. Bu closure String tipinde tek parametreye sahip ve herhangi bir geri dönüş tipi yok. Detail view controller içindeki butona aşağıdaki eklemeleri yapalım.

    @IBAction func emergencyBtnPressed(_ sender: UIButton) {
        dismiss(animated: true) { [weak self] in                 // 1
            self?.dataTransferClosure?(sender.titleLabel!.text!) // 2
        }
    }
  1. [weak self] daha ileri düzey bir konu olan Bellek Sızıntısı (Memory Leak) ile alakalı başka bir dersin konusu closure kullanırken bunu eklemeniz bellek kullanımı açısından önemli.
  2. Burada acil durum numaramızın yazılı olduğu butonun içindeki String değeriniz closure kullanarak geriye döndürüyoruz.

Birinci view controller’a öncekilerde yaptığımız gibi bir buton, acil durum numarasının yazacağı bir label ve bu butona ait bir segue ekledim. Kodun içindeki prepare(for:sender:) metoduna yeni bir if bloğu ekledim.

 if segue.identifier == "closureSegue" {
            if let detailVC = segue.destination as? DetailViewController {
                detailVC.dataTransferClosure = { [weak self] phoneNumber in
                    self?.transferredDataLbl.text = phoneNumber
                }
            }
        }

Detail View Controller dismiss edildiği anda dataTransferClosure tetiklenecek buradan gelen değerle ve transferredDataLbl’ın içini dolduracak.

En başta da dediğimiz gibi veri aktarımı için closure kullanarak protokol vs. oluşturmanıza gerek yok.

Closure kullanarak veri aktarımını eğer gereğinden fazla yaparsanız kodu anlaşılması güç hale de getirebilirsiniz. Eğer closure kullanımı yukarıda anlattığım diğer veri taşıma yollarından daha mantıklı gelirse kullanın. Sadece daha kolay olduğu için bu yolu seçmemenizi öneririm.

Projenin tamamlanmış halinin kodlarına buradan ulaşabilirsiniz.

Bu yazıda mobilhanem.com da iOS Uygulama Geliştirme eğitim serisinin Sayfalar arası veri aktarımı (Segue, Delegation, Closure) dersini işledik.  Sayfalar arası veri aktarımı çok sayfalı uygulamalarınızda sık sık kullanacağınız önemli bir konsept. Bu konseptlerden kendinize en uygun olanını seçip onu kullanmanızı tavsiye ediyorum. Bir yanlış ya da eksik görürseniz yorumlar kısmına ya da buradan soru cevap kısmına yazabilirsiniz. Diğer derste görüşmek üzere.

14
Exit mobile version