英文:
Binding an item within a collection
问题
以下是翻译好的部分:
查看以下代码
```swift
struct FruitDetailView: View {
@EnvironmentObject var merchant: Merchant
@State var item: Fruit
var body: some View {
VStack {
Text(item.name)
Button("Press Me") {
item.name = "Watermelon"
merchant.updateFruits(with: item)
}
}
}
}
先前向用户显示了水果列表,当用户选择一个水果时,该选定的水果会传递到此详细视图,现在它作为 @State 受控属性存在。
Merchant 也会传递,因为我们想要保持商户的原始数组同步。同时更新水果和商户很繁琐。是否没有办法绑定水果,以便对其进行任何修改也会修改商户的水果数组中的条目?
这篇文章是基于我之前的帖子这里。
在 Objective-C 的世界中,通过视图层次结构传递指针可以轻松更新以下三个项目。
@implementation Merchant
- (id)init {
self = [super init];
if (self) {
Fruit *apple = [[Fruit alloc] initWithName: @"Apple"];
Fruit *orange = [[Fruit alloc] initWithName: @"Orange"];
Fruit *pear = [[Fruit alloc] initWithName: @"Pear"];
self.fruits = @[apple, orange, pear];
}
return self;
}
@end
@interface FruitDetailViewController : UIViewController
@property (nonatomic, strong) Fruit *fruit;
@end
@implementation FruitDetailViewController
- (IBAction)buttonAction:(UIButton *)sender {
self.fruit.name = @"Cherry";
}
请注意,我已经删除了代码部分中的 HTML 实体字符 (`"`) 并进行了翻译。
<details>
<summary>英文:</summary>
Looking at the following code
struct FruitDetailView: View {
@EnvironmentObject var merchant: Merchant
@State var item: Fruit
var body: some View {
VStack {
Text(item.name)
Button("Press Me") {
item.name = "Watermelon"
merchant.updateFruits(with: item)
}
}
}
}
A list of fruits was previously displayed to the user and when the user selected a fruit, that selected fruit was passed to this detail view. Where it now exists as a @State controlled property.
The merchant is also passed along because we want to keep the original array in sync. Updating both the fruit and the merchant is a pain. Is there no way to bind the fruit so that any modifications to it, also modifies the entry in the array of fruits on the merchant?
This post leads on from my previous post [here][1].
In the world of Objective-C the following three items could be updated by just passing a pointer through the view hierarchy.
@implementation Merchant
-
(id)init {
self = [super init];
if (self) {Fruit *apple = [[Fruit alloc] initWithName: @"Apple"]; Fruit *orange = [[Fruit alloc] initWithName: @"Orange"]; Fruit *pear = [[Fruit alloc] initWithName: @"Pear"]; self.fruits = @[apple, orange, pear];
}
return self;
}
@end
@interface FruitDetailViewController : UIViewController
@property (nonatomic, strong) Fruit * fruit;
@end
@implementation FruitDetailViewController
- (IBAction)buttonAction:(UIButton *)sender {
self.fruit.name = @"Cherry";
}
[1]: https://stackoverflow.com/q/75493273/9400730
</details>
# 答案1
**得分**: 0
正如我在你之前的问题中所说,有很多方法可以完成这些任务。
这里是另一种方法,只将 `Binding Fruit` 传递给 `FruitDetailView`。这真的取决于你想要做什么。
```swift
struct ContentView: View {
@StateObject var merchant = Merchant()
var body: some View {
FruitsView().environmentObject(merchant)
}
}
struct Fruit: Identifiable, Hashable {
let id = UUID()
var name: String
}
final class Merchant: ObservableObject {
@Published var fruits = [Fruit(name: "Banana"), Fruit(name: "Apple")]
}
struct Selector: Identifiable {
let id = UUID()
var index: Int
}
struct FruitsView: View {
@EnvironmentObject var merchant: Merchant
@State var selection: Selector?
var body: some View {
VStack {
ForEach(merchant.fruits.indices, id: \.self) { index in
Button(merchant.fruits[index].name) {
selection = Selector(index: index)
}.buttonStyle(.borderedProminent)
}
}
.sheet(item: $selection) { selector in
FruitDetailView(fruit: $merchant.fruits[selector.index])
}
}
}
struct FruitDetailView: View {
@Binding var fruit: Fruit
var body: some View {
VStack {
Text(fruit.name)
Button("Press Me") {
fruit.name = "Watermelon"
}
}
}
}
编辑1:
这是另一种方法,使用 NavigationLink
转到 FruitDetailView
目标视图,而不是使用 sheet
,只使用绑定:
struct FruitsView: View {
@EnvironmentObject var merchant: Merchant
var body: some View {
NavigationStack {
ForEach($merchant.fruits, id: \.id) { $fruit in
NavigationLink(destination: FruitDetailView(fruit: $fruit)) {
Text(fruit.name)
}
}
}
}
}
英文:
Like I said in your previous question, there are many ways to do things.
Here is yet another way, where only a Binding Fruit
is passed to the FruitDetailView
. It really depends what you want to do.
struct ContentView: View {
@StateObject var merchant = Merchant()
var body: some View {
FruitsView().environmentObject(merchant)
}
}
struct Fruit: Identifiable, Hashable {
let id = UUID()
var name: String
}
final class Merchant: ObservableObject {
@Published var fruits = [Fruit(name: "Banana"), Fruit(name: "Apple")]
}
struct Selector: Identifiable {
let id = UUID()
var index: Int
}
struct FruitsView: View {
@EnvironmentObject var merchant: Merchant
@State var selection: Selector?
var body: some View {
VStack {
ForEach(merchant.fruits.indices, id: \.self) { index in
Button(merchant.fruits[index].name) {
selection = Selector(index: index)
}.buttonStyle(.borderedProminent)
}
}
.sheet(item: $selection) { selector in
FruitDetailView(fruit: $merchant.fruits[selector.index])
}
}
}
struct FruitDetailView: View {
@Binding var fruit: Fruit
var body: some View {
VStack {
Text(fruit.name)
Button("Press Me") {
fruit.name = "Watermelon"
}
}
}
}
EDIT-1:
Here is another way using a NavigationLink
to goto
the FruitDetailView
destination view,
instead of a sheet
, using only bindings:
struct FruitsView: View {
@EnvironmentObject var merchant: Merchant
var body: some View {
NavigationStack {
ForEach($merchant.fruits, id: \.id) { $fruit in
NavigationLink(destination: FruitDetailView(fruit: $fruit)) {
Text(fruit.name)
}
}
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论