将详细信息视图中的数据发送回我的UITableView

时间:2011-02-18 04:59:33

标签: iphone objective-c cocoa-touch uitableview uinavigationcontroller

首先,感谢God Over Stack Overflow。我是Objective-C / Cocoa / iPhone开发的新手,这个网站是一个很棒的帮助。

我有一个窗口,它有一个标签栏,有一个导航控制器,可以加载UITableViewController。单击导航栏上的“添加”按钮(以编程方式创建),按下这样按下UITableViewController:

InvoiceAddViewController *addController = [[InvoiceAddViewController alloc] initWithNibName:@"InvoiceAddViewController" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:addController animated:YES];

此表视图控制器推送它自己的详细信息视图:

    UITableViewCell *targetCell = [self.tableView cellForRowAtIndexPath:indexPath];

    GenericTextFieldDetailViewController *dvController = [[GenericTextFieldDetailViewController alloc] initWithNibName:@"GenericTextFieldDetailViewController" bundle:[NSBundle mainBundle]];

    dvController.fieldName = targetCell.textLabel.text;
    dvController.fieldValue = targetCell.detailTextLabel.text;

    [self.navigationController pushViewController:dvController animated:YES];
    [dvController release];

概念是,您单击表视图控制器中的单元格,例如“Notes”。这会将“GenericTextFieldDetailViewController”与您单击的“字段”的名称以及值(如果已存在)一起推送。这允许我重用我的详细信息视图,而不是为每个字段创建一个广告。

为了推送数据,我在“添加”UITableViewController上创建了一个方法:

- (void) updateField:(NSString*) fieldName value:(NSString*) fieldValue
{
    UITableViewCell *targetCell;
    if([fieldName isEqualToString:@"Invoice ID"])
    {
        NSUInteger indexArr[] = {1,1};
        targetCell = [[self tableView] cellForRowAtIndexPath:[NSIndexPath indexPathWithIndexes:indexArr length:2]];
        targetCell.detailTextLabel.text = fieldValue;
    }
    else if([fieldName isEqualToString:@"P.O. Number"])
    {
        NSUInteger indexArr[] = {1,2};
        targetCell = [[self tableView] cellForRowAtIndexPath:[NSIndexPath indexPathWithIndexes:indexArr length:2]];
        targetCell.detailTextLabel.text = fieldValue;
    }
    else if([fieldName isEqualToString:@"Add Note"])
    {
        NSUInteger indexArr[] = {3,0};
        targetCell = [[self tableView] cellForRowAtIndexPath:[NSIndexPath indexPathWithIndexes:indexArr length:2]];
        targetCell.detailTextLabel.text = fieldValue;
    }
}

此方法旨在接收我在“Generic”中使用此方法推送的数据:

- (IBAction)saveField:(id)sender
{
    self.fieldValue = theTextField.text;
    InvoiceAddViewController *parentController = (InvoiceAddViewController*)self.view.superview;
    [parentController updateField:self.fieldName value:self.fieldValue];
}

这给我们带来了问题: 当save方法触发时,它会抛出一个无效的选择器错误,因为self.view.superview不是推动“Generic”详细信息视图的UITableView。

我尝试了以下组合(来自GDB):

(gdb) po [[self view] superview]
<UIViewControllerWrapperView: 0x4b6d4d0; frame = (0 64; 320 367); autoresize = W+H; layer = <CALayer: 0x4b6c090>>
(gdb) po [[self navigationController] parentViewController]
<UITabBarController: 0x4d2fa90>
(gdb) po [self parentViewController]
<UINavigationController: 0x4d2fdc0>

我觉得我要在我想要调用的UITableView周围着陆,但找不到它。

我做错了什么?

避免拉出更多头发

4 个答案:

答案 0 :(得分:3)

问题是您混淆了导航堆栈的视图层次结构。您的详细信息视图控制器希望向控制器发送消息,将其推送到堆栈,这是导航控制器的viewControllers数组中倒数第二个对象。

尝试将saveField:方法更改为:

- (IBAction)saveField:(id)sender
{
    self.fieldValue = theTextField.text;
    NSArray *navigationStack = self.navigationController.viewControllers;
    InvoiceAddViewController *parentController = (InvoiceAddViewController*)[navigationStack objectAtIndex:navigationSTack.count - 2];
    [parentController updateField:self.fieldName value:self.fieldValue];
}

编辑:我应该注意这个设计非常脆弱。更好的方法是应用模型 - 视图 - 控制器并创建表示字段和标题值的对象。然后,您的InvoiceAddViewController可以将这些对象的实例传递给您的详细控制器,并且当您的详细控制器更改它们时,这些更改可以很容易地反映在您的其他控制器中。

编辑2:这是一个如何工作的提示。

   UITableViewCell *targetCell = [self.tableView cellForRowAtIndexPath:indexPath];

    GenericTextFieldDetailViewController *dvController = [[GenericTextFieldDetailViewController alloc] initWithNibName:@"GenericTextFieldDetailViewController" bundle:[NSBundle mainBundle]];

    dvController.dataObject = [self dataObjectForIndexPath:indexPath];
    [self.navigationController pushViewController:dvController animated:YES];
    [dvController release];

我假设您已经实现了dataObjectForIndexPath:方法。推测tableView:cellForRowAtIndexPath:也会使用此方法配置其单元格。

现在,您可以同时删除saveField:和updateField:方法。在InvoiceAddViewController中,viewWillAppear:可用于刷新您的视图,如下所示:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self.tableView reloadData]
}

这里有各种各样的可能性。 reloadData是一个非常沉重的人。尝试像KVO这样的东西,使其更加自动化。

在你的细节控制器中,当然不要忘记更新对象,说在视图中会消失做类似的事情

self.dataObject.fieldValue = theTextField.text;

这只是为了让你入门。有很多可能性和细节需要考虑。你应该看看很多示例代码,这个模式得到了很多使用。开发人员门户网站上的CoreDataBooks示例使用了类似的模式,几乎可以肯定其他模式。

答案 1 :(得分:0)

您应该使用通知,KVO或授权。使用适合在此平台上传递数据的设计模式。即使您可以使用其中一种模式直接进行呼叫,也绝对可以证明是以封装方式实现此目的的更好方法。

答案 2 :(得分:0)

这种方法只是为代表团而尖叫。在详细视图控制器上创建协议,声明类似

的方法

- (void)genericTextFieldDetailViewController:(GenericTextFieldDetailViewController *)controller didUpdateValue:(NSString *)value forField:(NSString *)field

现在,当您准备好发回数据时,您只需将此消息发送给您的代理人(推送视图控制器)。

[self.delegate genericTextFieldDetailViewController:self didUpdateValue:newValue forField:passedInField]

确保在创建和推送详细视图控制器时,将自己指定为委托并实现该方法来处理传入值。我想你会发现这种方法更灵活。

答案 3 :(得分:0)

我遇到了同样的问题,但没有表视图。我想从最后一个视图控制器更改一个值。我正在使用Xcode 5:

 {

    NSArray *navigationStack = self.navigationController.viewControllers;
    ViewController *control = [navigationStack objectAtIndex:([navigationStack count] -2)];
    control.pagecont.currentPage = self.currIndex;
    CGRect frame;
    frame.origin.x = control.scroll.frame.size.width * control.pagecont.currentPage;
    frame.origin.y = 50;
    frame.size = control.scroll.frame.size;

    [control.scroll scrollRectToVisible:frame animated:YES];

    [self.navigationController popViewControllerAnimated:YES];

}