Üst iOS'tan Container View Controller'a erişin


203

iOS6'da yeni Kapsayıcı Görünümü'nü fark ettim, ancak denetleyicisini içeren görünümden nasıl erişeceğinden emin değilim.

Senaryo:

misal

Uyarı görünümü denetleyicisindeki etiketlere kapsayıcı görünümünü barındıran görünüm denetleyicisinden erişmek istiyorum.

Aralarında bir segue var, kullanabilir miyim?


modern konteyner görünümleri için burada tamamen açıklanmıştır: stackoverflow.com/a/23403979/294884
Fattie

Yanıtlar:


362

Evet, segue'i alt görünüm denetleyicisine (ve görünümüne ve alt görünümlerine) erişmek için kullanabilirsiniz. alertview_embedStoryboard'daki Öznitelikler denetçisini kullanarak segue'e bir tanımlayıcı (örneğin ) verin . Ardından, üst görünüm denetleyicisine (kapsayıcı görünümünü barındıran) aşağıdaki gibi bir yöntem uygulayın:

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
   NSString * segueName = segue.identifier;
   if ([segueName isEqualToString: @"alertview_embed"]) {
       AlertViewController * childViewController = (AlertViewController *) [segue destinationViewController];
       AlertView * alertView = childViewController.view;
       // do something with the AlertView's subviews here...
   }
}

1
biz selamlamıyoruz? burada bir şey mi kaçırıyorum ...?
Adam Waite

25
evet, ikinci görüntü denetleyicisi ilk görüntü denetleyicisinin alt öğesi haline getirildiğinde oluşan bir yerleştirme sekmesi vardır. preparForSegue: bu gerçekleşmeden hemen önce çağrılır. bu fırsatı çocuğa veri aktarmak veya daha sonra kullanmak üzere çocuğa bir referans depolamak için kullanabilirsiniz. ayrıca bkz. developer.apple.com/library/ios/#documentation/uikit/reference/…
Peter E

1
Doğru, görünüm yüklendiğinde 'ikinci görünüm denetleyicisi ilk görünüm denetleyicisinin alt öğesi haline getiriliyor mu? Bu şimdi daha mantıklı, teşekkürler. Projemle birlikte değilim ama daha sonra test edeceğim
Adam Waite

1
tam olarak, viewDidLoad'dan önce çağrılır. ViewDidLoad öğesine ulaşıldığında, üst öğe ve alt öğe bağlanır ve üst öğedeki [self childViewControllers] tüm alt denetleyicilerden oluşan bir dizi döndürür (aşağıdaki rdelmar'ın cevabına bakın).
Peter E

2
Önerilen çözüme bir uyarı ekleyeceğim: (alt) hedef görünüm denetleyicisinin view özelliğine erişirken çok dikkatli olun: bazı durumlarda bu, viewDidLoad'ın orada çağrılmasına neden olur ve daha sonra gerekli segue verilerinin önceden ayarlanmasını öneririm böylece viewDidLoad güvenle ateş edebilir.
AlwaysLearning

56

Bunu sadece ile yapabilirsiniz self.childViewControllers.lastObject(sadece bir çocuğunuz olduğunu varsayarsak, aksi takdirde kullanın objectAtIndex:).


1
@RaphaelOliveira, illa ki değil. Tek bir görünümde birden fazla childController'ınız varsa, bu tercih edilen yaklaşım olacaktır. Birden fazla kabı aynı anda koordine etmenizi sağlar. preparForSegue yalnızca üzerinde işlem yaptığı tek alt denetleyici örneğine başvuruyor.
Fydo

2
@Fydo, ve 'segue için hazırlanın' üzerindeki birden fazla konteynerin tamamını ele alma problemi nedir?
Lay González

1
Ne if (! Dehşeti) Eğer Storyboard'dan anahtarı karar ya da değil Sonra kod yapmak değişiklikleri, vb kazıp zorunda vb seques kullanmak
Tom Andersen

2
Bu benim her zamanki yaklaşımım, ama childViewControllers"çok erken" eriştiğim için şimdi benim için çöküyor
Mazyod

24

Hızlı Programlama için

böyle yazabilirsin

var containerViewController: ExampleViewController?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    // you can set this name in 'segue.embed' in storyboard
    if segue.identifier == "checkinPopupIdentifierInStoryBoard" {
        let connectContainerViewController = segue.destinationViewController as ExampleViewController
        containerViewController = connectContainerViewController
    }
}

İf ifadesinde segueName'den sonra soru işareti için ne kullanılır? "segueName ise?"
Aziz

19

prepareForSegueYaklaşım çalışır, ancak segue tanımlayıcı sihirli dize dayanır. Belki daha iyi bir yol var.

Eğer peşinde olduğunuz VC sınıfını biliyorsanız, bunu bir computed özelliği ile çok düzgün bir şekilde yapabilirsiniz:

var camperVan: CamperVanViewController? {
  return childViewControllers.flatMap({ $0 as? CamperVanViewController }).first
  // This works because `flatMap` removes nils
}

Bu güveniyor childViewControllers. Birincisine güvenmenin kırılgan olabileceğini kabul etsem de, aradığınız sınıfı adlandırmak bunu oldukça sağlam gösteriyor.


3
return childViewControllers.filter { $0 is CamperVanViewController }.firstbir astarda
Adam Waite

1
O zamandan beri yaptım childViewControllers.flatMap({ $0 as? CamperVanViewController }).firstki daha iyi olduğunu düşünüyorum, çünkü herhangi bir nil döküyor ve kurtuluyor.
SimplGy

Bu görünüm denetleyicisine birden fazla kez erişmek istiyorsanız bu gerçekten iyi bir çözümdür
Gabriel Goncalves

bu umutsuzdur - o sınıftan sadece birine sahip olmanız için özel bir neden yoktur. tam olarak bu yüzden tanımlayıcılar vardır. sadece standart formülü takip edin ... stackoverflow.com/a/23403979/294884
Fattie

sadece ilk elemanı almak için filtreleme yapmayın. sadece kullanın first(where:). childViewControllers.first(where: { $0 is CamperVanViewController })
Alexander - Monica'yı eski

9

Hesaplanan bir özelliği kullanan Swift 3 için güncellenmiş bir cevap:

var jobSummaryViewController: JobSummaryViewController {
    get {
        let ctrl = childViewControllers.first(where: { $0 is JobSummaryViewController })
        return ctrl as! JobSummaryViewController
    }
}

Bu sadece ilk maça ulaşana kadar çocukların listesini tekrarlar.


8

self.childViewControllers üst öğeden denetime ihtiyacınız olduğunda daha alakalı olur. Örneğin, alt denetleyici bir tablo görünümündeyse ve zorla yeniden yüklemek ya da bir düğme dokunuşu veya Ebeveyn Görünümü Denetleyicisindeki herhangi bir olay aracılığıyla bir özelliği değiştirmek istiyorsanız, bunu ReadyForSegue yoluyla değil, ChildViewController'in örneğine erişerek yapabilirsiniz. Her ikisinin de uygulamaları farklı şekillerde var.


2

Görünüm denetleyicisinin türünde Swift'in switch deyimini kullanmanın başka bir yolu daha vardır:

override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
  switch segue.destination
  {
    case let aViewController as AViewController:
      self.aViewController = aViewController
    case let bViewController as BViewController:
      self.bViewController = bViewController
    default:
      return
  }
}

1

Kod gibi kullanın:

- (IBAction)showCartItems:(id)sender{ 
  ListOfCartItemsViewController *listOfItemsVC=[self.storyboard instantiateViewControllerWithIdentifier:@"ListOfCartItemsViewController"];
  [self addChildViewController:listOfItemsVC];
 }

1

Birisinin Swift 3.0'ı araması durumunda ,

viewController1 , viewController2 vb. daha sonra erişilebilir olacaktır.

let viewController1 : OneViewController!
let viewController2 : TwoViewController!

// Safety handling of optional String
if let identifier: String = segue.identifier {

    switch identifier {

    case "segueName1":
        viewController1 = segue.destination as! OneViewController
        break

    case "segueName2":
        viewController2 = segue.destination as! TwoViewController
        break

    // ... More cases can be inserted here ...

    default:
        // A new segue is added in the storyboard but not yet including in this switch
        print("A case missing for segue identifier: \(identifier)")
        break
    }

} else {
    // Either the segue or the identifier is inaccessible 
    print("WARNING: identifier in segue is not accessible")
}

1

Jenerik ile bazı tatlı şeyler yapabilirsiniz. İşte Array uzantısı:

extension Array {
    func firstMatchingType<Type>() -> Type? {
        return first(where: { $0 is Type }) as? Type
    }
}

Daha sonra bunu viewController'ınızda yapabilirsiniz:

var viewControllerInContainer: YourViewControllerClass? {
    return childViewControllers.firstMatchingType()!
}

0

böyle yazabilirsin

- (IBAction)showDetail:(UIButton *)sender {  
            DetailViewController *detailVc = [self.childViewControllers firstObject];  
        detailVc.lable.text = sender.titleLabel.text;  
    }  
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.