如何使用公共目标对象来处理多个视图的操作/出口?

时间:2011-08-05 02:08:18

标签: iphone model-view-controller interface-builder

我开始尝试使用以前使用过代码的IB。我已经为iPad / iPhone Portrait / Landscape创建了笔尖,所以我有四种可能的视图。所有文件所有者都设置为UIViewController,都有一个对象从调色板集添加到我的NSObject子类“TargetObject”。设置IBActions / IBOutlets没问题,只需按住Ctrl键拖动即可在一个笔尖中创建代码,然后右键单击该对象并将操作/出口拖动到其他笔尖中的右控件,以便所有连接都存在碎粒。我们的想法是,app委托启动当前设备的相应视图控制器,然后该视图控制器根据方向加载正确的视图。

(也许有自动掌握这种方法是没有必要的 - 我想知道是否有可能这样做,因为让布局重新安排自己恰到好处的那些IB控件是非常非常令人沮丧的。)

这些一对多视图和目标对象背后的想法是我可以在应用程序的任何位置使用它们。例如,我显示的内容是核心数据中的对象 我想在应用程序的多个位置允许详细查看/编辑。这似乎是一种非常MVC的做事方式。

当我这样做时,问题出现在视图控制器的loadView中:

NSArray *portraitViewArray = [[NSBundle mainBundle] loadNibNamed:@"Weekview_iPad_Portrait" owner:self options:nil];
portraitWeekView = [portraitViewArray objectAtIndex:0];

NSArray *landscapeViewArray = [[NSBundle mainBundle] loadNibNamed:@"Weekview_iPad_Landscape" owner:self options:nil];
landscapeWeekView = [landscapeViewArray objectAtIndex:0];

// this object is intended to be a common target for portrait and landscape arrays. 
weekViewTarget = [portraitViewArray objectAtIndex:1];

UIInterfaceOrientation interfaceOrientation = self.interfaceOrientation;

if(interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
    self.view = portraitWeekView;
else 
    self.view = landscapeWeekView;

正如您所猜测的那样,纵向方向的一切都很好但在景观中与目标对象的任何交互都会崩溃。 weekViewTarget对象引用使用纵向视图创建的对象,因此横向视图正在查找未使用引用保留的其他实例。在横向视图中_viewDelegate是nil - 它是私有的,所以我无法在代码中修复它。

我也可以使用引用来保持横向目标对象,但是每当方向改变时都会出现问题 - 如果有任何有状态控件(如UITableView),数据源/委托将发生变化,从而导致轮换时的奇怪用户体验。除非我确保目标对象只实现简单的操作,并使用单独的共享对象为任何表视图或所需的文本视图实现数据源/委托对象。

我想到的一件事就是让目标成为单身人士。看起来像一个黑客,这将是无法工作的时间。

使用公共目标对象创建多个以nib实现的视图的正确方法是什么?

  • 进展 - 如果我给每个目标一个指向其自己类型的指针,事情就会开始起作用:

    WeekViewTarget *forwardTarget;
    

并让真实处理程序的forwardTarget = nil在未使用的处理程序的forwardTarget设置如下:

weekViewTarget = [portraitViewArray objectAtIndex:1];
forwardedWeekViewTarget = [landscapeViewArray objectAtIndex:1];
forwardedWeekViewTarget.forwardTarget = weekViewTarget;

然后在每个IBAction中执行以下操作:

- (IBAction)timeBlockTapped:(id)sender {

    if(forwardTarget != nil) {
        [forwardTarget performSelector:@selector(timeBlockTapped:) withObject:sender];
    } else {
        NSLog(@"Got a tap event with sender %@", [sender description]);
    }
}

虽然感觉不对。

2 个答案:

答案 0 :(得分:17)

Adam,听起来你想要一个代理(又称外部)对象。

您的问题

NSBundle和UINib在从NIB实例化对象图时返回自动释放的数组。在事件循环结束时释放数组时,将释放您未专门保留的任何对象。显然,只要保留所有者,就会保留笔尖内相互引用的对象。

所以你的动作目标对象的第二个实例正在被释放,因为你没有挂在它上面。

您真正想要的只是两个NIB都可以引用的目标对象的单个实例。因此,您无法让NIB实例化该对象的实例。但是你的NIB需要能够引用该对象实例!

亲爱的,该怎么办?!

解决方案:代理/外部对象

基本上,外部对象是您放置在NIB中的代理。然后,您在实例化实例化NIB的对象图时输入。 NIB加载程序用您外部提供的实例替换NIB中的代理,并配置您在Interface Builder中分配给代理的任何目标或出口。

如何做到

  1. 在Interface Builder中拖动“外部对象”而不是NSObject实例。它将与Xcode 4.x中的文件所有者一起显示在上部。
  2. 选择它并在“Identity Inspector”中将其类设置为适当的类型。
  3. 在“属性检查器”中,将标识符字符串设置为“TargetObject”(或者您要在下面的代码中使用的任何字符串)。
  4. 利润!
  5. 代码

    id owner = self; // or whatever
    id targetObject = self.targetObject; // or whatever...
    
    // Construct a dictionary of objects with strings identifying them.  
    // These strings should match the Identifiers you specified in IB's 
    // Attribute Inspector.
    NSDictionary *externalObjects = [NSDictionary dictionaryWithObjectsAndKeys:
                                        targetObject, @"TargetObject",
                                        nil];
    
    // Load the NIBs ready for instantiation.
    UINib *portraitViewNib = [UINib nibWithNibName:@"Weekview_iPad_Portrait" bundle:nil];
    UINib *landscapeViewNib = [UINib nibWithNibName:@"Weekview_iPad_Landscape" bundle:nil];
    
    // Set up the NIB options pointing to your external objects dictionary.
    NSDictionary *nibOptions = [NSDictionary dictionaryWithObjectsAndKeys:
                                   externalObjects, UINibExternalObjects, 
                                   nil];
    
    // Instantiate the objects in the nib passing in the owner 
    // (coincidentally also a proxy in the NIB) and your options dictionary.
    NSArray *portraitViewArray =  [portraitViewNib instantiateWithOwner:owner
                                                                options:nibOptions];
    NSArray *landscapeViewArray = [landscapeViewNib instantiateWithOwner:owner
                                                                 options:nibOptions];
    
    self.view = [(UIInterfaceOrientationIsPortrait(interfaceOrientation)) ? portraitViewArray : landscapeViewArray) objectAtIndex:0];
    

    注释

    您正在使用NSBundle加载NIB。你应该使用UINib。读取NIB文件要快得多。此外,它将NIB加载与对象实例化分开。这意味着您可以在init,awakeFromNib或viewDidLoad中加载NIB,而不是实例化NIB的内容。然后在你需要NIB中的实际对象的最后一刻,你可以调用instantiateWithOwner。

    在使用UITableViewController的情况下,您可以在viewDidLoad中加载表格单元格nib但实际上不实例化它们,直到在-tableView中需要该类型的单元格:cellForRowAtIndexPath:。

答案 1 :(得分:0)

在interfaceOrientation的基础上,您可以识别方向并重置xib中控件的帧数。 For more, this will guide you...