Учитывая, как мне получить свой viewController?

У меня есть указатель на UIView. Как получить доступ к его UIViewController? [self superview] - это еще один UIView, но не UIViewController, правильно?

Ответ 1

Да, superview - это представление, содержащее ваше представление. Ваш взгляд не должен знать, что именно является его контроллером представления, потому что это нарушит принципы MVC.

Контроллер, с другой стороны, знает, на какой взгляд он отвечает за (self.view = myView), и обычно это представление делегирует методы/события для обработки контроллеру.

Как правило, вместо указателя на ваш вид вы должны иметь указатель на свой контроллер, который, в свою очередь, может выполнять какую-либо управляющую логику или передавать что-то своему представлению.

Ответ 2

Из UIResponder документации nextResponder:

Класс UIResponder не сохраняет и не устанавливает следующий респондент автоматически, вместо этого он возвращает nil по умолчанию. Подклассы должны переопределить этот метод для установки следующего респондента. UIView реализует этот метод, возвращая объект UIViewController, который управляет им (если он есть) или его суперпредставлением (если его нет); UIViewController реализует метод, возвращая его представления superview; UIWindow возвращает объект приложения, а UIApplication возвращает nil.

Таким образом, если вы просматриваете представления nextResponder до nextResponder пор, пока он не будет UIViewController тип UIViewController, то у вас есть любые представления родительского viewController.

Обратите внимание, что у него все еще может не быть родительского контроллера представления. Но только в том случае, если представление не является частью иерархии представлений представлений viewControllers.

Расширение Swift 3 и Swift 4.1:

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

Расширение Swift 2:

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

Objective-C категория:

@interface UIView (mxcl)
- (UIViewController *)parentViewController;
@end

@implementation UIView (mxcl)
- (UIViewController *)parentViewController {
    UIResponder *responder = self;
    while ([responder isKindOfClass:[UIView class]])
        responder = [responder nextResponder];
    return (UIViewController *)responder;
}
@end

Этот макрос избегает загрязнения категории:

#define UIViewParentController(__view) ({ \
    UIResponder *__responder = __view; \
    while ([__responder isKindOfClass:[UIView class]]) \
        __responder = [__responder nextResponder]; \
    (UIViewController *)__responder; \
})

Ответ 3

Только для целей отладки вы можете вызвать _viewDelegate в представлениях, чтобы получить свои контроллеры. Это частный API, поэтому он не безопасен для App Store, но для его отладки это полезно.

Другие полезные методы:

  • _viewControllerForAncestor - получить первый контроллер, который управляет вид в цепочке супервизора. (спасибо n00neimp0rtant)
  • _rootAncestorViewController - получить контроллер предка, чей в настоящее время установлена ​​иерархия представлений.

Ответ 4

@andrey ответ в одной строке (проверено в Swift 4.1):

extension UIResponder {
    public var parentViewController: UIViewController? {
        return next as? UIViewController ?? next?.parentViewController
    }
}

использование:

 let vc: UIViewController = view.parentViewController

Ответ 5

Чтобы получить ссылку на UIViewController, имеющую UIView, вы можете сделать расширение UIResponder (что является суперклассом для UIView и UIViewController), что позволяет перейти через цепочку ответчиков и, таким образом, достичь UIViewController (иначе возвращается nil).

extension UIResponder {
    func getParentViewController() -> UIViewController? {
        if self.nextResponder() is UIViewController {
            return self.nextResponder() as? UIViewController
        } else {
            if self.nextResponder() != nil {
                return (self.nextResponder()!).getParentViewController()
            }
            else {return nil}
        }
    }
}

//Swift 3
extension UIResponder {
    func getParentViewController() -> UIViewController? {
        if self.next is UIViewController {
            return self.next as? UIViewController
        } else {
            if self.next != nil {
                return (self.next!).getParentViewController()
            }
            else {return nil}
        }
    }
}

let vc = UIViewController()
let view = UIView()
vc.view.addSubview(view)
view.getParentViewController() //provide reference to vc

Ответ 6

Быстрый и общий способ в Swift 3:

extension UIResponder {
    func parentController<T: UIViewController>(of type: T.Type) -> T? {
        guard let next = self.next else {
            return nil
        }
        return (next as? T) ?? next.parentController(of: T.self)
    }
}

//Use:
class MyView: UIView {
    ...
    let parentController = self.parentController(of: MyViewController.self)
}

Ответ 7

Если вы не знакомы с кодом и хотите, чтобы ViewController соответствовал данному представлению, вы можете попробовать:

  • Запустить приложение в отладке
  • Перейдите к экрану
  • Инспектор запуска View
  • Возьмите вид, который хотите найти (или детское представление еще лучше).
  • На правой панели введите адрес (например, 0x7fe523bd3000)
  • В консоли отладки запускайте команды записи:
    po (UIView *)0x7fe523bd3000
    po [(UIView *)0x7fe523bd3000 nextResponder]
    po [[(UIView *)0x7fe523bd3000 nextResponder] nextResponder]
    po [[[(UIView *)0x7fe523bd3000 nextResponder] nextResponder] nextResponder]
    ...

В большинстве случаев вы получите UIView, но время от времени будет работать класс на основе UIViewController.

Ответ 8

Я думаю, что вы можете распространять кран на контроллер вида и позволить ему обрабатывать его. Это более приемлемый подход. Что касается доступа к контроллеру представления с его точки зрения, вы должны поддерживать ссылку на контроллер вида, поскольку нет другого способа. Смотрите эту тему, это может помочь: Доступ к диспетчеру просмотра из представления

Ответ 9

Более безопасный код типа для Swift 3.0

extension UIResponder {
    func owningViewController() -> UIViewController? {
        var nextResponser = self
        while let next = nextResponser.next {
            nextResponser = next
            if let vc = nextResponser as? UIViewController {
                return vc
            }
        }
        return nil
    }
}

Ответ 10

Увы, это невозможно, если вы не подклассифицируете представление и не предоставили ему свойство экземпляра или подобное, которое хранит ссылку на контроллер представления внутри него после добавления представления в сцену...

В большинстве случаев очень легко обойти оригинальную проблему этого сообщения, так как большинство контроллеров представления являются известными объектами программиста, который отвечал за добавление любых представлений в ViewController View;-) Вот почему я предположите, что Apple никогда не потрудилась добавить это свойство.

Ответ 11

Немного поздно, но вот расширение, которое позволяет вам найти ответчика любого типа, включая ViewController.

extension NSObject{
func findNext(type: AnyClass) -> Any{
    var resp = self as! UIResponder

    while !resp.isKind(of: type.self) && resp.next != nil
    {
        resp = resp.next!
    }

    return resp
  }                       
}

Ответ 12

Если вы установили точку останова, вы можете вставить ее в отладчик для печати иерархии представлений:

po [[UIWindow keyWindow] recursiveDescription]

Вы должны быть в состоянии найти своего родителя вида где-то в этом беспорядке:)