[ios] UIView에서 UIViewController에 접근 하시겠습니까?

UIView에서 그것 으로 갈 수있는 내장 된 방법 이 UIViewController있습니까? 나는 당신이에서 얻을 수 알고 UIViewController그에게 UIView통한 [self view]하지만 역 참조가 있는지 궁금 해서요?



답변

이것이 오랫동안 받아 들여진 대답이므로 더 나은 대답으로 수정해야한다고 생각합니다.

필요성에 대한 의견 :

  • 뷰가 뷰 컨트롤러에 직접 액세스 할 필요는 없습니다.
  • 대신 뷰는 뷰 컨트롤러와 독립적이어야하며 다른 컨텍스트에서 작동 할 수 있어야합니다.
  • 뷰 컨트롤러와 인터페이싱하기 위해 뷰가 필요한 경우 권장되는 방법과 Cocoa에서 Apple이하는 일은 델리게이트 패턴을 사용하는 것입니다.

구현 방법의 예는 다음과 같습니다.

@protocol MyViewDelegate < NSObject >

- (void)viewActionHappened;

@end

@interface MyView : UIView

@property (nonatomic, assign) MyViewDelegate delegate;

@end

@interface MyViewController < MyViewDelegate >

@end

뷰는 델리게이트와 인터페이스하고 ( UITableView예를 들어) 뷰 컨트롤러 또는 다른 클래스에서 구현되었는지는 중요하지 않습니다.

내 원래 답변은 다음과 같습니다. 권장하지 않습니다. 뷰 컨트롤러에 직접 액세스 할 수있는 나머지 답변도 없습니다.

기본 제공 방법이 없습니다. 당신이를 추가하여 주위를 얻을 수 있지만 IBOutlet상의 UIView와 인터페이스 빌더에서 이러한 연결이 권장되지 않습니다. 뷰는 뷰 컨트롤러에 대해 알 수 없습니다. 대신 @Phil M이 제안한대로 대리자로 사용할 프로토콜을 만들어야합니다.


답변

Brock이 게시 한 예제를 사용하여 UIViewController 대신 UIView 범주로 변경하고 하위 뷰가 부모 UIViewController를 찾을 수 있도록 재귀 적으로 만들었습니다.

@interface UIView (FindUIViewController)
- (UIViewController *) firstAvailableUIViewController;
- (id) traverseResponderChainForUIViewController;
@end

@implementation UIView (FindUIViewController)
- (UIViewController *) firstAvailableUIViewController {
    // convenience function for casting and to "mask" the recursive function
    return (UIViewController *)[self traverseResponderChainForUIViewController];
}

- (id) traverseResponderChainForUIViewController {
    id nextResponder = [self nextResponder];
    if ([nextResponder isKindOfClass:[UIViewController class]]) {
        return nextResponder;
    } else if ([nextResponder isKindOfClass:[UIView class]]) {
        return [nextResponder traverseResponderChainForUIViewController];
    } else {
        return nil;
    }
}
@end

이 코드를 사용하려면 새 클래스 파일 (내 이름은 “UIKit 카테고리”)에 추가하고 클래스 데이터를 제거하십시오. @interface를 헤더에 복사하고 @implementation을 .m 파일에 복사하십시오. 그런 다음 프로젝트에서 #import “UIKitCategories.h”를 가져 와서 UIView 코드 내에서 사용하십시오.

// from a UIView subclass... returns nil if UIViewController not available
UIViewController * myController = [self firstAvailableUIViewController];


답변

UIView의 하위 클래스입니다 UIResponder. 를 돌려주는 구현으로 UIResponder메소드 -nextResponder를 배치합니다 nil. 다음과 같이 (어떤 이유로에 대신) UIView문서화되어있는 것처럼이 메소드를 대체합니다 . 뷰 컨트롤러가없는 경우이 메서드는 수퍼 뷰를 반환합니다.UIResponderUIView-nextResponder

이것을 프로젝트에 추가하면 롤 준비가 된 것입니다.

@interface UIView (APIFix)
- (UIViewController *)viewController;
@end

@implementation UIView (APIFix)

- (UIViewController *)viewController {
    if ([self.nextResponder isKindOfClass:UIViewController.class])
        return (UIViewController *)self.nextResponder;
    else
        return nil;
}
@end

이제 UIView뷰 컨트롤러를 반환하는 작업 방법이 있습니다.


답변

UIView에 범주를 추가하지 않고도 전체 응답자 체인을 통과하는보다 가벼운 접근 방식을 제안합니다.

@implementation MyUIViewSubclass

- (UIViewController *)viewController {
    UIResponder *responder = self;
    while (![responder isKindOfClass:[UIViewController class]]) {
        responder = [responder nextResponder];
        if (nil == responder) {
            break;
        }
    }
    return (UIViewController *)responder;
}

@end


답변

이미 주어진 여러 답변을 결합하여 구현과 함께 제공합니다.

@implementation UIView (AppNameAdditions)

- (UIViewController *)appName_viewController {
    /// Finds the view's view controller.

    // Take the view controller class object here and avoid sending the same message iteratively unnecessarily.
    Class vcc = [UIViewController class];

    // Traverse responder chain. Return first found view controller, which will be the view's view controller.
    UIResponder *responder = self;
    while ((responder = [responder nextResponder]))
        if ([responder isKindOfClass: vcc])
            return (UIViewController *)responder;

    // If the view controller isn't found, return nil.
    return nil;
}

@end

이 카테고리는 내 ARC 지원의 일부입니다 내가 만드는 모든 응용 프로그램에서 제공하는 정적 라이브러리의 . 여러 번 테스트를 받았으며 문제 나 누수를 찾지 못했습니다.

추신 : 관련보기가 귀하의 하위 클래스 인 경우처럼 카테고리를 사용할 필요가 없습니다. 후자의 경우 메소드를 서브 클래스에 넣으면됩니다.


답변

비록 이것이 pgb가 권장하는 IMHO에 의해 기술적으로 해결 될 수 있지만 , 이것은 디자인 결함입니다. 보기는 컨트롤러를 인식하지 않아도됩니다.


답변

나는 수정 내가 어떤보기 버튼을 통과 그것의 부모를 얻을 수 등의 레이블을 지정할 수 있도록 답을 UIViewController. 여기 내 코드가 있습니다.

+(UIViewController *)viewController:(id)view {
    UIResponder *responder = view;
    while (![responder isKindOfClass:[UIViewController class]]) {
        responder = [responder nextResponder];
        if (nil == responder) {
            break;
        }
    }
    return (UIViewController *)responder;
}

스위프트 3 버전 편집

class func viewController(_ view: UIView) -> UIViewController {
        var responder: UIResponder? = view
        while !(responder is UIViewController) {
            responder = responder?.next
            if nil == responder {
                break
            }
        }
        return (responder as? UIViewController)!
    }

편집 2 :-신속한 확장

extension UIView
{
    //Get Parent View Controller from any view
    func parentViewController() -> UIViewController {
        var responder: UIResponder? = self
        while !(responder is UIViewController) {
            responder = responder?.next
            if nil == responder {
                break
            }
        }
        return (responder as? UIViewController)!
    }
}