如何在 SwiftUI 中使用枚举绑定数据?
Posted
技术标签:
【中文标题】如何在 SwiftUI 中使用枚举绑定数据?【英文标题】:How can I use enum to Binding data in SwiftUI? 【发布时间】:2021-03-23 02:26:50 【问题描述】:我正在尝试绑定到枚举实例的 rawValue
enum Brand: Int, CaseIterable, Codable
case toyota = 0
case mazda
case suzuki
struct Car
var brand: Brand
struct CarView: View
@Binding car: Car
var body: some View
SomeView(selectionInt: $car.brand)
但我收到此错误:
无法将“绑定”类型的值转换为预期参数 输入“绑定”。
如果尝试使用 $car.brand.rawValue 代替(在 SomeView 的参数中),但我收到此错误:
无法分配给属性:“rawValue”是不可变的。
如何将我的视图绑定到模型实例的 rawValue?
【问题讨论】:
让您的 Car 严格成为 ObservableObject 类并让品牌发布。您将为自己省去解决方法的所有麻烦。 Binding是双向连接,它需要一个可以存储并且可以被Observed的父级。 @lorem-ipsum 要使 Car 结构成为 ObservableObject,我必须将其转换为类。在我的实际代码中,我根本不处理汽车,在这个例子中我用汽车表示的模型实际上是一个符合 Codable 的结构,它是另一个或两个 Codable 结构的子结构。以汽车类比为例,当我访问汽车品牌时,它实际上是 Vehicle 模型的成员... vehicle.groundVehicle.car.brand。 “Vehicle”是从 XML 文件中解码出来的,如果我偏离当前设置太远,恐怕我可能会搞砸我目前设置的所有内容。 没问题我想我会建议它。您始终可以使用class
手动符合Codable
。当您可能在struct
中处理许多var
时,答案中的解决方法似乎很多工作,下面的解决方法只是每次都创建一个新的Car
。您不妨将整个Car
与@State
作为父级和@Binding
作为子级传递,并为自己避免错误的可能性(由多个变量引起)。其余的现有变量会发生什么?像一个新的车牌号码?孩子手头没有原始资料
是的。我觉得我需要一种新的方法。与其尝试将 selectionInt 绑定到 Car.brand,不如将其保留为简单的 @State var selectionInt: Int 然后通过函数调用设置汽车实例的品牌?通过与 SomeView() 寻呼机绑定的某种 onChange 调用?
如果你使用@State
(在View
中)或@Published
在ObservableObject
class
中,你只能有效地绑定到单个变量。
【参考方案1】:
使用brandProxy
来维护其他Car
变量的完整性
import SwiftUI
enum Brand: Int, CaseIterable, Codable
case toyota = 0
case mazda
case suzuki
case unknown
func description() -> String
var result = "unknown"
switch self
case .toyota:
result = "toyota"
case .mazda:
result = "mazda"
case .suzuki:
result = "suzuki"
case .unknown:
result = "unknown"
return result
struct CarView: View
//This makes it mutable
@State var car: Car
var brandProxy: Binding<Int>
Binding<Int>(
get:
car.brand.rawValue
,
set:
car.brand = Brand(rawValue: $0) ?? Brand.unknown
)
var body: some View
VStack
Text(car.brand.description())
SomeView(selectedIndex: brandProxy)
【讨论】:
干杯。第一个选项看起来与我目前拥有的代码非常相似,但我没有使用 Picker。我正在使用第三方寻呼机,它只接受一个 Int 作为它的“选择”,因此我尝试检索品牌的 rawValue。 我不知道为什么我没有早点想到这个。使用代理。我更新了代码。这不会替换您的整个Car
完美。这是我希望的简单解决方案。干杯!
@Ribena:我的代码基本上有什么问题或者这个答案有什么新的?!
@swiftPunk 您的回答本身没有问题。这很有帮助!这对我来说更容易理解,除了声明一个新变量之外,没有对任何代码进行任何更改。因为我的代码实际上根本不处理汽车或汽车模型,而是更复杂,所以 lorem ipsum 的解决方案更适合我保持简单。将 Binding.init(get: () 等行(来自您的解决方案)输入到 SomeView 的参数中对我来说有点吓人。毫无疑问,其他提出这个问题的人会更喜欢您的答案,而是赞成!【参考方案2】:
对于这种绑定,你需要一个自定义的 Binding 来帮助 SwiftUI 绑定数据!
信息:您不能更改枚举的 rawValue,因为它们是不可变的! 并且尝试以绑定方式更改它们对您没有帮助,它们是不可变的! 你可以让他们不设置他们!
因此,有了这些信息,您就可以知道为什么 SwiftUI 不希望将它绑定在第一个位置!因为如果 SwiftUI 使该 Binding 成为可能,那将是对枚举 rawValues 的重大违反!然后我们将尝试以这种方式使用绑定能力,即我们改变汽车的 Int 而不是 Enum。以代码为例。
import SwiftUI
struct ContentView: View
@State private var selectedCar: Car = Car(brand: Brand.suzuki)
var body: some View
VStack
Picker("", selection: $selectedCar.brand)
ForEach(Brand.allCases, id: \.self) car in
Text(car.description)
.pickerStyle(SegmentedPickerStyle())
.padding()
CarView(selectionInt: Binding.init(get: () -> Int in return selectedCar.brand.rawValue ,
set: newValue in
if let unwrappedBrand = Brand(rawValue: newValue) selectedCar = Car(brand: unwrappedBrand) ) )
struct CarView: View
@Binding var selectionInt: Int
var body: some View
Text("selected Int: " + selectionInt.description)
.padding()
let selectedCar = (Brand(rawValue: selectionInt)?.description ?? "unknown")
Text( "selected car: " + selectedCar)
.padding()
VStack(spacing: 20.0)
Button("select toyota") selectionInt = 0
Button("select mazda") selectionInt = 1
Button("select suzuki") selectionInt = 2
.font(Font.body.weight(Font.Weight.bold))
enum Brand: Int, CaseIterable, Codable, CustomStringConvertible
case toyota = 0
case mazda = 1
case suzuki = 2
var description: String
switch self
case .toyota: return "toyota"
case .mazda: return "mazda"
case .suzuki: return "suzuki"
struct Car
var brand: Brand
【讨论】:
用我的代码尝试一下,它的工作方式几乎与我预期的一样。 SomeView 根据汽车品牌正确显示相关图像。但 SomeView 实际上是某种选择器/寻呼机。当我转到另一个品牌时,它似乎正在获取但没有设置品牌。所以我将如何拥有它,使其成为双向绑定。 @Ribena:所以新问题与您提出的问题无关,因为我们知道问题是关于 在 SwiftUI 中绑定到枚举的 rawValue,我想我用工作示例对其进行了解释。您要问的新问题可能与您的代码调试有关,我认为最好在单独的问题中提出。使用代码和视图使该问题发生。谢谢。 很公平。我应该根据我想要实现的目标来构建最初的问题。感谢您提供更新的答案。 @Ribena:我刚刚以这种方式更新了自定义绑定,您也可以更改汽车并稍微清理代码。 干杯。您的答案有效,但最终我将接受的答案更改为 lorem ipsum 的,因为它更好地回答了我的问题的精神。不过,下次我会尽量说得更清楚,因为我同意我在问题标题和正文中并没有完全问同样的问题。以上是关于如何在 SwiftUI 中使用枚举绑定数据?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 SwiftUI 中使用可观察对象并与 NavigationLink 绑定?