首先,感谢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周围着陆,但找不到它。
我做错了什么?
避免拉出更多头发
答案 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];
}