xcode 将数据从子模态 VC 传递到父视图控制器的最佳方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15814426/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 03:07:39  来源:igfitidea点击:

Best way to pass data from Child Modal VC to the Parent View Controller?

iosobjective-cxcodemodalviewcontroller

提问by Alan

What is the best way to pass data from a child modal view to the parent view controller?

将数据从子模式视图传递到父视图控制器的最佳方法是什么?

I have a Child Modal Login Screen on my iPad app that I want to pass back user information to the parent Split View Controller.

我的 iPad 应用程序上有一个子模态登录屏幕,我想将用户信息传回父拆分视图控制器。

I am thinking of using NSNotification, but I am not sure if this is the easiest/most efficient way to pass data back to the parent.

我正在考虑使用 NSNotification,但我不确定这是否是将数据传递回父级的最简单/最有效的方法。

Thanks! Alan

谢谢!艾伦

回答by Carl Veazey

I would suggest, as iPatel did, to use delegationto solve your problem. The relationship between the parent view controller and the login view controller makes this pattern appropriate. When one object creates another in order to fulfill a specific responsibility, one should consider delegation as a way to have the created object communicate with the creator. A particularly compelling reason to choose delegation would be if the task to be accomplished potentially has multiple steps that require a high level of interaction between objects. You can look at the NSURLConnectionDelegateprotocolas an illustration of this. Connecting to a URL is a complex task, involving stages such as processing responses, meeting authentication challenges, saving downloaded data, and handling errors, the connection and the delegate handle this together over the lifetime of the connection.

我建议,就像 iPatel 所做的那样,使用委托来解决您的问题。父视图控制器和登录视图控制器之间的关系使这种模式合适。当一个对象为了履行特定的职责而创建另一个对象时,应该将委托视为一种让创建的对象与创建者进行通信的方式。选择委托的一个特别令人信服的理由是,如果要完成的任务可能具有多个需要对象之间高级交互的步骤。你可以看看NSURLConnectionDelegate协议作为一个例证。连接到 URL 是一项复杂的任务,涉及处理响应、满足身份验证挑战、保存下载的数据和处理错误等阶段,连接和委托在连接的整个生命周期内一起处理这些。

As you've probably noticed, in Objective-C protocols are used to achieve delegation without tightly coupling the created object (in this case your login view controller) to the object that created it (the parent view controller). The login view controller can then interact with any object that can receive the messages defined in its protocol, rather than rely on any particular class implementation. Tomorrow, if you receive a requirement to allow any view controller to show the login view, the login view controller wouldn't need to change. Your other view controllers can implement its delegate protocol, create and present the login view, and assign themselves as delegates without the login view controller ever knowing of their existence.

您可能已经注意到,Objective-C 协议用于实现委托,而无需将创建的对象(在本例中为您的登录视图控制器)与创建它的对象(父视图控制器)紧密耦合。然后,登录视图控制器可以与任何可以接收其协议中定义的消息的对象进行交互,而不是依赖于任何特定的类实现。明天,如果您收到要求允许任何视图控制器显示登录视图,则登录视图控制器不需要更改。你的其他视图控制器可以实现它的委托协议,创建和呈现登录视图,并在登录视图控制器不知道它们存在的情况下将自己分配为委托。

Some delegation examples you'll find on Stack Overflow may be very confusing and very un-like what's found in the built-in frameworks. One must pick the names and interfaces of the protocols carefully, as well as the responsibilities assigned to each object, so that code reuse is maximized and the objective of the code is achieved.

您会在 Stack Overflow 上找到的一些委托示例可能非常令人困惑,并且与内置框架中的内容非常不同。必须仔细挑选协议的名称和接口,以及分配给每个对象的职责,以便最大化代码重用,实现代码的目标。

You should first take a look at the many delegate protocols within the built-in frameworks for an idea of what the relationship looks like when expressed in code. Here is another small example, based on your login use case. I hope you will find that the purpose of the delegation is clear and that the roles and responsibilities of the objects involved are clear and expressed through their names within the code.

您应该首先查看内置框架中的许多委托协议,以了解在代码中表达的关系是什么样的。这是另一个基于您的登录用例的小示例。我希望你会发现委托的目的是明确的,并且所涉及的对象的角色和职责是明确的,并且在代码中通过它们的名称来表达。

First, let's look at the LoginViewController's delegate protocol:

首先,我们来看看 LoginViewController 的委托协议:

#import <UIKit/UIKit.h>

@protocol LoginViewControllerDelegate;

@interface LoginViewController : UIViewController

// We choose a name here that expresses what object is doing the delegating
@property (nonatomic, weak) id<LoginViewControllerDelegate> delegate;

@end

@protocol LoginViewControllerDelegate <NSObject>

// The methods declared here are all optional
@optional

// We name the methods here in a way that explains what the purpose of each message is
// Each takes a LoginViewController as the first argument, allowing one object to serve
// as the delegate of many LoginViewControllers
- (void)loginViewControllerDidLoginSuccessfully:(LoginViewController *)lvc;
- (void)loginViewController:(LoginViewController *)lvc didFailWithError:(NSError *)error;
- (void)loginViewControllerDidReceivePasswordResetRequest:(LoginViewController *)lvc;
- (void)loginViewControllerDiDReceiveSignupRequest:(LoginViewController *)lvc;
- (BOOL)loginViewControllerShouldAllowAnonymousLogin:(LoginViewController *)lvc;

@end

The login controller can communicate a number of events to its delegate, as well as ask its delegate for information used to customize its behavior. It communicates events to the delegate in its implementation as part of its response to user actions:

登录控制器可以向它的委托传达许多事件,也可以向它的委托询问用于定制其行为的信息。它在其实现中将事件传达给委托,作为其对用户操作的响应的一部分:

#import "LoginViewController.h"

@interface LoginViewController ()

@property (weak, nonatomic) IBOutlet UIButton *anonSigninButton;

@end

@implementation LoginViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    //  Here we ask the delegate for information used to layout the view
    BOOL anonymousLoginAllowed = NO;
    //  All our protocol methods are @optional, so we must check they are actually implemented before calling.
    if ([self.delegate respondsToSelector:@selector(loginViewControllerShouldAllowAnonymousLogin:)]) {
        // self is passed as the LoginViewController argument to the delegate methods
        // in this way our delegate can serve as the delegate of multiple login view controllers, if needed
        anonymousLoginAllowed = [self.delegate loginViewControllerShouldAllowAnonymousLogin:self];
    }
    self.anonSigninButton.hidden = !anonymousLoginAllowed;
}

- (IBAction)loginButtonAction:(UIButton *)sender
{
    // We're preteneding our password is always bad. So we assume login succeeds when allowed anonmously
    BOOL loginSuccess = [self isAnonymousLoginEnabled];
    NSError *loginError = [self isAnonymousLoginEnabled] ? nil : [NSError errorWithDomain:@"domain" code:0 userInfo:nil];

    //  Fake concurrency
    double delayInSeconds = 1.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        //  Notify delegate of failure or success
        if (loginSuccess) {
            if ([self.delegate respondsToSelector:@selector(loginViewControllerDidLoginSuccessfully:)]) {
                [self.delegate loginViewControllerDidLoginSuccessfully:self];
            }
        }
        else {
            if ([self.delegate respondsToSelector:@selector(loginViewController:didFailWithError:)]) {
                [self.delegate loginViewController:self didFailWithError:loginError];
            }
        }
    });
}

- (IBAction)forgotPasswordButtonAction:(id)sender
{
    //  Notify delegate to handle forgotten password request.
    if ([self.delegate respondsToSelector:@selector(loginViewControllerDidReceivePasswordResetRequest:)]) {
        [self.delegate loginViewControllerDidReceivePasswordResetRequest:self];
    }
}

- (IBAction)signupButtonAction:(id)sender
{
    //  Notify delegate to handle signup request.
    if ([self.delegate respondsToSelector:@selector(loginViewControllerDiDReceiveSignupRequest:)]) {
        [self.delegate loginViewControllerDiDReceiveSignupRequest:self];
    }
}

- (BOOL)isAnonymousLoginEnabled
{
    BOOL anonymousLoginAllowed = NO;

    if ([self.delegate respondsToSelector:@selector(loginViewControllerShouldAllowAnonymousLogin:)]) {
        anonymousLoginAllowed = [self.delegate loginViewControllerShouldAllowAnonymousLogin:self];
    }
    return  anonymousLoginAllowed;
}

@end

The main view controller instantiates and presents a login view controller, and handles its delegate messages:

主视图控制器实例化并呈现登录视图控制器,并处理其委托消息:

#import "MainViewController.h"
#import "LoginViewController.h"

#define LOGGED_IN NO

@interface MainViewController () <LoginViewControllerDelegate>

@end

@implementation MainViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    //  Fake loading time to show the modal cleanly
    if (!LOGGED_IN) {
        double delayInSeconds = 1.0;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            //  Create a login view controller, assign its delegate, and present it
            LoginViewController *lvc = [[LoginViewController alloc] init];
            lvc.delegate = self;
            [self presentViewController:lvc animated:YES completion:^{
                NSLog(@"modal completion finished.");
            }];
        });
    }
}

#pragma mark - LoginViewControllerDelegate


- (void)loginViewControllerDidLoginSuccessfully:(LoginViewController *)lvc
{
    NSLog(@"Login VC delegate - Login success!");
    [self dismissViewControllerAnimated:YES completion:NULL];
}

- (void)loginViewController:(LoginViewController *)lvc didFailWithError:(NSError *)error
{
    // Maybe show an alert...
    // UIAlertView *alert = ...
}

- (void)loginViewControllerDidReceivePasswordResetRequest:(LoginViewController *)lvc
{
    // Take the user to safari to reset password maybe
     NSLog(@"Login VC delegate - password reset!");
}

- (void)loginViewControllerDiDReceiveSignupRequest:(LoginViewController *)lvc
{
    // Take the user to safari to open signup form maybe
    NSLog(@"Login VC delegate - signup requested!");
}

- (BOOL)loginViewControllerShouldAllowAnonymousLogin:(LoginViewController *)lvc
{
    return YES;
}

@end

Logging in can be a complex, interactive process in some ways, so I do recommend you seriously consider using delegation instead of notifications. However, one thing that may be problematic is that delegates are necessarily only a single object. If you need to have multiple, disparate objects know about the login view controller's progress and stae, then you may need to use notifications. Especially if the login process can be constrained to be very simple, in a way that does not require any interaction beyond passing one-way messages and data, then notifications can become a viable option. You can pass arbitrary variables in a notification back inside the userInfoproperty which is an NSDictionaryof whatever you decide to stuff in it. Notifications can impact performance but I understand that only happens nowadays when observers number in the hundreds. Even still, it is not the most natural fit in my mind, as you have the parent object (that more or less controls the lifetime of the child) asking a third party object for updates from the child object.

在某些方面,登录可能是一个复杂的交互过程,因此我建议您认真考虑使用委派而不是通知。但是,可能有问题的一件事是委托必须只是一个对象。如果您需要让多个不同的对象了解登录视图控制器的进度和状态,那么您可能需要使用通知。特别是如果登录过程可以被限制为非常简单,除了传递单向消息和数据之外不需要任何交互,那么通知可以成为一个可行的选择。您可以将通知中的任意变量传递回userInfo属性内,这是一个NSDictionary无论你决定往里面塞什么东西。通知会影响性能,但我知道只有当观察者数量达到数百时才会发生这种情况。即便如此,这在我看来并不是最合适的,因为您有父对象(或多或少控制子对象的生命周期)向第三方对象询问来自子对象的更新。

回答by iPatel

You can get it by using Protocol, it is Best way.

您可以通过使用Protocol来获得它,这是最好的方法。

I will give you the Basic Idea for how to create a Protocol

我会给你关于如何创建协议的基本想法

Also, read this question: How do I create delegates in Objective-C?

另外,请阅读这个问题: 如何在 Objective-C 中创建委托?

Following code give you the basic idea for Protocol, here in below code you can get Button title from MasterViewControllerto DetailViewController.

以下代码为您提供了 Protocol 的基本概念,在下面的代码中,您可以从MasterViewControllerto获取按钮标题DetailViewController

#DetailViewController.h

#import <UIKit/UIKit.h>

@protocol MasterDelegate <NSObject>
-(void) getButtonTitile:(NSString *)btnTitle;
@end


@interface DetailViewController : MasterViewController

@property (nonatomic, assign) id<MasterDelegate> customDelegate; 

#DetailViewController.m

if([self.customDelegate respondsToSelector:@selector(getButtonTitile:)])
{
          [self.customDelegate getButtonTitile:button.currentTitle];    
}

#MasterViewController.m

create obj of DetailViewController

DetailViewController *obj = [[DetailViewController alloc] init];
obj.customDelegate = self;
[self.navigationController pushViewController:reportTypeVC animated:YES];

and add delegate method in MasterViewController.m for get button title.

#pragma mark -
#pragma mark - Custom Delegate  Method

-(void) getButtonTitile:(NSString *)btnTitle;
{
    NSLog(@"%@", btnTitle);

}