我试图从子视图控制器中调用我的根视图控制器中的方法,这样当我更改选项时,它们将自动更新根视图,而根视图将更新其他几个视图控制器。对于第二部分,我使用了通知,但为此我首先尝试使用委托,因为它(所以我一直相信)是一个很好的编程习惯。我无法使其工作,并且知道我可以轻松地设置另一个通知来完成这项工作。我应该继续尝试实施代理还是仅使用通知?
答案 0 :(得分:58)
对许多情况来说,委派是一种很好的编程习惯,但这并不意味着如果你不熟悉它就必须使用它。委托和通知都有助于将视图控制器彼此分离,这是一件好事。通知可能更容易编码,并提供多个对象可以观察一个通知的优势。对于代理人来说,如果不修改委托对象就不可能做到这一点(并且很不寻常)。
委派的一些好处:
只有您可以决定哪种模式更适合您。在任何情况下,您都应该考虑不让视图控制器发送通知或委托消息。在许多情况下,视图控制器应该更改模型,然后模型应该通知其观察者或其代理它已被更改。
实现委托模式很简单:
在您的ChildViewController.h中,声明委托必须在以后实现的委托协议:
@protocol ChildViewControllerDelegate <NSObject>
@optional
- (void)viewControllerDidChange:(ChildViewController *)controller;
@end
在文件的顶部,创建一个实例变量来保存指向ChildViewController中委托的指针:
@protocol ChildViewControllerDelegate;
@interface ChildViewController : UIViewController {
id <ChildViewControllerDelegate> delegate;
...
}
@property (assign) id <ChildViewControllerDelegate> delegate;
...
@end
在RootViewController.h中,使您的类符合委托协议:
@interface RootViewController : UIViewController <ChildViewControllerDelegate> {
...
在RootViewController实现中,实现委托方法。此外,在创建ChildViewController实例时,必须分配委托。
@implement RootViewController
...
// in some method:
ChildViewController *controller = [[ChildViewController alloc] initWithNibName:...
controller.delegate = self;
...
- (void)viewControllerDidChange:(ChildViewController *)controller {
NSLog(@"Delegate method was called.");
}
...
在ChildViewController实现中,在适当的时间调用委托方法:
@implementation ChildViewController
...
// in some method:
if ([self.delegate respondsToSelector:@selector(viewControllerDidChange:)]) {
[self.delegate viewControllerDidChange:self];
}
...
就是这样。 (注意:我已经从内存中写了这个,所以可能有一些拼写错误/错误。)
答案 1 :(得分:25)
我想补充一下:
接收通知的对象可以 事件发生后才做出反应 发生了。这是一个重要的 与代表团的区别。该 代表有机会拒绝 或修改提议的操作 委托对象。观察 另一方面,物体不能 直接影响即将来临 操作
答案 2 :(得分:11)
通常,如果您需要根据模型中数据的更改来更新UI,您可以让视图控制器观察相关的模型数据,并在收到更改通知时更新其视图。
我认为代表团更正式,更像Peter Hosey最近分享的区别:
区别在于授权 for to-one(和双向) 沟通,而通知 是为了多对,单向 通信。
此外,我发现(完全)更新viewWillAppear:
中的视图工作正常(但这不是性能受到关注的最佳解决方案)。
答案 3 :(得分:6)
通知可以使程序的运行时行为变得更加复杂。可以把它想象成具有多个目的地的goto。未定义这些目的地的顺序。如果您遇到崩溃,那么堆栈跟踪信息很少。
在某些情况下,使用通知是有意义的 - 通常是将模型更改或全局状态更改传达给您的视图。例如,网络已关闭,应用程序将辞职等等!
在iOS中学习委托模式是值得的。代理在调试时为您提供完整的堆栈跟踪。它们可以显着简化运行时行为,同时仍然实现解耦对象的目标。
答案 4 :(得分:2)
代表们有点难以习惯,但我认为这是最好的做法,就像Apple一样,他们只是工作。
我总是使用正式的协议声明。这在我的脑海里更合乎逻辑,而且在代码中非常清楚。 我建议使用UIView来更改您的选项而不是控制器。我总是使用一个主控制器并且有一个控制器可以控制的许多子类UIView。(但是,您可以修改控制器的以下代码,如果您确实需要控制器而不是正常视图。)在子视图的头文件中,使其如下所示:
// ChildView.h
#import <UIKit/UIKit.h>
@protocol ChildViewDelegate; // tells the compiler that there will be a protocol definition later
@interface ChildViewController : UIView {
id <ChildViewDelegate> delegate;
// more stuff
}
// properties and class/instance methods
@end
@protocol ChildViewDelegate // this is the formal definition
- (void)childView:(ChildView *)c willDismissWithButtonIndex:(NSInteger)i; // change the part after (ChildView *)c to reflect the chosen options
@end
可以在ChildView的实现中的某处调用@protocol
和第二个@end
之间的方法,然后您的根视图控制器可以是接收“通知”的委托。
.m文件应该是这样的:
// ChildView.m
#import "ChildView.h"
@implementation ChildView
- (id)initWithDelegate:(id<ChildViewDelegate>)del { // make this whatever you want
if (self = [super initWithFrame:CGRect(0, 0, 50, 50)]) { // if frame is a parameter for the init method, you can make that here, your choice
delegate = del; // this defines what class listens to the 'notification'
}
return self;
}
// other methods
// example: a method that will remove the subview
- (void)dismiss {
// tell the delegate (listener) that you're about to dismiss this view
[delegate childView:self willDismissWithButtonIndex:3];
[self removeFromSuperView];
}
@end
然后根视图控制器的.h文件将包含以下代码:
// RootViewController.h
#import "ChildView.h"
@interface RootViewController : UIViewController <ChildViewDelegate> {
// stuff
}
// stuff
@end
实现文件将实现ChildView.h中协议中定义的方法,因为它将在ChildView调用它时运行。在那种方法中,把你收到通知时发生的东西放好。
答案 5 :(得分:0)
在这种情况下,您不需要使用委派或通知,因为您实际上不需要在视图之间直接通信。正如gerry3所说,您需要更改数据模型本身,然后让所有其他视图响应该更改。
您的数据模型应该是所有视图控制器都可以访问的独立对象。 (懒惰的方式是将其作为app委托的属性驻留。)当用户在View A中进行更改时,View A的控制器会将该更改写入数据模型。然后,只要打开视图B到Z,他们的控制器就会读取数据模型并适当地配置视图。
这样,视图和控制器都不需要彼此了解,并且所有更改都发生在一个中心对象中,因此可以轻松跟踪它们。
答案 6 :(得分:0)
您的根视图控制器是否真的需要了解更改,或仅了解子视图?
如果根控制器不必知道,让设置发出通知,其他视图正在寻找对我来说似乎是一个更好的答案,因为它简化了代码。没有必要引入比你更多的复杂性。