10

子モーダル ビューから親ビュー コントローラーにデータを渡す最良の方法は何ですか?

iPad アプリに子モーダル ログイン画面があり、ユーザー情報を親 Split View Controller に戻したいと考えています。

NSNotification の使用を考えていますが、これがデータを親に戻す最も簡単で効率的な方法かどうかはわかりません。

ありがとう!アラン

4

2 に答える 2

14

iPatel が行ったように、委任を使用して問題を解決することをお勧めします。親View ControllerとログインView Controllerの関係により、このパターンが適切になります。特定の責任を果たすためにあるオブジェクトが別のオブジェクトを作成する場合、作成されたオブジェクトが作成者と通信する方法として委任を考慮する必要があります。委任を選択する特に説得力のある理由は、達成するタスクに、オブジェクト間の高レベルの相互作用を必要とする複数のステップが含まれる可能性がある場合です。プロトコルを見ることができますNSURLConnectionDelegateこれの実例として。URL への接続は、応答の処理、認証チャレンジへの対応、ダウンロードしたデータの保存、エラーの処理などの段階を含む複雑なタスクです。接続とデリゲートは、接続の存続期間にわたってこれを一緒に処理します。

お気づきかもしれませんが、Objective-C では、作成されたオブジェクト (この場合はログイン ビュー コントローラー) をそれを作成したオブジェクト (親ビュー コントローラー) に密結合することなく委任を実現するためにプロトコルが使用されます。ログイン ビュー コントローラーは、特定のクラスの実装に依存するのではなく、プロトコルで定義されたメッセージを受信できる任意のオブジェクトと対話できます。明日、任意のビュー コントローラーがログイン ビューを表示できるようにする必要がある場合、ログイン ビュー コントローラーを変更する必要はありません。他のビュー コントローラーは、デリゲート プロトコルを実装し、ログイン ビューを作成して表示し、ログイン ビュー コントローラーがその存在を認識しなくても、自分自身をデリゲートとして割り当てることができます。

Stack Overflow にある委譲の例の中には、非常に紛らわしく、組み込みのフレームワークに見られるものとはまったく異なるものがあるかもしれません。コードの再利用を最大化し、コードの目的が達成されるように、プロトコルの名前とインターフェイス、および各オブジェクトに割り当てられた責任を慎重に選択する必要があります。

コードで表現したときに関係がどのように見えるかを理解するために、組み込みフレームワーク内の多くのデリゲート プロトコルを最初に確認する必要があります。ログインのユースケースに基づいた別の小さな例を次に示します。委任の目的が明確であり、関連するオブジェクトの役割と責任が明確であり、コード内の名前で表現されていることがわかると思います。

まず、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

ログイン コントローラーは、多くのイベントをそのデリゲートに伝えることができるだけでなく、その動作をカスタマイズするために使用される情報をデリゲートに要求することもできます。ユーザー アクションへの応答の一部として、その実装でデリゲートにイベントを伝達します。

#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

メイン ビュー コントローラーは、ログイン ビュー コントローラーをインスタンス化して提示し、そのデリゲート メッセージを処理します。

#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あなたがそれに詰め込むことに決めたものは何でも。通知はパフォーマンスに影響を与える可能性がありますが、オブザーバーが何百人もいる場合にのみ発生することを私は理解しています. それでも、親オブジェクト (子の寿命を多かれ少なかれ制御する) がサード パーティ オブジェクトに子オブジェクトからの更新を要求しているため、これは私の考えでは最も自然な適合ではありません。

于 2013-04-05T06:17:18.093 に答える
5

Protocolを使用して取得できます。これが最善の方法です。

プロトコルの作成方法の基本的な考え方を説明します

また、次の質問もお読みください: Objective-C でデリゲートを作成するにはどうすればよいですか?

次のコードは、プロトコルの基本的な考え方を示しています。以下のコードでは、ボタンのタイトルを からMasterViewControllerに取得できます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);

}
于 2013-04-04T14:44:19.813 に答える