如何使用@StateObject 修改布尔值?
Posted
技术标签:
【中文标题】如何使用@StateObject 修改布尔值?【英文标题】:How to modify Boolean value using @StateObject? 【发布时间】:2020-07-02 15:31:06 【问题描述】:我试图使用@StateObject
重新创建地标应用程序的功能,Apple 去年以tutorial 发布了该应用程序。我有以下代码。这是一个基本的应用程序,您可以看到 JSON 对象的 isFavorite
值是真还是假。在 Apple 的示例中,可以通过点击按钮更改 isFavorite
值,然后保持更改。
不过,我并不完全理解 Apple 的示例。如何在不重写 JSON 本身的情况下保留值?这不是一个坏例子吗?
import SwiftUI
import Combine
import Foundation
struct Item: Codable, Identifiable, Equatable
var id: Int
var name: String
var isFavorite: Bool
final class UserData: ObservableObject
@Published var items = Bundle.main.decode([Item].self, from: "data.json")
@Published var showFavorites = false
struct ContentView: View
@State var itemID = Item.ID()
@StateObject var userData = UserData()
var body: some View
NavigationView
VStack
Toggle(isOn: $userData.showFavorites)
Text("Show Favorites Only")
List
ForEach(userData.items) item in
if !userData.showFavorites || item.isFavorite
NavigationLink(destination: ContentDetail(itemID: item.id - 1))
ContentRow(item: item)
struct ContentRow: View
var item: Item
var body: some View
HStack
Text(item.name)
Spacer()
if item.isFavorite
Image(systemName: "star.fill")
.imageScale(.medium)
.foregroundColor(.yellow)
struct ContentDetail: View
@State var itemID = Item.ID()
@StateObject var userData = UserData()
var body: some View
VStack
Button
userData.items[itemID].isFavorite.toggle()
label:
if userData.items[itemID].isFavorite
Image(systemName: "star.fill")
.foregroundColor(Color.yellow)
else
Image(systemName: "star")
.foregroundColor(Color.gray)
Text(userData.items[itemID].name)
extension Bundle
func decode<T: Decodable>(_ type: T.Type, from file: String, dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .deferredToDate, keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys) -> T
guard let url = self.url(forResource: file, withExtension: nil) else
fatalError("Failed to locate \(file) in bundle.")
guard let data = try? Data(contentsOf: url) else
fatalError("Failed to load \(file) from bundle.")
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = dateDecodingStrategy
decoder.keyDecodingStrategy = keyDecodingStrategy
do
return try decoder.decode(T.self, from: data)
catch DecodingError.keyNotFound(let key, let context)
fatalError("Failed to decode \(file) from bundle due to missing key '\(key.stringValue)' not found – \(context.debugDescription)")
catch DecodingError.typeMismatch(_, let context)
fatalError("Failed to decode \(file) from bundle due to type mismatch – \(context.debugDescription)")
catch DecodingError.valueNotFound(let type, let context)
fatalError("Failed to decode \(file) from bundle due to missing \(type) value – \(context.debugDescription)")
catch DecodingError.dataCorrupted(_)
fatalError("Failed to decode \(file) from bundle because it appears to be invalid JSON")
catch
fatalError("Failed to decode \(file) from bundle: \(error.localizedDescription)")
【问题讨论】:
【参考方案1】:您似乎正在创建 两个 UserData
的实例。
struct ContentView: View
...
@StateObject var userData = UserData()
...
struct ContentDetail: View
...
@StateObject var userData = UserData()
...
使用@StateObject
并不意味着它的实例对于您的所有视图都是相同的。当 SwiftUI 决定使您的视图无效/重绘时,@StateObject
将持续存在,但这并不意味着同一对象将在全局范围内可用。
在ContentView
的示例中,您正在访问(并进行更改)UserData
的一个实例,而在ContentDetail
中,您正在使用另一个 实例。这也意味着您不必要地对 JSON 进行两次解码。
如果您希望UserData
在您当前的环境中全球可用,您可以尝试@EnvironmentObject
。
您也可以将UserData
传递给您的DetailView
:
NavigationLink(destination: ContentDetail(itemID: item.id - 1, userData: userData))
...
struct ContentDetail: View
...
@ObservedObject var userData: UserData // <- declare only
...
请注意,您的详细视图中不需要@StateObject
。您可以安全地使用@ObservedObject
,因为它已传递到您的详细视图(而不是在其中创建)。详情请见this question。
【讨论】:
以上是关于如何使用@StateObject 修改布尔值?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 StateObject 并与 JSON loader 结合?