【问题标题】:Add or remove an item from a collection (set, array, or dictionairy) with a SwiftUI Toggle使用 SwiftUI 切换从集合(集合、数组或字典)中添加或删除项目
【发布时间】:2021-01-10 19:08:12
【问题描述】:

我正在构建一个类似于 Apple 的 Fruta SwiftUI 示例应用中的“收藏夹”功能的功能。该应用程序的用户可以查看冰沙列表并将任何冰沙标记为“最喜欢的”。收藏夹列表存储在单独的屏幕上并可供查看。

下面的示例视图做了两个重要的事情:

  1. 它会检查冰沙是否已经在isFavorite 的收藏夹列表中,以便可以通过按钮图像反映这一点。
  2. 它使用toggleFavorite 在收藏夹列表中添加/删除冰沙。

型号:

struct Smoothie: Identifiable, Codable {
    var id: String
    var title: String
    var description: String
    var hasFreeRecipe = false
}

class FrutaModel: ObservableObject {
    @Published private(set) var favoriteSmoothieIDs = Set<Smoothie.ID>()

    func toggleFavorite(smoothie: Smoothie) {
        if favoriteSmoothieIDs.contains(smoothie.id) {
            favoriteSmoothieIDs.remove(smoothie.id)
        } else {
            favoriteSmoothieIDs.insert(smoothie.id)
        }
    }
    
    func isFavorite(smoothie: Smoothie) -> Bool {
        favoriteSmoothieIDs.contains(smoothie.id)
    }
}

用于(取消)收藏冰沙的按钮视图:

struct SmoothieFavoriteButton: View {
    @EnvironmentObject private var model: FrutaModel
    
    var smoothie: Smoothie?
    
    var isFavorite: Bool {
        guard let smoothie = smoothie else { return false }
        return model.favoriteSmoothieIDs.contains(smoothie.id)
    }
    
    var body: some View {
        Button(action: toggleFavorite) {
            Label("Favorite", systemImage: isFavorite ? "heart.fill" : "heart")
        }
    }
    
    func toggleFavorite() {
        guard let smoothie = smoothie else { return }
        model.toggleFavorite(smoothie: smoothie)
    }
}

我的问题:如何在使用 Toggle 而不是 Button 时获得类似的结果? Toggle 需要绑定作为属性,但由于特定冰沙的 isFavorite 值来自包含完整收藏夹列表的 Set (favoriteSmoothieIDs),因此我不能简单地为 Toggle 提供绑定。

【问题讨论】:

  • 如果此代码包含您所指的列表,我相信有人已经为您回答了这个问题。 (model.favoriteSmoothieIDs 为空)。请提供足够的代码来证明您的问题。这包括为 Smoothie 添加一个定义和一个显示列表的视图。
  • 我刚刚添加了定义Smoothie 的结构,如果这就是你的意思。我认为 model.favoriteSmoothieIDs 中存储的实际值并不重要。 favoriteSmoothieID 列表可以为空,这也是用户尚未将任何冰沙标记为收藏的默认状态。
  • 是的。我明白。我从原始教程中获取了冰沙的定义,并在发表我的第一条评论之前运行了你的代码。按原样运行代码实际上并没有做任何事情。另外:您添加了未定义的 MeasuredIngredient,因此再次有人很难帮助您,因为您没有提供有意义的工作示例。

标签: swift swiftui toggle


【解决方案1】:

这是必需的代码:

@State var isToggleFavorite = false

Toggle("", isOn: $isToggleFavorite)
            .onChange(of: isToggleFavorite) { value in
                guard let smoothie = smoothie else { return }
                model.toggleFavorite(smoothie: smoothie)
            }

在这里,我添加了响应您的请求的代码,并保留了原始代码。

struct SmoothieFavoriteButton: View {
    @EnvironmentObject private var model: FrutaModel
    
    var smoothie: Smoothie?

    @State var isToggleFavorite = false // <== Newly Added

    var isFavorite: Bool {
        guard let smoothie = smoothie else { return false }
        return model.favoriteSmoothieIDs.contains(smoothie.id)
    }
    var body: some View {
        Toggle("", isOn: $isToggleFavorite)
            .onChange(of: isToggleFavorite) { value in
                guard let smoothie = smoothie else { return }
                model.toggleFavorite(smoothie: smoothie)
            } // <== Newly Added

        Button(action: toggleFavorite) {
            Label("Favorite", systemImage: isFavorite ? "heart.fill" : "heart")
        }
        .foregroundColor(isFavorite ? .accentColor : nil)
        .accessibility(label: Text("\(isFavorite ? "Remove from" : "Add to") Favorites"))
        } // <== Newly Added
    } 
    
    func toggleFavorite() {
        guard let smoothie = smoothie else { return }
        model.toggleFavorite(smoothie: smoothie)
    }
}

[]

您可以将按钮替换为 Text 对象:

 Text(isToggleFavorite ? Image(systemName: "heart.fill"): Image(systemName: "heart"))

但是您需要保留 isFavorite 值。

如果可以接受将心脏放在开关的左侧,您可以完全移除按钮并将心脏图像放在开关的标签内:

Toggle(isOn: $isToggleFavorite) {
                isToggleFavorite ?
                    Image(systemName: "heart.fill") : Image(systemName: "heart")
            }
            .onChange(of: isToggleFavorite) { value in
                guard let smoothie = smoothie else { return }
                model.toggleFavorite(smoothie: smoothie)
            }

您有多种选择。

【讨论】:

  • 感谢您的广泛帮助!这解决了问题的第二部分,但不是第一部分:当页面首次加载以显示切换的正确状态时,检查冰沙是否已经在收藏夹列表中。您无法通过在 onAppear 修饰符中设置正确的 isToggleFavorite 状态来解决此问题,因为这会立即触发切换的 onChange 状态。我现在在 Toggle 上使用 onTapGesture 而不是 onChange,但这不适用于带有标签的 Toggle。
  • 我已经在这上面花了几个小时。如果我有机会回到它,我会的。我需要专注于其他事情。如果到目前为止我的回答有帮助,请考虑投票,让其他人有机会找到它。其他人可能会给你我没有提供的答案。那将是理想的。 ?
猜你喜欢
  • 2017-05-05
  • 1970-01-01
  • 2018-06-26
  • 1970-01-01
  • 2012-09-05
  • 2021-08-27
  • 2011-03-03
相关资源
最近更新 更多