将数据从我的详细视图发送回我的 UITableView
首先,感谢上帝提供了 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];
概念是,您单击表视图控制器中的单元格,例如“注释” 。这将使用您单击的“字段”的名称和值(如果已存在)推送“GenericTextFieldDetailViewController”。这使我能够重复使用我的详细视图,而不是为每个字段创建一个令人作呕的广告。
为了将数据推回,我在“Add”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 不是推送“通用”详细视图的 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 着陆,但找不到它。
我做错了什么?
不要拔出更多的头发
First of all, thank God for Stack Overflow. I am new at Objective-C/Cocoa/iPhone development, and this site is an amazing aid.
I have a window, that has a tab bar, that has a navigation controller, that loads a UITableViewController. Clicking an "Add" button on the navigation bar (created programatically), pushes a UITableViewController like so:
InvoiceAddViewController *addController = [[InvoiceAddViewController alloc] initWithNibName:@"InvoiceAddViewController" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:addController animated:YES];
This table view controller pushes it's own detail view:
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];
The concept being, you click on a cell in the table view controller such as "Notes". This pushes "GenericTextFieldDetailViewController" with the name of the "field" you clicked, and the value (if one already exists). This allows me to reuse my detail view rather than creating one ad nauseum for every field.
In order to push data back, I created a method on the "Add" 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;
}
}
This method is designed to receive the data I push with this method in "Generic":
- (IBAction)saveField:(id)sender
{
self.fieldValue = theTextField.text;
InvoiceAddViewController *parentController = (InvoiceAddViewController*)self.view.superview;
[parentController updateField:self.fieldName value:self.fieldValue];
}
Which brings us to the problem:
When the save method fires off, it throws an invalid selector error because self.view.superview is not the UITableView that pushed the "Generic" detail view.
I have tried the following combinations (from 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>
I feel like I'm landing all around the UITableView I want to invoke, but can't find it.
What am I doing wrong?
refrains from pulling more hair out
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
问题是您混淆了导航堆栈的视图层次结构。您的详细视图控制器想要向将其推送到堆栈上的控制器发送一条消息,该控制器是导航控制器的 viewControllers 数组中倒数第二个对象。
尝试将您的 saveField: 方法更改为:
编辑:我应该注意到这个设计非常脆弱。更好的方法是应用模型-视图-控制器并创建一个表示字段和标题值的对象。然后,您的 InvoiceAddViewController 可以将这些对象的实例传递到您的详细信息控制器,并且当您的详细信息控制器更改它们时,这些更改可以轻松地反映在您的其他控制器中。
编辑 2:这是它如何工作的提示。
我假设您已经实现了 dataObjectForIndexPath: 方法。据推测 tableView:cellForRowAtIndexPath: 也会使用此方法来配置其单元格。
现在,您可以消除 saveField: 和 updateField: 方法。在您的 InvoiceAddViewController 中, viewWillAppear: 可用于刷新您的视图,如下所示:
这里有一个充满可能性的世界。 reloadData 是一个非常严厉的方法。尝试使用 KVO 之类的东西来使其更加自动化。
当然,在您的详细控制器中,不要忘记更新对象,比如视图将消失,以执行类似的操作,
这只是为了让您开始。有很多可能性和细节需要考虑。您确实应该查看大量示例代码,这种模式被大量使用。开发人员门户上的 CoreDataBooks 示例使用类似的模式,几乎肯定还有其他模式。
The problem is that you are confusing the view hierarchy for the navigation stack. Your detail view controller wants to send a message to the controller that pushed it on the stack, which is the second to last object in the navigation controller's viewControllers array.
Try changing your saveField: method to:
Edit: I should note this design is very brittle. A better way is to apply Model-View-Controller and create an object that represents a field and title value. Then your InvoiceAddViewController can pass instances of these objects to your detail controller, and as your detail controller changes them, these changes can be easily reflected in your other controllers.
Edit 2: Here is a hint of how it will work.
I'm assuming you've implemented a dataObjectForIndexPath: method. Presumably tableView:cellForRowAtIndexPath: would also use this method to configure its cells.
Now, you can eliminate both the saveField: and updateField: methods. In your InvoiceAddViewController, viewWillAppear: could be used to refresh your view like this:
There is a whole world of possibilities here. reloadData is a very heavy handed one. Experiment with stuff like KVO to make this more automatic.
In your detail controller, of course, don't forget to update the object, say in view will disappear to do something like
This is just to get you started. There are a lot of possibilities and details to consider. You should really look at a lot of sample code, this pattern gets used a lot. The CoreDataBooks example on the developer portal uses a similar pattern, there are almost certainly others.
您应该使用通知、KVO 或委托。使用适合在此平台上传递数据的设计模式。即使您可以直接使用这些模式之一进行调用,也肯定会证明这是以封装方式实现此目的的更好方法。
You should be using notification, KVO or delegation. Use the design patterns that fit passing data around on this platform. Even if you can make the call directly using one of these patterns will most certainly prove to be a better way of achieving this in an encapsulated manner.
这种做法简直就是在呼唤授权。在详细视图控制器上创建一个协议,声明一个类似
- (void)genericTextFieldDetailViewController:(GenericTextFieldDetailViewController *)controller didUpdateValue:(NSString *)value forField:(NSString *)field
的方法。准备好发送回数据,您只需将此消息发送给您的委托(推送视图控制器)。
[self.delegate genericTextFieldDetailViewController:self didUpdateValue:newValue forField:passedInField]
确保当您创建并推送详细信息视图控制器时,您将自己指定为它的委托并实现该方法来处理传入的值。我想您会发现这种方法更加灵活。
This approach is just screaming for delegation. Create a protocol on your detail view controller, declare a method like
- (void)genericTextFieldDetailViewController:(GenericTextFieldDetailViewController *)controller didUpdateValue:(NSString *)value forField:(NSString *)field
Now when you're ready to send the data back you would just send this message to your delegate (the pushing view controller).
[self.delegate genericTextFieldDetailViewController:self didUpdateValue:newValue forField:passedInField]
Make sure that when you create and push the detail view controller, you assign yourself as it's delegate and implement that method to handle the incoming values. I think you'll find this approach way more flexible.
我遇到了同样的问题,但不是表视图。我想更改最后一个视图控制器的值。我正在使用 Xcode 5:
I had the same problem, but not with a table view. I wanted to change a value from the last view controller. I'm using Xcode 5: