4

私はブロックの使用について非常に多くの肯定的なことを読みました-特にそれはデリゲート呼び出しを排除することによってコードを単純化します。アニメーションの最後で、デリゲート呼び出しの代わりにブロックが使用されている例を見つけました。そこで、どのように実行できるかを理解しています。

しかし、ビューコントローラを表示および非表示にするときにデリゲートを使用しなければならないという面倒なスキームも、ブロックを使用して簡略化できるかどうかを知りたいと思います。

スキームを表示および却下するための標準的な推奨方法は次のようになります。VC1では新しいVC2が提示され、VC1の代理人によって再度却下されます。

  VC2 *vc2 = [[VC2 alloc] initWithNibName:@"VC2" bundle:nil ];
  vc2.delegate = self; 
  [self presentModalViewController: vc2 animated: YES]; // or however you present your VC2

Withis VC2がvc1に戻ります:

[self.delegate vc2HasFinished];

これを機能させるには、次のようなプロトコルを作成する必要があります。VC2Protocol.hファイル内

 @protocol VC2Protocol <NSObject>
    -(void)vc2HasFinished;
    @end

次に、これ VC2Protocol.hをVC1とVC2に含めます。VC1では、次のようにこのメソッドを定義する必要があります。

-(void) weitereRufNrVCDidFinish{
    [self dismissModalViewControllerAnimated:YES completion: nil];

}

そのためだけにプロトコルを宣言する必要がなく、より簡潔なコードを記述できれば本当に素晴らしいでしょう。

ありがとう!

4

4 に答える 4

4

モーダルに提示されたvcを却下する場合、vcがそれ自体を却下する可能性があることに注意してください。したがって、代わりにvc2内[self.delegate vc2HasFinished];で言うことができます。[self dismissModalViewControllerAnimated:YES completion: nil];

しかし、私はあなたに同意します。ブロックは有用であり、デリゲートは不器用です(そして、特にARC以前ではエラーが発生しやすくなります)。したがって、VCでデリゲートコールバックを置き換える方法は次のとおりです。たとえば、画像をフェッチしたばかりであるなど、vcがデリゲートに何かを伝えたい状況を考えてみましょう...

// vc2.h
@property (nonatomic, copy) void (^whenYouFetchAnImage)(UIImage *);
// note, no delegate property here

// vc2.m
// with your other synthesizes
@synthesize whenYouFetchAnImage=_whenYouFetchAnImage;

// when the image is fetched
self.whenYouFetchAnImage(theFetchedImage);

提示するvcはデリゲートを設定しませんが、イメージがフェッチされたときに(独自の実行コンテキストで)実行するコードを新しいvcに提供します...

// presenting vc.m
VC2 *vc2 = [[VC2 alloc] initWithNibName:@"VC2" bundle:nil];

// say this presenting vc has an image view that will show the image fetched
// by vc2.  (not totally plausible since this image view will probably be covered by vc2
// when the block is invoked)
vc2.whenYouFetchAnImage = ^(UIImage *image) { self.myImageView.image = image; };
于 2012-09-02T17:48:42.777 に答える
3

ブロックはVC2のプロパティ/ivarsとして使用できるため、VC1が希望どおりに設定するcompletionBlockを作成すると、VC2は完了したときにそれを呼び出すことができますcompletionBlock();

基本的に:

typedef void (^VC2CompletionBlock)(void);
@interface VC2 : UIViewController {
     VC2CompletionBlock completionBlock;
}

@property (nonatomic, copy) VC2CompletionBlock completionBlock;

@end

そして、VC2.mのどこかで呼び出すことができます

...
self.completionBlock();
...

ブロックをtypedefすると、新しいカスタムタイプのブロックを作成できます。おそらく、戻り値やその他のパラメーターを使用して、VC2でブロックに渡すことができます。

typedef void (^VC2CompletionBlock)(BOOL success, NSData data);

オブジェクト自体はブロックの基本構造(つまり、取り込むことができるパラメーター)を知っているだけでよく、ブロックや作成者に関する情報は必要ないため、これが役立つことを願っています。ブロックの使用は強力です。

ただし、ブロックは奇妙なメモリの問題を引き起こす可能性があるため、ブロックに関する適切なドキュメントを必ずお読みください。

于 2012-09-02T17:47:50.593 に答える
2

すべてのコントローラーで特定のブロックiVarを作成しなくても、一般的な方法でこれを解決できます。「完了時」のブロック処理を提供するクラスを作成し、それから継承するだけで、ビューコントローラはすべて「完了時」の機能を持ちます。プロパティを設定するか、「便利な」メソッドを提供するだけです。

この最初の部分の元のコードは壊れたばかりなので、変更しました。-うーん。恥ずかしい。とにかく、あなたはその考えを理解します(そして私はあなたが協会を嫌う人である場合にのみこれを提案します)。

// Use this as a base class for your view controllers...
typedef void(^WhenDoneWithViewControllerBlock)(
    UIViewController *viewController,
    BOOL canceled);
@interface BlockDismissingViewController : UIViewController
@property (nonatomic, strong) WhenDoneWithViewControllerBlock whenDone;
- (void)done:(BOOL)canceled;
@end

@implementation BlockDismissingViewController
- (void)done:(BOOL)canceled {
    if (self.whenDone) {
        self.whenDone(self, canceled);
    }
}
@end

// The "convenience" method should probably be something like this...
@implementation UIViewController (BlockDismissingViewController)
- (void)presentViewController:(BlockDismissingViewController *)viewControllerToPresent
                     animated:(BOOL)flag
                   completion:(void (^)(void))completion
                     whenDone:(WhenDoneWithViewControllerBlock)whenDone {
    viewControllerToPresent.whenDone = whenDone;
    [self presentViewController:viewControllerToPresent
                        animated:flag
                      completion:completion];
}
@end

または、UIViewControllerのカテゴリとして実行すると、すべてのViewControllerがこの機能を利用できるようになります。通知センターを使用して、適切なブロックを呼び出すことができます...

@interface UIViewController (WhenDoneWithViewControllerBlock)
- (void)done:(BOOL)canceled;
@end

@implementation UIViewController (WhenDoneWithViewControllerBlock)
- (void)presentViewController:(UIViewController *)viewControllerToPresent
                     animated:(BOOL)flag
                   completion:(void (^)(void))completion
                     whenDone:(WhenDoneWithViewControllerBlock)doneBlock {
    if (doneBlock) {
        __block id observer = [[NSNotificationCenter defaultCenter]
                               addObserverForName:@"DoneWithViewControllerNotification"
                               object:viewControllerToPresent
                               queue:nil
                               usingBlock:^(NSNotification *note) {
            [[NSNotificationCenter defaultCenter] removeObserver:observer];
            doneBlock(viewControllerToPresent, [[note.userInfo objectForKey:@"canceled"] boolValue]);
        }];
    }
    [self presentViewController:viewControllerToPresent
                       animated:flag
                     completion:completion];
}

- (void)done:(BOOL)canceled {
    [[NSNotificationCenter defaultCenter]
        postNotificationName:@"DoneWithViewControllerNotification"
                      object:self
                    userInfo:@{ @"canceled" : @(canceled) }];
}
@end

または、カテゴリが必要であるが、iVarが必要で、通知センターをバイパスする場合...

// Using associated objects in a category
@interface UIViewController (WhenDoneWithViewControllerBlock)
@property (nonatomic, strong) WhenDoneWithViewControllerBlock whenDone;
- (void)done:(BOOL)canceled;
@end
@implementation UIViewController (WhenDoneWithViewControllerBlock)
char const kWhenDoneKey[1];
- (WhenDoneWithViewControllerBlock)whenDone {
    return objc_getAssociatedObject(self, kWhenDoneKey);
}
- (void)setWhenDone:(WhenDoneWithViewControllerBlock)whenDone {
    objc_setAssociatedObject(self, kWhenDoneKey, whenDone, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)presentViewController:(UIViewController *)viewControllerToPresent
                     animated:(BOOL)flag
                   completion:(void (^)(void))completion
                     whenDone:(WhenDoneWithViewControllerBlock)whenDone {
    viewControllerToPresent.whenDone = whenDone;
    [self presentViewController:viewControllerToPresent animated:flag completion:completion];
}

- (void)done:(BOOL)canceled {
    if (self.whenDone) {
        self.whenDone(self, canceled);
    }
}
@end

もちろん、これらは単なる例ですが、うまくいけば、あなたはアイデアを得ることができます。

ビューコントローラが完了すると、それはただ呼び出します

[self done:canceledOrSuccess];

ブロックが呼び出されます。

関連するオブジェクトの時間とメモリの両方にパフォーマンスコストがかかりますが、最後のカテゴリを使用するのが私のお気に入りです。「whenDone」ブロックを保持する「iVar」の便利さ(明示的に設定できます)を取得し、表示するための「便利な」メソッドを取得します。すべてのView Controllerは、カテゴリを追加するだけで、この機能を自動的に取得します。

于 2012-09-02T18:43:45.723 に答える
-1

ビューコントローラを閉じるための推奨される方法は、dismissViewControllerAnimated:completionです。だからここにあなたが探しているあなたのブロックがあります。

于 2012-09-02T17:01:14.907 に答える