iphone - 如何使用公共(public)目标对象来处理多个 View 的操作/导出?

标签 iphone model-view-controller interface-builder

我开始尝试在以前使用代码的地方使用 IB。我已经为 iPad/iPhone 纵向/横向创建了 Nib ,因此我有四种可能的 View 。所有文件所有者都设置为 UIViewController,所有对象都从调色板集添加到我的 NSObject 子类“TargetObject”。设置 IBActions/IBOutlets 没有问题,只需像往常一样按住 Ctrl 键并拖动即可在一个 Nib 中创建代码,然后右键单击该对象并将操作/导出拖动到其他 Nib 中的右侧控件,以便在所有 Nib 中都存在连接 Nib 。这个想法是应用程序委托(delegate)为当前设备启动适当的 View Controller ,然后该 View Controller 根据方向加载正确的 View 。

(也许对于自动调整大小掌握,这种方法是不必要的 - 我想知道是否可以这样做,因为让布局重新排列自己正好与那些 IB 控件是非常非常令人沮丧的。)

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

当我这样做时,问题出现在 View Controller 的 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 对象引用使用纵向 View 创建的对象,因此横向 View 正在寻找尚未通过引用保存的不同实例。在横向 View 中 _viewDelegate 为零 - 它是私有(private)的,所以我无法在代码中修复它。

我也可以通过引用来保持景观目标对象,但是每当方向发生变化时就会出现问题 - 如果有任何有状态控件(如 UITableView),数据源/委托(delegate)将发生变化,从而导致旋转时奇怪的用户体验。除非我确保目标对象仅实现简单的操作,并使用单独的共享对象来实现所需的任何 TableView 或 TextView 的数据源/委托(delegate)对象。

我想到的一件事是使目标成为单例。看起来像是一种黑客攻击,有时会无法工作。

使用通用目标对象对模型执行操作来创建作为 Nib 实现的多个 View 的正确方法是什么?

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

    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]);
    }
}

但还是感觉不太对劲。

最佳答案

Adam,听起来您想要一个代理(也称为外部)对象。

你的问题

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

因此,您的操作目标对象的第二个实例正在被释放,因为您没有捕获它。

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

天哪,该怎么办?!

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

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

如何做

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

代码

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: 中需要该类型的单元格。

关于iphone - 如何使用公共(public)目标对象来处理多个 View 的操作/导出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6950674/

相关文章:

javascript - sailsjs 在 Controller 工作时更新 View

xcode - 无法使用Xcode 6创建iPhone xib文件

ios - 我如何安排两个 UILabel 以便一个自动调整大小而另一个占用剩余空间?

ios - 是否可以在 UILabel/TextView 中输入 'hyperlink' 文本,但点击此 'hyperlink' 即可激活 segue?

iphone - iPhone应用程序由于后台定位模式而被拒绝

iPhone 3/4 像素完美

model-view-controller - 数据库和模型之间是否应该有一个抽象层?

python - 如何设置3层Web应用程序项目

iphone - View 上的任何触摸都会使其崩溃

iphone - 两个 View Controller 之间viewDidAppear和viewDidDisappear的回调顺序