태그 보관물: uiview

uiview

뷰가 주어지면 어떻게 viewController를 얻습니까? [self superview]또 다른 UIView것은 아니지만

에 대한 포인터가 있습니다 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; \
})


답변

@andrey 는 한 줄로 답변합니다 ( Swift 4.1 에서 테스트 됨 ).

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

용법:

 let vc: UIViewController = view.parentViewController


답변

디버그 목적으로 만 _viewDelegate 뷰를 하여 뷰 컨트롤러를 가져올 수 있습니다. 이것은 개인 API이므로 App Store에는 안전하지 않지만 디버깅에는 유용합니다.

다른 유용한 방법 :

  • _viewControllerForAncestor-슈퍼 뷰 체인에서 뷰를 관리하는 첫 번째 컨트롤러를 가져옵니다. (감사합니다 n00neimp0rtant)
  • _rootAncestorViewController -현재 창에 뷰 계층이 설정된 조상 컨트롤러를 가져옵니다.

답변

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 코어 응답을 찾으려면 다음을 시도하십시오.

  1. 디버그에서 앱 실행
  2. 화면으로 이동
  3. 뷰 관리자 시작
  4. 찾고자하는보기를 가져 오십시오 (또는 더 나은보기).
  5. 오른쪽 창에서 주소를 얻습니다 (예 : 0x7fe523bd3000)
  6. 디버그 콘솔에서 명령 작성을 시작하십시오.
    po (UIView *) 0x7fe523bd3000
    po [(UIView *) 0x7fe523bd3000 next 응답자]
    po [[(UIView *) 0x7fe523bd3000 nextResponder] nextResponder]
    po [[[((UIView *) 0x7fe523bd3000 nextResponder] nextResponder] nextResponder]
    ...

대부분의 경우 UIView를 얻지 만 때때로 UIViewController 기반 클래스가 있습니다.