假设我有几个属于UIViewController的对象(模型)。当其中一个模型更改或执行某些操作时,我希望它通知UIViewController,以便视图可以立即更新,或者执行一些动画作为响应。
我该怎么做?具体地,
(1。)单个对象如何知道ViewController?如果我在每个对象中存储一个指向ViewController的指针,那么我将介绍一个循环引用,其中ViewController引用引用ViewController的子节点。
(2。)即使使用弱指针,模型对象本身也不知道ViewController中的哪个视图对应于它自己。例如,假设有10个视图和10个模型。模型没有与视图耦合(即每个模型都有一个视图,表示它在10的数组中,但索引并不总是相同,因为用户不断切换它们,所以虽然模型有一个稳定索引0 ... 9,表示模型0的视图可以在索引1,或稍后在索引2,3,...,9),所以当模型更新时,它需要以某种方式告诉ViewController哪个10个视图代表它,除了它不知道。因此ViewController反过来无法知道要更新哪个View。
我应该补充说,我当前的代码只是给每个模型对象一个指向其相应View的指针,因此它不需要通过ViewController来访问正确的View并更新其内容。但这似乎违反了MVC,所以我试图纠正我的代码,并意识到我不知道在这种特殊情况下如何。
很抱歉,如果这令人困惑。如果有什么不清楚,我会更新问题。 编辑:更新了更具体的示例代码。
@interface ViewController : UIViewController<ProfileViewControllerDelegate>
{
BattleModel* battle;
}
@property (nonatomic, strong) BattleModel* battle;
@property (nonatomic, weak) NSArray* heroPersons; // Person objects
@property (nonatomic, weak) NSArray* villainPersons; // Person objects
@property (nonatomic, strong) IBOutletCollection(PersonImageView) NSArray* villainImages;
@property (nonatomic, strong) IBOutletCollection(HealthBarView) NSArray* villainHealthBars;
@property (nonatomic, strong) IBOutletCollection(PersonImageView) NSArray* heroImages;
@property (nonatomic, strong) IBOutletCollection(HealthBarView) NSArray* heroHealthBars;
@property (weak, nonatomic) IBOutlet UITextView* reporter;
描述了ViewController接口。现在,每个Person类都处理Person的修改,以及Person对攻击的反应。
@interface Person : NSObject
{
__weak Profile* profile;
__weak PersonImageView* personImg;
__weak BattleModel* currentBattle;
Reaction* currentReaction;
}
// Reaction happens in the following methods:
-(BOOL) targetedFoeWithMove:(SelectedMove*)selMove log:(UITextView*)reporter;
-(BOOL) wasTargetedByMove:(SelectedMove*)selMove log:(UITextView*)reporter;
-(void) hitFoeWithMove:(SelectedMove*)selMove log:(UITextView*)reporter;
-(void) wasHitByMove:(SelectedMove*)selMove log:(UITextView*)reporter;
正如您所看到的,我目前违反了MVC,因为记者被传递到模型Person,还有一个指向VC应该处理的personImg的指针。无论如何,随着战斗的发生,在各种有针对性的命中程序中查询Reaction *,这就是修改发生的地方,并通知记者。注意Person在没有任何IDEA的情况下如何在宏方案UIViewController中占用相应的PersonView索引,所以当currentReaction在某个深度例程中触发并传递数据时,ViewController不知道要更新哪个UIView。它在某一点上知道(当Person被创建时),但是heroImages和villainImages数组被转移了很多(如果一个人从英雄转向反派,甚至转换侧面),所以它出生时的指数不是指数它必然在Reaction触发并需要更新PersonImageView时。这有点令人烦恼,所以我可能需要将数据传回UIViewController,告诉它哪个PersonImageView刚刚接受了一个反应(我该怎么做?)。
这是一个反应示例,只是为了完整性并帮助您理解系统:
-(void) wasHitByMove:(SelectedMove*)selMove log:(UITextView*)reporter
{
[self.currentReaction personWasHitByMove:selMove inBattle:self.currentBattle log:reporter];
}
并且取决于反应是否相关(反应的几个子类做了不同的事情),通过Person属性设置器调用类似“loseLife:(int)life”的修改,这就是我需要报告的时候/动画要冒泡到VC。现在,错误的代码(糟糕的设计)只是直接转到personImg的动画,然后是记者的文本通知。我意识到这是糟糕的设计,这就是为什么我在这里,但它并不像通过回调告诉VC那么简单,“(BOOL)嘿,发生在我身上的事情。”因为哪个PersonImageView是“我”? ViewController如何在自己的方法中执行战斗逻辑,知道立即响应回调?回调怎么样呢? (Re:Delegates。所以将委托协议附加到Person,PersonImageView和UIViewController?既然它们都遵循协议,那么他们可以通过协议方法解释公共消息吗?)
我也查看过NSNotification,但发现这个目的太复杂了;看起来它更像是被动的Observer类型的东西,在我的应用程序中,当一个Reaction触发时,我需要UIViewController 立即使用UITextView文本+ PersonImageView动画更新屏幕。在反应可以再次触发之前,动画需要立即发生,因为它可能涉及PersonImageView反弹,掉落或改变外观。它不是一个实时游戏,但是下一个攻击已经排队,并且在之前的攻击触发currentReaction之后会暂时发生,所以任何类型的延迟或被动观察都可能是坏的。
答案 0 :(得分:1)
委托。
假设您有一个带按钮的视图。给它一个协议:
@protocol MyButtonViewDelegate
@required
- (void)buttonPressed;
@end
现在,带按钮的视图具有如下属性:
@property (nonatomic, weak) NSObject<MyButtonViewDelegate> *delegate;
它是对视图控制器的引用。
带按钮类的视图将有一个这样的方法,它连接到按钮:
- (IBAction)buttonPressed:(id)sender {
[self.delegate buttonPressed];
}
您的视图控件符合MyButtonViewDelegate
协议,并将自身设置为视图的委托。然后它实现方法buttonPressed
:
- (void)buttonPressed {
// do whatever you want to do when that button was pressed
}
如果您需要在此方法中传递对视图的引用,您也可以这样做:
@protocol MyViewDelegate
@required
- (void)someMethod:(id)sender;
@end
然后:
- (IBAction)someUserInteractionWithView {
[self.delegate someMethod:self];
}
委托然后从协议实现该方法:
- (void)someMethod:(id)sender {
// sender is a reference to the view which called this method on delegate
}
您可以编写协议方法以根据需要发送任意数量的参数。您甚至可以使用这些方法返回您使用的值:
@protocol MyResizableViewProtocol
@required
- (CGRect)newSizeForResizableView:(MyResizableView*)resizableView;
@end
视图类中的某些内容触发在委托上调用此方法并请求新大小。委托实现此方法:
- (CGRect)newSizeForResizableView:(MyResizableView*)resizableView {
return CGRectMake(0.0f, 0.0f, 100.0f, 400.0f);
}
可调整大小的视图使用了这样的方法:
- (void)timeToResize {
self.frame = [self.delegate newSizeForResizableView:self];
}
就我而言,理解协议和代理是iOS开发的一个重要方面。
答案 1 :(得分:1)
有几种方法可以做到这一点。一种方法是通知。当这些有趣的事件发生时,让您的模型发出通知。然后,您的视图控制器可以注册接收这些通知并采取相应措施。
在每个模型中,执行以下操作:
[[NSNotificationCenter defaultCenter] postNotificationName:@"somethingInModel3" object:self];
在视图控制器中,在init
或viewDidLoad
中执行以下操作:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleModel3Event:)
name:@"somethingInModel3"
object:nil];
选择器指定将处理通知的方法。您需要根据需要编写该方法。
您还需要(例如,在视图控制器中的dealloc
中):
[[NSNotificationCenter defaultCenter] removeObserver:self];
答案 2 :(得分:1)
Bocks是在objective-c中实现回调的好方法。 Here is a link to Apple's documentation。 Apple已经通过NSOperationQueue
,NSOperation
,查看动画和许多其他API在其API上实现了块。在iOS中查看此article about communication patterns,它解释了使用委派,阻止,通知等的正确环境。