Добраться до UIViewController из UIView? - PullRequest
177 голосов
/ 27 августа 2009

Есть ли встроенный способ получить от UIView до UIViewController? Я знаю, что вы можете перейти от UIViewController к UIView через [self view], но мне было интересно, есть ли обратная ссылка?

Ответы [ 25 ]

5 голосов
/ 24 мая 2012

Не думаю, что это «плохая» идея выяснить, кто является контроллером представления в некоторых случаях. Что может быть плохой идеей, так это сохранить ссылку на этот контроллер, так как он может меняться так же, как меняются суперпредставления. В моем случае у меня есть геттер, который пересекает цепочку респондента.

//. Ч

@property (nonatomic, readonly) UIViewController * viewController;

// м.

- (UIViewController *)viewController
{
    for (UIResponder * nextResponder = self.nextResponder;
         nextResponder;
         nextResponder = nextResponder.nextResponder)
    {
        if ([nextResponder isKindOfClass:[UIViewController class]])
            return (UIViewController *)nextResponder;
    }

    // Not found
    NSLog(@"%@ doesn't seem to have a viewController". self);
    return nil;
}
4 голосов
/ 07 апреля 2014

Самый простой цикл do while для поиска viewController.

-(UIViewController*)viewController
{
    UIResponder *nextResponder =  self;

    do
    {
        nextResponder = [nextResponder nextResponder];

        if ([nextResponder isKindOfClass:[UIViewController class]])
            return (UIViewController*)nextResponder;

    } while (nextResponder != nil);

    return nil;
}
3 голосов
/ 15 апреля 2013

Это не дает прямого ответа на вопрос, а скорее делает предположение о цели вопроса.

Если у вас есть представление и в этом представлении вам нужно вызвать метод для другого объекта, например, скажем, контроллера представления, вы можете вместо этого использовать NSNotificationCenter.

Сначала создайте строку уведомления в заголовочном файле

#define SLCopyStringNotification @"ShaoloCopyStringNotification"

По вашему мнению звоните postNotificationName:

- (IBAction) copyString:(id)sender
{
    [[NSNotificationCenter defaultCenter] postNotificationName:SLCopyStringNotification object:nil];
}

Затем в вашем контроллере представления вы добавляете наблюдателя. Я делаю это в viewDidLoad

- (void)viewDidLoad
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(copyString:)
                                                 name:SLCopyStringNotification
                                               object:nil];
}

Теперь (также в том же контроллере представления) реализуйте ваш метод copyString: как показано в @selector выше.

- (IBAction) copyString:(id)sender
{
    CalculatorResult* result = (CalculatorResult*)[[PercentCalculator sharedInstance].arrayTableDS objectAtIndex:([self.viewTableResults indexPathForSelectedRow].row)];
    UIPasteboard *gpBoard = [UIPasteboard generalPasteboard];
    [gpBoard setString:result.stringResult];
}

Я не говорю, что это правильный способ сделать это, просто он выглядит чище, чем запуск цепочки первого респондента. Я использовал этот код для реализации UIMenuController в UITableView и передачи события обратно в UIViewController, чтобы я мог что-то сделать с данными.

3 голосов
/ 05 декабря 2018

Свифт 4

(более кратко, чем другие ответы)

fileprivate extension UIView {

  var firstViewController: UIViewController? {
    let firstViewController = sequence(first: self, next: { $0.next }).first(where: { $0 is UIViewController })
    return firstViewController as? UIViewController
  }

}

Мой вариант использования, для которого мне сначала нужно получить доступ к представлению UIViewController: у меня есть объект, который охватывает AVPlayer / AVPlayerViewController, и я хочу предоставить простой show(in view: UIView) метод, который будет вставлять AVPlayerViewController в view. Для этого мне нужно получить доступ view s UIViewController.

3 голосов
/ 09 августа 2018

Более быстрое решение

extension UIView {
    var parentViewController: UIViewController? {
        for responder in sequence(first: self, next: { $0.next }) {
            if let viewController = responder as? UIViewController {
                return viewController
            }
        }
        return nil
    }
}
3 голосов
/ 05 декабря 2015

Это, конечно, плохая идея и неправильный дизайн, но я уверен, что нам всем понравится решение Swift с лучшим ответом, предложенным @Phil_M:

static func firstAvailableUIViewController(fromResponder responder: UIResponder) -> UIViewController? {
    func traverseResponderChainForUIViewController(responder: UIResponder) -> UIViewController? {
        if let nextResponder = responder.nextResponder() {
            if let nextResp = nextResponder as? UIViewController {
                return nextResp
            } else {
                return traverseResponderChainForUIViewController(nextResponder)
            }
        }
        return nil
    }

    return traverseResponderChainForUIViewController(responder)
}

Если вы намереваетесь делать простые вещи, такие как показ модального диалога или отслеживание данных, это не оправдывает использование протокола. Я лично храню эту функцию в служебном объекте, вы можете использовать ее из всего, что реализует протокол UIResponder, как:

if let viewController = MyUtilityClass.firstAvailableUIViewController(self) {}

Все кредиты @ Phil_M

2 голосов
/ 22 июня 2018

Версия Swift 4

extension UIView {
var parentViewController: UIViewController? {
    var parentResponder: UIResponder? = self
    while parentResponder != nil {
        parentResponder = parentResponder!.next
        if let viewController = parentResponder as? UIViewController {
            return viewController
        }
    }
    return nil
}

Пример использования

 if let parent = self.view.parentViewController{

 }
2 голосов
/ 03 января 2018

Может быть, я опоздал сюда. Но в этой ситуации мне не нравится категория (загрязнение). Я люблю так:

#define UIViewParentController(__view) ({ \
UIResponder *__responder = __view; \
while ([__responder isKindOfClass:[UIView class]]) \
__responder = [__responder nextResponder]; \
(UIViewController *)__responder; \
})
1 голос
/ 04 марта 2018

Обновленная версия для swift 4: Спасибо за @Phil_M и @ paul-slm

static func firstAvailableUIViewController(fromResponder responder: UIResponder) -> UIViewController? {
    func traverseResponderChainForUIViewController(responder: UIResponder) -> UIViewController? {
        if let nextResponder = responder.next {
            if let nextResp = nextResponder as? UIViewController {
                return nextResp
            } else {
                return traverseResponderChainForUIViewController(responder: nextResponder)
            }
        }
        return nil
    }

    return traverseResponderChainForUIViewController(responder: responder)
}
1 голос
/ 04 июня 2013

К ответу Фила:

В строке: id nextResponder = [self nextResponder];, если self (UIView) не является подпредставлением представления ViewController, если вы знаете иерархию self (UIView), вы также можете использовать: id nextResponder = [[self superview] nextResponder]; ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...