Greifen Sie über das übergeordnete iOS auf den Container View Controller zu

203

In iOS6 ist mir die neue Containeransicht aufgefallen, ich bin mir jedoch nicht ganz sicher, wie ich über die enthaltene Ansicht auf den Controller zugreifen soll.

Szenario:

Beispiel

Ich möchte über den Ansichtscontroller, in dem sich die Containeransicht befindet, auf die Beschriftungen im Alert View Controller zugreifen.

Es gibt einen Übergang zwischen ihnen, kann ich das nutzen?

Adam Waite
quelle
Hier vollständig erklärt, für moderne Containeransichten: stackoverflow.com/a/23403979/294884
Fattie

Antworten:

362

Ja, Sie können den Abschnitt verwenden, um Zugriff auf den untergeordneten Ansichtscontroller (und seine Ansicht und Unteransichten) zu erhalten. Geben Sie dem Segue alertview_embedmithilfe des Attributinspektors im Storyboard eine Kennung (z. B. ). Lassen Sie dann den übergeordneten Ansichtscontroller (der die Containeransicht enthält) eine Methode wie die folgende implementieren:

- (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...
   }
}
Peter E.
quelle
1
wir seegieren nicht? vermisse ich hier etwas ...?
Adam Waite
25
Ja, es gibt einen Einbettungsabschnitt, der auftritt, wenn der zweite Ansichtscontroller dem ersten Ansichtscontroller untergeordnet wird. prepareForSegue: wird aufgerufen, bevor dies geschieht. Sie können diese Gelegenheit nutzen, um Daten an das Kind zu übergeben oder einen Verweis auf das Kind zur späteren Verwendung zu speichern. Siehe auch developer.apple.com/library/ios/#documentation/uikit/reference/…
Peter E
1
Ah, richtig, wird der zweite Ansichts-Controller beim Laden der Ansicht zum untergeordneten Element des ersten Ansichts-Controllers? Das macht jetzt mehr Sinn, danke. Ich bin jetzt nicht mit meinem Projekt, werde es aber später testen
Adam Waite
1
genau, es wird vor viewDidLoad aufgerufen. Wenn viewDidLoad erreicht ist, sind Eltern und Kind verbunden, und [self childViewControllers] im übergeordneten Element gibt ein Array aller untergeordneten Controller zurück (siehe Antwort von rdelmar unten).
Peter E
2
Ich würde der vorgeschlagenen Lösung eine Einschränkung hinzufügen: Seien Sie sehr vorsichtig, wenn Sie auf die Ansichtseigenschaft des (untergeordneten) Zielansichts-Controllers zugreifen: Unter bestimmten Umständen wird dessen viewDidLoad dort und dann aufgerufen. Ich würde empfehlen, alle erforderlichen Segue-Daten im Voraus einzurichten damit viewDidLoad sicher ausgelöst werden kann.
AlwaysLearning
56

Sie können dies einfach mit tun self.childViewControllers.lastObject(vorausgesetzt, Sie haben nur ein Kind, andernfalls verwenden objectAtIndex:).

rdelmar
quelle
1
@ RaphaelOliveira, nicht unbedingt. Wenn Sie mehrere childController in einer einzigen Ansicht haben, ist DIES der bevorzugte Ansatz. Damit können Sie mehrere Container gleichzeitig koordinieren. prepareForSegue verweist nur auf die einzelne untergeordnete Controller-Instanz, auf die es wirkt.
Fydo
2
@Fydo, und was ist das Problem bei der Handhabung aller mehreren Container auf der Seite "Vorbereitung auf den Übergang"?
Lay González
1
Was ist, wenn Sie (Horror!) Entscheiden, vom Storyboard zu wechseln oder keine Sequenzen usw. zu verwenden? Dann müssen Sie den Code ausgraben, Änderungen vornehmen usw.
Tom Andersen
2
Dies ist mein üblicher Ansatz, aber er stürzt jetzt für mich ab, da ich auf den childViewControllers"zu früh"
Mazyod
24

für die schnelle Programmierung

du kannst so schreiben

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
    }
}
Obst A.Suk
quelle
Was nützt das Fragezeichen nach segueName in der if-Anweisung? "wenn segueName?"
Der Reverend
18

Der prepareForSegueAnsatz funktioniert, basiert jedoch auf der magischen Zeichenfolge der Segue-Kennung. Vielleicht gibt es einen besseren Weg.

Wenn Sie die Klasse des VC kennen, nach dem Sie suchen, können Sie dies mit einer berechneten Eigenschaft sehr gut tun:

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

Dies hängt davon ab childViewControllers. Obwohl ich der Meinung bin, dass es fragil sein könnte, sich auf die erste zu verlassen, lässt die Benennung der Klasse, die Sie suchen, dies ziemlich solide erscheinen.

Einfach
quelle
3
return childViewControllers.filter { $0 is CamperVanViewController }.firstin einem Einzeiler
Adam Waite
1
Ich habe seitdem getan, childViewControllers.flatMap({ $0 as? CamperVanViewController }).firstwas ich für ein bisschen besser halte, da es keine Nullen wirft und beseitigt.
Einfach
Dies ist eine wirklich gute Lösung, wenn Sie mehr als einmal auf diesen View Controller zugreifen möchten
Gabriel Goncalves
Das ist hoffnungslos - es gibt keinen bestimmten Grund, warum Sie möglicherweise nur eine dieser bestimmten Klassen haben. Genau deshalb gibt es Bezeichner. Folgen Sie
Fattie
Filtern Sie nicht nur, um das erste Element zu übernehmen. einfach benutzen first(where:). childViewControllers.first(where: { $0 is CamperVanViewController })
Alexander - Reinstate Monica
9

Eine aktualisierte Antwort für Swift 3 unter Verwendung einer berechneten Eigenschaft:

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

Dadurch wird nur die Liste der untergeordneten Elemente wiederholt, bis die erste Übereinstimmung erreicht ist.

Robin Daugherty
quelle
8

self.childViewControllers ist relevanter, wenn Sie die Kontrolle des Elternteils benötigen. Wenn es sich bei dem untergeordneten Controller beispielsweise um eine Tabellenansicht handelt und Sie sie zwangsweise neu laden oder eine Eigenschaft über ein Tastendruck oder ein anderes Ereignis auf dem übergeordneten View Controller ändern möchten, können Sie dies tun, indem Sie auf die Instanz von ChildViewController zugreifen und nicht über prepareForSegue. Beide haben ihre Anwendungen auf unterschiedliche Weise.

Gautam Jain
quelle
2

Es gibt eine andere Möglichkeit, die switch-Anweisung von Swift für den Typ des View-Controllers zu verwenden:

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
  }
}
Joanna Carter
quelle
1

Ich benutze Code wie:

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

Falls jemand nach Swift 3.0 sucht ,

Auf viewController1 , viewController2 usw. kann dann zugegriffen werden.

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")
}
Marco Leong
quelle
1

Mit Generika können Sie einige süße Dinge tun. Hier ist eine Erweiterung von Array:

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

Sie können dies dann in Ihrem viewController tun:

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

du kannst so schreiben

- (IBAction)showDetail:(UIButton *)sender {  
            DetailViewController *detailVc = [self.childViewControllers firstObject];  
        detailVc.lable.text = sender.titleLabel.text;  
    }  
}
Khurshid
quelle