关于ios:将数据从Child Modal VC传递到父视图控制器的最佳方法?

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

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

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

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

谢谢!艾伦


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

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

您将在堆栈溢出中发现的一些委托示例可能非常混乱,与内置框架中发现的非常不一样。必须仔细选择协议的名称和接口,以及分配给每个对象的职责,以便最大限度地实现代码重用和代码目标。

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

首先,让我们看看LoginviewController的委托协议:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#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

登录控制器可以将许多事件传递给它的委托,并要求其委托提供用于自定义其行为的信息。它在实现过程中将事件传递给代理,作为对用户操作的响应的一部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#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

在某些方面,登录可能是一个复杂的、交互式的过程,因此我建议您认真考虑使用委派而不是通知。然而,有一件事可能是有问题的,那就是委托必然只是一个对象。如果需要多个不同的对象知道登录视图控制器的进度和状态,那么可能需要使用通知。尤其是,如果登录过程可以被限制为非常简单,在某种程度上,除了传递单向消息和数据之外,不需要任何交互,那么通知就可以成为一个可行的选项。您可以将通知中的任意变量传递回userInfo属性,该属性是一个NSDictionary属性,包含您决定在其中填充的内容。通知可能会影响性能,但我知道,只有当观察者以数百为单位时,才会发生这种情况。尽管如此,在我看来这并不是最自然的,因为您有父对象(或多或少控制子对象的生命周期)向第三方对象请求子对象的更新。


你可以通过使用协议来获得它,这是最好的方法。

我将向您介绍如何创建协议的基本概念

另外,请阅读以下问题:如何在Objective-C中创建代表?

下面的代码给出了协议的基本概念,在下面的代码中,您可以从MasterViewControllerDetailViewController获得按钮标题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#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);

}