Ios 从UIView访问UIViewController?

Ios 从UIView访问UIViewController?,ios,objective-c,cocoa-touch,uiview,uiviewcontroller,Ios,Objective C,Cocoa Touch,Uiview,Uiviewcontroller,从UIView到它的UIViewController是否有一种内置方式?我知道你可以通过[self view]从UIViewController到它的UIView,但我想知道是否有相反的参考?因为这是一个长期以来被接受的答案,我觉得我需要用一个更好的答案来纠正它 关于需要的一些评论: 您的视图不需要直接访问视图控制器 相反,视图应该独立于视图控制器,并且能够在不同的上下文中工作 如果您需要视图以某种方式与视图控制器交互,推荐的方式以及Apple在Cocoa中所做的是使用委托模式 下面是一个如

UIView
到它的
UIViewController
是否有一种内置方式?我知道你可以通过
[self view]
UIViewController
到它的
UIView
,但我想知道是否有相反的参考?

因为这是一个长期以来被接受的答案,我觉得我需要用一个更好的答案来纠正它

关于需要的一些评论:

  • 您的视图不需要直接访问视图控制器
  • 相反,视图应该独立于视图控制器,并且能够在不同的上下文中工作
  • 如果您需要视图以某种方式与视图控制器交互,推荐的方式以及Apple在Cocoa中所做的是使用委托模式
下面是一个如何实现它的示例:

@protocol MyViewDelegate < NSObject >

- (void)viewActionHappened;

@end

@interface MyView : UIView

@property (nonatomic, assign) MyViewDelegate delegate;

@end

@interface MyViewController < MyViewDelegate >

@end
@protocol MyViewDelegate
-(无效)已发生的事件;
@结束
@接口MyView:UIView
@属性(非原子,赋值)MyViewDelegate委托;
@结束
@接口MyViewController
@结束
视图与其委托接口(例如,正如
UITableView
所做的那样),它不关心它是在视图控制器中实现的,还是在您最终使用的任何其他类中实现的

我的原始答案如下:我不建议这样做,其他答案也不建议直接访问视图控制器

没有内在的方法可以做到这一点。虽然您可以通过在
UIView
上添加
IBOutlet
并在Interface Builder中连接它们来解决此问题,但不建议这样做。视图不应该知道视图控制器。相反,您应该按照@Phil M的建议去做,并创建一个协议作为代理使用。

没有办法

我要做的是将UIViewController指针传递到UIView(或适当的继承)。很抱歉,我不能帮助IB解决这个问题,因为我不相信IB


回答第一个评论:有时候你确实需要知道谁给你打电话,因为这决定了你能做什么。例如,对于一个数据库,您可能具有只读访问权限或读/写…

即使这在技术上可以按照pgb的建议解决,IMHO,这是一个设计缺陷。视图不需要知道控制器

我的解决方案可能会被认为是假的,但我的情况与mayoneez类似(我想切换视图以响应EagleView中的手势),我通过以下方式获得了Eagle的视图控制器:

EAGLViewController *vc = ((EAGLAppDelegate*)[[UIApplication sharedApplication] delegate]).viewController;
#define UIViewParentController(__view) ({ \
UIResponder *__responder = __view; \
while ([__responder isKindOfClass:[UIView class]]) \
__responder = [__responder nextResponder]; \
(UIViewController *)__responder; \
})

UIView
UIResponder
的子类
UIResponder
列出了方法
-nextResponder
,其实现返回
nil
UIView
覆盖此方法,如
UIResponder
中所述(出于某种原因而不是
UIView
),如下所示:如果视图具有视图控制器,则由
-nextResponder
返回。如果没有视图控制器,该方法将返回superview

将此添加到您的项目中,您就可以开始了

@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
有了一个返回视图控制器的工作方法。

使用Brock发布的示例,我对其进行了修改,使其成为UIView的一个类别,而不是UIViewController,并使其递归,以便任何子视图都可以(希望)找到父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
-(UIViewController*)viewController
{
    UIResponder *nextResponder =  self;

    do
    {
        nextResponder = [nextResponder nextResponder];

        if ([nextResponder isKindOfClass:[UIViewController class]])
            return (UIViewController*)nextResponder;

    } while (nextResponder != nil);

    return nil;
}
要使用此代码,请将其添加到新的类文件(我将其命名为“UIKITCOLEGORIES”)中,并删除类数据。。。将@interface复制到头文件中,将@implementation复制到.m文件中。然后在您的项目中,导入“UIKitCategories.h”并在UIView代码中使用:

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

不要忘记,您可以访问视图是其子视图的窗口的根视图控制器。从这里开始,如果您正在使用导航视图控制器,并希望将新视图推送到该控制器上:

    [[[[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,但我认为认可的方法是实施新协议或重新使用现有协议。协议将观察者与被观察者隔离开来,有点像在他们之间放置一个邮件槽。实际上,这就是Gabriel通过pushViewController方法调用所做的;视图“知道”礼貌地要求navigationController推送视图是正确的协议,因为viewController符合navigationController协议。虽然您可以创建自己的协议,但只需使用Gabriel的示例并重新使用UINavigationController协议就可以了

我认为有一种情况是被观察者需要通知观察者

我看到了一个类似的问题,UIViewController中的UIView正在响应一种情况,它需要首先告诉其父视图控制器隐藏back按钮,然后在完成后告诉父视图控制器它需要将自己从堆栈中弹出

我一直在与代表们一起尝试,但没有成功


我不明白为什么这是个坏主意?

我建议使用一种更轻量级的方法来遍历整个响应者链,而不必在UIView上添加类别:

@implementation MyUIViewSubclass

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

@end
我不认为在某些情况下找出谁是视图控制器是一个“坏”主意。保存对该控制器的引用可能是个坏主意,因为它可能会随着SuperView的更改而更改。 在我的例子中,有一个getter遍历响应器链

customView.target(forAction: Selector("viewDidLoad"), withSender: nil)
//h

//m


结合几个已经给出的答案,我正在将其与我的实现一起发布:

@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
该类别是我在创建的每个应用程序上发布的已启用弧的静态库的一部分。经过多次测试,我没有发现任何问题或泄漏

附言:你不需要像我一样使用分类,如果公司
- (IBAction) copyString:(id)sender
{
    [[NSNotificationCenter defaultCenter] postNotificationName:SLCopyStringNotification object:nil];
}
- (void)viewDidLoad
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(copyString:)
                                                 name:SLCopyStringNotification
                                               object:nil];
}
- (IBAction) copyString:(id)sender
{
    CalculatorResult* result = (CalculatorResult*)[[PercentCalculator sharedInstance].arrayTableDS objectAtIndex:([self.viewTableResults indexPathForSelectedRow].row)];
    UIPasteboard *gpBoard = [UIPasteboard generalPasteboard];
    [gpBoard setString:result.stringResult];
}
-(UIViewController*)viewController
{
    UIResponder *nextResponder =  self;

    do
    {
        nextResponder = [nextResponder nextResponder];

        if ([nextResponder isKindOfClass:[UIViewController class]])
            return (UIViewController*)nextResponder;

    } while (nextResponder != nil);

    return nil;
}
    + (UIViewController *) getNearestViewController:(Class) c {
NSArray *arrVc = [[[[UIApplication sharedApplication] keyWindow] rootViewController] childViewControllers];

for (UIViewController *v in arrVc)
{
    if ([v isKindOfClass:c])
    {
        return v;
    }
}

return nil;}
     RequiredViewController* rvc = [Utilities getNearestViewController:[RequiredViewController class]];
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)
}
if let viewController = MyUtilityClass.firstAvailableUIViewController(self) {}
+(UIViewController *)viewController:(id)view {
    UIResponder *responder = view;
    while (![responder isKindOfClass:[UIViewController class]]) {
        responder = [responder nextResponder];
        if (nil == responder) {
            break;
        }
    }
    return (UIViewController *)responder;
}
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)!
    }
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)!
    }
}
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
    }
}
#define UIViewParentController(__view) ({ \
UIResponder *__responder = __view; \
while ([__responder isKindOfClass:[UIView class]]) \
__responder = [__responder nextResponder]; \
(UIViewController *)__responder; \
})
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)
}
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
}
 if let parent = self.view.parentViewController{

 }
extension UIView {
    var parentViewController: UIViewController? {
        for responder in sequence(first: self, next: { $0.next }) {
            if let viewController = responder as? UIViewController {
                return viewController
            }
        }
        return nil
    }
}
fileprivate extension UIView {

  var firstViewController: UIViewController? {
    let firstViewController = sequence(first: self, next: { $0.next }).first(where: { $0 is UIViewController })
    return firstViewController as? UIViewController
  }

}
@interface UIView(Private)
- (UIViewController *)_viewControllerForAncestor;
@end

// Later in the code
UIViewController *vc = [myView _viewControllerForAncestor];
var parentViewController: UIViewController? {
    let s = sequence(first: self) { $0.next }
    return s.compactMap { $0 as? UIViewController }.first
}
customView.target(forAction: Selector("viewDidLoad"), withSender: nil)