Доступ до контролера перегляду контейнерів з батьківського iOS


203

в iOS6 я помітив новий Container View, але не зовсім впевнений, як отримати доступ до його контролера з перегляду, що містить.

Сценарій:

приклад

Я хочу отримати доступ до міток у контролері подання сповіщень від контролера перегляду, в якому розміщено подання контейнера.

Між ними є смуток, чи можу я це використати?


повністю пояснено тут, для сучасних переглядів контейнерів: stackoverflow.com/a/23403979/294884
Fattie

Відповіді:


362

Так, ви можете використовувати segue, щоб отримати доступ до контролера дитячого перегляду (та його перегляду та підвідгляду). Дайте segue ідентифікатор (наприклад alertview_embed), використовуючи інспектор Attributes у Storyboard. Тоді попросіть батьківський контролер подання (той, у якому розміщено вигляд контейнера), реалізувати такий метод:

- (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
ми не переслідуємо? я щось тут пропускаю ...?
Адам Уайт

25
так, існує вбудований вираз, який виникає, коли другий контролер подання є дочірнім контролером першого перегляду. PrepaForSegue: викликається безпосередньо перед цим. ви можете скористатися цією можливістю для передачі даних дитині або для збереження посилання на дитину для подальшого використання. дивіться також developer.apple.com/library/ios/#documentation/uikit/reference/…
Пітер Е

1
Ну правильно, чи "контролер другого перегляду робиться дочірнім контролером першого виду", коли подання завантажується? Зараз це має більше сенсу, дякую. Я зараз не зі своїм проектом, але протестую пізніше
Адам Уайт

1
саме він називається перед viewDidLoad. До того часу, як буде досягнуто viewDidLoad, батько та дитина були підключені, і [self childViewControllers] у батьків буде повернути масив усіх дочірніх контролерів (див. Відповідь rdelmar нижче).
Пітер Е

2
Я б додав одне застереження до запропонованого рішення: будьте дуже обережні при доступі до властивості подання диспетчера подання (дочірні): в деяких випадках це призведе до виклику його viewDidLoad туди і потім. Я рекомендую заздалегідь налаштувати будь-які необхідні дані виразки. так що viewDidLoad може безпечно стріляти.
Завжди слухання

56

Це можна зробити просто за допомогою self.childViewControllers.lastObject(якщо у вас є тільки одна дитина, інакше використовуйте objectAtIndex:).


1
@RaphaelOliveira, не обов'язково. Якщо у вас є кілька дочірніх контролерів в одному огляді, ЦЕ буде кращим підходом. Це дозволяє вам координувати кілька контейнерів одночасно. PrepaForSegue має посилання лише на один екземпляр дочірного контролера, на який він діє.
Фідо

2
@Fydo, а яка проблема з обробкою всіх декількох контейнерів на "Підготуватися до ядра"?
Закладіть Гонсалес

1
Що робити, якщо (жах!) Ви вирішили переключитися з розкадрів чи не використовувати послідовності тощо. Тоді вам доведеться викопати код, внести зміни тощо.
Том Андерсен

2
Це мій звичайний підхід, але він зараз виходить з ладу для мене, оскільки я отримую доступ до childViewControllers"занадто рано"
Мазюд

24

для програмування Swift

можна писати так

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
    }
}

Яке використання знака питання після segueName в операторі if? "якщо segueName?"
Преосвященний

18

prepareForSegueПідхід працює, але він покладається на рядок магічного ідентифікатора Segue. Можливо, є кращий спосіб.

Якщо ви знаєте клас VC, який ви шукаєте, ви можете зробити це дуже акуратно за допомогою обчисленої властивості:

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

Це спирається на childViewControllers. Хоча я погоджуюся, що можна покластися на перший, але називання класу, який ви шукаєте, здається цілком солідним.


3
return childViewControllers.filter { $0 is CamperVanViewController }.firstв одному лайнері
Адам Уейт

1
Я з тих пір зробив, childViewControllers.flatMap({ $0 as? CamperVanViewController }).firstщо, на мою думку, трохи краще, оскільки він кидає і позбавляється від будь-яких нулів.
SimplGy

Це дійсно гарне рішення, якщо ви хочете отримати доступ до цього контролера перегляду не один раз
Габріель Гонкалвес,

це безперспективно - немає жодної конкретної причини, якщо у вас може бути лише один із цього класу. саме тому існують ідентифікатори. просто дотримуйтесь стандартної формули ... stackoverflow.com/a/23403979/294884
Fattie

не фільтруйте лише для того, щоб взяти перший елемент. просто використовувати first(where:). childViewControllers.first(where: { $0 is CamperVanViewController })
Олександр - Відновіть Моніку

9

Оновлена ​​відповідь для Swift 3 з використанням обчисленої властивості:

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

Це лише повторює список дітей, поки не досягне першого матчу.


8

self.childViewControllers є більш актуальним, коли вам потрібен контроль з боку батьків. Наприклад, якщо дочірний контролер є поданням таблиці, і ви хочете його перезавантажити насильно або змінити властивість за допомогою натискання кнопки чи будь-якої іншої події на контролері батьківського перегляду, ви можете це зробити, звернувшись до екземпляра ChildViewController, а не через PrepaForSegue. Обидва мають свої додатки по-різному.


2

Існує ще один спосіб використання оператора перемикання Swift щодо типу контролера перегляду:

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

Я використовую код як:

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

1

Якщо хтось шукає Swift 3.0 ,

viewController1 , viewController2 і так далі будуть доступні.

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

З загальним ти можеш робити кілька солодких речей. Ось розширення до масиву:

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

Потім ви можете зробити це у своєму представленні Контролер:

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

0

можна писати так

- (IBAction)showDetail:(UIButton *)sender {  
            DetailViewController *detailVc = [self.childViewControllers firstObject];  
        detailVc.lable.text = sender.titleLabel.text;  
    }  
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.