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)!
}
}
뷰가 하위 뷰인 창에 대한 루트 뷰 컨트롤러에 액세스 할 수 있다는 것을 잊지 마십시오. 거기에서, 예를 들어 네비게이션 뷰 컨트롤러를 사용하고 있고 새로운 뷰를 푸시하려는 경우 :
[[[[self window] rootViewController] navigationController] pushViewController:newController animated:YES];
그러나 먼저 창의 rootViewController 속성을 올바르게 설정해야합니다. 예를 들어 앱 대리자에서 컨트롤러를 처음 만들 때 이렇게하십시오.
-(void) applicationDidFinishLaunching:(UIApplication *)application {
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
RootViewController *controller = [[YourRootViewController] alloc] init];
[window setRootViewController: controller];
navigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
[controller release];
[window addSubview:[[self navigationController] view]];
[window makeKeyAndVisible];
}
Ushox를 포함한 이러한 답변은 기술적으로 정확하지만 승인 된 방법은 새로운 프로토콜을 구현하거나 기존 프로토콜을 재사용하는 것입니다. 프로토콜은 관찰자와 관찰자를 격리 시키며, 그 사이에 메일 슬롯을 넣는 것과 같습니다. 실제로, 그것은 pushViewController 메소드 호출을 통해 Gabriel이하는 일입니다. viewController는 navigationController 프로토콜을 준수하기 때문에, view는 navigationController에게 예의 바르게 푸시를 요청하는 것이 적절한 프로토콜임을 "알고있다". 가브리엘의 예제를 사용하고 UINavigationController 프로토콜을 재사용하는 것만으로도 고유 한 프로토콜을 만들 수 있습니다.
재사용하려는 작은 구성 요소가있는 상황을 우연히 발견하고 재사용 가능한보기 자체에 코드를 추가했습니다 (실제로 버튼을 여는 버튼 이상은 아닙니다 PopoverController).
이것은 iPad에서 잘 작동하지만 ( UIPopoverController선물 자체는 참조 할 필요가 없습니다 UIViewController) 동일한 코드를 작동시키는 것은 갑자기에서를 참조하는 것을 의미 presentViewController합니다 UIViewController. 일관성이 없습니까?
앞에서 언급했듯이 UIView에 논리를 사용하는 것이 가장 좋은 방법은 아닙니다. 그러나 별도의 컨트롤러에 필요한 몇 줄의 코드를 포장하는 것은 실제로 쓸모가 없다고 생각했습니다.
어느 쪽이든, UIView에 새로운 속성을 추가하는 신속한 솔루션이 있습니다.
extension UIView {
var viewController: UIViewController? {
var responder: UIResponder? = self
while responder != nil {
if let responder = responder as? UIViewController {
return responder
}
responder = responder?.nextResponder()
}
return nil
}
}
어떤 경우에 뷰 컨트롤러가 누구인지 알아내는 것이 "나쁜"생각은 아닙니다. 나쁜 생각이 될 수있는 것은이 컨트롤러에 대한 참조를 저장하면 감독이 변경되는대로 변경 될 수 있기 때문입니다. 제 경우에는 응답자 체인을 가로 지르는 게터가 있습니다.
//.h
@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;
}
viewController를 찾기위한 가장 간단한 while 루프입니다.
-(UIViewController*)viewController
{
UIResponder *nextResponder = self;
do
{
nextResponder = [nextResponder nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]])
return (UIViewController*)nextResponder;
} while (nextResponder != nil);
return nil;
}
이것은 질문에 직접 대답하는 것이 아니라 질문의 의도에 대한 가정을합니다.
뷰가 있고 해당 뷰에서 뷰 컨트롤러와 같은 다른 객체에서 메소드를 호출해야하는 경우 NSNotificationCenter를 대신 사용할 수 있습니다.
먼저 헤더 파일에 알림 문자열을 작성하십시오.
#define SLCopyStringNotification @"ShaoloCopyStringNotification"
viewcall 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];
}
이제 동일한 뷰 컨트롤러에서도 위의 @selector에 표시된 것처럼 copyString : 메소드를 구현하십시오.
- (IBAction) copyString:(id)sender
{
CalculatorResult* result = (CalculatorResult*)[[PercentCalculator sharedInstance].arrayTableDS objectAtIndex:([self.viewTableResults indexPathForSelectedRow].row)];
UIPasteboard *gpBoard = [UIPasteboard generalPasteboard];
[gpBoard setString:result.stringResult];
}
나는 이것이 올바른 방법이라고 말하지 않고 첫 번째 응답자 체인을 실행하는 것보다 더 깨끗해 보입니다. 이 코드를 사용하여 UITableView에서 UIMenuController를 구현하고 이벤트를 다시 UIViewController로 전달하여 데이터로 무언가를 할 수 있습니다.
분명히 나쁜 생각과 잘못된 디자인이지만 @Phil_M이 제안한 최고의 답변의 Swift 솔루션을 모두 즐길 수 있다고 확신합니다.
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의 모든 크레딧
Maybe I'm late here. But in this situation I don't like category (pollution). I love this way:
#define UIViewParentController(__view) ({ \
UIResponder *__responder = __view; \
while ([__responder isKindOfClass:[UIView class]]) \
__responder = [__responder nextResponder]; \
(UIViewController *)__responder; \
})
Swiftier solution
extension UIView {
var parentViewController: UIViewController? {
for responder in sequence(first: self, next: { $0.next }) {
if let viewController = responder as? UIViewController {
return viewController
}
}
return nil
}
}
Swift 4
(more concise than the other answers)
fileprivate extension UIView {
var firstViewController: UIViewController? {
let firstViewController = sequence(first: self, next: { $0.next }).first(where: { $0 is UIViewController })
return firstViewController as? UIViewController
}
}
My use case for which I need to access the view first UIViewController: I have an object that wraps around AVPlayer / AVPlayerViewController and I want to provide a simple show(in view: UIView) method that will embed AVPlayerViewController into view. For that, I need to access view's UIViewController.
Swift 4 version
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
}
Usage example
if let parent = self.view.parentViewController{
}
To Phil's answer:
In line: id nextResponder = [self nextResponder]; if self(UIView) is not a subview of ViewController's view, if you know hierarchy of self(UIView) you can use also: id nextResponder = [[self superview] nextResponder];...
Updated version for swift 4 : Thanks for @Phil_M and @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)
}
My solution would probably be considered kind of bogus but I had a similar situation as mayoneez (I wanted to switch views in response to a gesture in an EAGLView), and I got the EAGL's view controller this way:
EAGLViewController *vc = ((EAGLAppDelegate*)[[UIApplication sharedApplication] delegate]).viewController;
I think there is a case when the observed needs to inform the observer.
I see a similar problem where the UIView in a UIViewController is responding to a situation and it needs to first tell its parent view controller to hide the back button and then upon completion tell the parent view controller that it needs to pop itself off the stack.
I have been trying this with delegates with no success.
I don't understand why this should be a bad idea?
Another easy way is to have your own view class and add a property of the view controller in the view class. Usually the view controller creates the view and that is where the controller can set itself to the property. Basically it is instead of searching around (with a bit of hacking) for the controller, having the controller to set itself to the view - this is simple but makes sense because it is the controller that "controls" the view.
If you aren't going to upload this to the App Store, you can also use a private method of UIView.
@interface UIView(Private)
- (UIViewController *)_viewControllerForAncestor;
@end
// Later in the code
UIViewController *vc = [myView _viewControllerForAncestor];
If your rootViewController is UINavigationViewController, which was set up in AppDelegate class, then
+ (UIViewController *) getNearestViewController:(Class) c {
NSArray *arrVc = [[[[UIApplication sharedApplication] keyWindow] rootViewController] childViewControllers];
for (UIViewController *v in arrVc)
{
if ([v isKindOfClass:c])
{
return v;
}
}
return nil;}
Where c required view controllers class.
USAGE:
RequiredViewController* rvc = [Utilities getNearestViewController:[RequiredViewController class]];
There is no way.
What I do is pass the UIViewController pointer to the UIView (or an appropriate inheritance). I'm sorry I can't help with the IB approach to the problem because I don't believe in IB.
To answer the first commenter: sometimes you do need to know who called you because it determines what you can do. For example with a database you might have read access only or read/write ...
참고URL : https://stackoverflow.com/questions/1340434/get-to-uiviewcontroller-from-uiview
'IT' 카테고리의 다른 글
| groupby를 사용하여 그룹에서 최대 값을 갖는 행을 가져옵니다. (0) | 2020.05.19 |
|---|---|
| URL 매개 변수를 JavaScript 객체로 변환 (0) | 2020.05.19 |
| 부트 스트랩 팝 오버 너비 변경 (0) | 2020.05.19 |
| UITableViewController없이 UITableView를 새로 고치려면 당기십시오. (0) | 2020.05.19 |
| 2012 년에서 2013 년으로 업데이트 한 후 파일 또는 어셈블리 'System.Web.Http 4.0.0을로드 할 수 없습니다. (0) | 2020.05.19 |