4

我已经阅读了很多关于使用块的积极内容——特别是它通过消除委托调用来简化代码。我发现了在动画结束时使用块而不是委托调用的示例 - 我了解它是如何完成的。

但我真的很想知道在呈现和关闭视图控制器时必须使用委托的繁琐方案是否也可以通过块来简化。

显示和解除方案的标准推荐方法如下所示,其中在 VC1 中呈现一个新的 VC2,该新 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 可以自行解除。因此,[self.delegate vc2HasFinished];您可以[self dismissModalViewControllerAnimated:YES completion: nil];在 vc2.

但我同意你的观点,即块有用而委托很笨拙(而且更容易出错,尤其是在 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 上的属性/变量,因此您可以拥有一个完成块,VC1 将其设置为它想要的任何内容,并且completionBlock();一旦完成,VC2 就可以调用它。

基本上:

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

@property (nonatomic, copy) VC2CompletionBlock completionBlock;

@end

然后在 VC2.m 的某个地方你可以调用

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

对你的块进行类型定义允许你创建一种新的自定义类型的块,可能是一个带有返回值或其他参数的块,然后可以通过 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 上的一个类别,现在您的所有视图控制器都将获得此功能。您可以使用通知中心来调用适当的块...

@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”的便利(您可以显式设置它),并且您可以获得用于呈现的“convenience”方法,并且每个视图控制器都会自动获得此功能,只需添加类别即可。

于 2012-09-02T18:43:45.723 回答
-1

关闭视图控制器的推荐方法是dismissViewControllerAnimated:completion。所以这是你在寻找的街区。

于 2012-09-02T17:01:14.907 回答