에 대한 포인터가 있습니다 UIView
. 어떻게 액세스 UIViewController
합니까? [self superview]
또 다른 UIView
것은 아니지만 그렇지 UIViewController
않습니까?
에 대한 포인터가 있습니다 UIView
. 어떻게 액세스 UIViewController
합니까? [self superview]
또 다른 UIView
것은 아니지만 그렇지 UIViewController
않습니까?
답변:
예, superview
보기가 포함 된보기입니다. 뷰 컨트롤러가 MVC 원칙을 위반하기 때문에 뷰 컨트롤러가 정확히 무엇인지 알 수 없습니다.
반면에 컨트롤러는 어떤보기를 담당하는지 알고 있습니다 (self.view = myView
)를 일반적으로이 뷰는 컨트롤러에 처리 할 메소드 / 이벤트를 위임합니다.
일반적으로 뷰에 대한 포인터 대신 컨트롤러에 대한 포인터가 있어야 제어 컨트롤러를 실행하거나 뷰에 무언가를 전달할 수 있습니다.
의 UIResponder
설명서에서 nextResponder
:
UIResponder 클래스는 다음 응답자를 자동으로 저장하거나 설정하지 않고 기본적으로 nil을 반환합니다. 다음 응답자를 설정하려면 서브 클래스가이 메소드를 대체해야합니다. UIView는 관리하는 UIViewController 객체 (있는 경우) 또는 superview (없는 경우)를 반환하여이 메서드를 구현합니다 . UIViewController는 뷰의 슈퍼 뷰를 반환하여 메소드를 구현합니다. UIWindow는 응용 프로그램 객체를 반환하고 UIApplication은 nil을 반환합니다.
따라서보기 nextResponder
유형이 될 때까지 다시 보기를 반복하면UIViewController
뷰의 부모 viewController가 있습니다.
여전히 그렇지 않을 수 있습니다 상위 뷰 컨트롤러 . 그러나 뷰가 viewController의 뷰 계층 구조의 일부가 아닌 경우에만 가능합니다.
스위프트 3 및 스위프트 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
}
}
스위프트 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
}
}
목표 -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; \
})
UIResponder
;)로 만들어야합니다 . 매우 교묘 한 게시물.
@andrey 는 한 줄로 답변합니다 ( Swift 4.1 에서 테스트 됨 ).
extension UIResponder {
public var parentViewController: UIViewController? {
return next as? UIViewController ?? next?.parentViewController
}
}
용법:
let vc: UIViewController = view.parentViewController
parentViewController
public
확장 프로그램이 UIView
설정 한 파일과 동일한 파일에 있으면 정의 할 수 없으며 fileprivate
컴파일 할 수는 있지만 작동하지 않습니다! 😐
디버그 목적으로 만 _viewDelegate
뷰를 하여 뷰 컨트롤러를 가져올 수 있습니다. 이것은 개인 API이므로 App Store에는 안전하지 않지만 디버깅에는 유용합니다.
다른 유용한 방법 :
_viewControllerForAncestor
-슈퍼 뷰 체인에서 뷰를 관리하는 첫 번째 컨트롤러를 가져옵니다. (감사합니다 n00neimp0rtant)_rootAncestorViewController
-현재 창에 뷰 계층이 설정된 조상 컨트롤러를 가져옵니다._viewControllerForAncestor
뷰 컨트롤러에 속하는 첫 번째 뷰를 찾을 때까지 수퍼 뷰를 순회합니다.
UIView가있는 UIViewController에 대한 참조를 얻으려면 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
스위프트 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)
}
코드에 익숙하지 않고 주어진 뷰에 ViewController 코어 응답을 찾으려면 다음을 시도하십시오.
po (UIView *) 0x7fe523bd3000 po [(UIView *) 0x7fe523bd3000 next 응답자] po [[(UIView *) 0x7fe523bd3000 nextResponder] nextResponder] po [[[((UIView *) 0x7fe523bd3000 nextResponder] nextResponder] nextResponder] ...
대부분의 경우 UIView를 얻지 만 때때로 UIViewController 기반 클래스가 있습니다.
탭을 뷰 컨트롤러에 전파하여 처리 할 수 있다고 생각합니다. 이것은 더 수용 가능한 접근 방식입니다. 뷰에서 뷰 컨트롤러에 액세스하는 경우 다른 방법이 없으므로 뷰 컨트롤러에 대한 참조를 유지해야합니다. 이 스레드를 참조하십시오. 보기에서보기 컨트롤러에 액세스
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
}
}
중단 점을 설정하면이를 디버거에 붙여 넣어 뷰 계층을 인쇄 할 수 있습니다.
po [[UIWindow keyWindow] recursiveDescription]
그 엉망에서 어딘가에 뷰의 부모를 찾을 수 있어야합니다 :)
recursiveDescription
단지 인쇄 뷰 컨트롤러를 볼 수 없습니다, 계층 구조를.