从包含的对象中调用UIViewController方法

时间:2014-01-30 01:33:18

标签: ios iphone objective-c model-view-controller uiviewcontroller

假设我有几个属于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之后会暂时发生,所以任何类型的延迟或被动观察都可能是坏的。

3 个答案:

答案 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];

在视图控制器中,在initviewDidLoad中执行以下操作:

[[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已经通过NSOperationQueueNSOperation,查看动画和许多其他API在其API上实现了块。在iOS中查看此article about communication patterns,它解释了使用委派,阻止,通知等的正确环境。