绑定到具有不同类型关联值的枚举的关联值
Binding to an associated value of an enum that has different types of associated values
的跟进问题。
如果:
enum Choice {
case one(String)
case two(String)
}
改为:
enum Choice {
case one(String)
case two(Bool)
}
在我看来,我正在打开枚举,然后将一个案例的关联值绑定到文本字段,而另一个绑定到切换按钮?在我需要绑定到 bool 的情况下, 现在对我不起作用。
我能想到的就是复制并粘贴 属性 并将所有 String
更改为 Bool
但是在我的实际代码中我有很多不同的类型所以方法是越来越傻了
任何想法将不胜感激,谢谢!
当前代码:
import SwiftUI
@main
struct MyApp: App {
@StateObject private var model = Model()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(model)
}
}
}
struct ContentView: View {
@EnvironmentObject private var model: Model
var body: some View {
VStack {
ScrollView {
Text(model.jsonString)
}
List($model.data, id: \.name, children: \.children) { $node in
HStack {
switch node.value {
case .none:
Spacer()
case .bool:
Toggle(node.name, isOn: $node.value.bool)
case let .int(value):
Stepper("\(value)", value: $node.value.int)
case let .float(value):
Text(value.description)
case let .string(value):
Text(value)
}
}
}
}
}
}
class Model: ObservableObject {
@Published var data: [Node] = [
Node(name: "My Bool", value: .bool(false)),
Node(name: "My Int", value: .int(25)),
Node(name: "My float", value: .float(3.123)),
Node(name: "My parent node", value: .none, children: [
Node(name: "Default name", value: .string("Untitled")),
Node(name: "Fluid animations", value: .bool(true)),
Node(name: "More children??", value: .none, children: [
Node(name: "Hello", value: .string("There"))
])
])
]
private let encoder = JSONEncoder()
init() {
encoder.outputFormatting = .prettyPrinted
}
var jsonString: String {
String(data: try! encoder.encode(data), encoding: .utf8)!
}
}
struct Node: Codable {
var name: String
var value: Value
var children: [Node]? = nil
}
enum Value: Codable {
case none
case bool(Bool)
case int(Int)
case float(Float)
case string(String)
var bool: Bool {
get {
switch self {
case let .bool(value): return value
default: fatalError("Unexpected value \(self)")
}
}
set {
switch self {
case .bool: self = .bool(newValue)
default: fatalError("Unexpected value \(self)")
}
}
}
var int: Int {
get {
switch self {
case let .int(value): return value
default: fatalError("Unexpected value \(self)")
}
}
set {
switch self {
case .int: self = .int(newValue)
default: fatalError("Unexpected value \(self)")
}
}
}
var float: Float {
get {
switch self {
case let .float(value): return value
default: fatalError("Unexpected value \(self)")
}
}
set {
switch self {
case .float: self = .float(newValue)
default: fatalError("Unexpected value \(self)")
}
}
}
var string: String {
get {
switch self {
case let .string(value): return value
default: fatalError("Unexpected value \(self)")
}
}
set {
switch self {
case .string: self = .string(newValue)
default: fatalError("Unexpected value \(self)")
}
}
}
}
更新:在下面添加了代码,我在更改答案并解决了一个奇怪的 swiftui 动画问题后得到了这些代码。
import SwiftUI
enum Value: Codable, Equatable {
case none
case bool(Bool)
case int(Int)
case float(Float)
case string(String)
}
struct Node: Codable {
var name: String
var value: Value
var children: [Node]? = nil
}
struct ValueView: View {
let name: String
@Binding var value: Value
var body: some View {
HStack {
switch value {
case .none:
Spacer()
case let .bool(bool):
Toggle(name, isOn: Binding(get: { bool }, set: { value = .bool([=13=]) } ))
case let .int(int):
Stepper("\(int)", value: Binding(get: { int }, set: { value = .int([=13=]) }))
case let .float(float):
Text(float.description)
case let .string(string):
Text(string)
}
}
}
}
class Model: ObservableObject {
@Published var data: [Node] = [
Node(name: "My Bool", value: .bool(false)),
Node(name: "My Int", value: .int(25)),
Node(name: "My float", value: .float(3.123)),
Node(name: "My parent node", value: .none, children: [
Node(name: "Default name", value: .string("Untitled")),
Node(name: "Fluid animations", value: .bool(true)),
Node(name: "More children??", value: .none, children: [
Node(name: "Hello", value: .string("There"))
])
])
]
private let encoder = JSONEncoder()
init() {
encoder.outputFormatting = .prettyPrinted
}
var jsonString: String {
String(data: try! encoder.encode(data), encoding: .utf8)!
}
}
struct ContentView: View {
@StateObject private var model = Model()
var body: some View {
VStack {
ScrollView {
Text(model.jsonString)
}
List($model.data, id: \.name, children: \.children) { $node in
ValueView(name: node.name, value: $node.value)
.animation(.default, value: node.value)
}
}
}
}
@main
struct MyApp: App {
@StateObject private var model = Model()
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
事实上,拥有几个非常相似的属性是一种代码味道,而且添加这些属性只是因为 UI 层需要它们。
但是,由于您已经切换了值类型,您可以将绑定逻辑推送到该 (UI) 层。这是一个可能的实现:
struct ValueView: View {
let name: String
@Binding var value: Value
var body: some View {
HStack {
switch value {
case .none:
Spacer()
case let .bool(bool):
Toggle(name, isOn: Binding(get: { bool }, set: { value = .bool([=10=]) } ))
case let .int(int):
Stepper("\(int)", value: Binding(get: { int }, set: { value = .int([=10=]) }))
case let .float(float):
Text(float.description)
case let .string(string):
Text(string)
}
}
}
}
我还冒昧地将代码提取到专用视图并将该视图与 Node
类型解耦,这在 Swift 中更加惯用 UI 并使您的代码更易读和更容易维护。
考虑到以上几点,ContentView
就变成了:
用法:
struct ContentView: View {
@StateObject private var model = Model()
var body: some View {
VStack {
ScrollView {
Text(model.jsonString)
}
List($model.data, id: \.name, children: \.children) { $node in
ValueView(name: node.name, value: $node.value)
}
}
}
}
,您可以安全地从 Value
枚举中删除“重复的”属性。
如果:
enum Choice {
case one(String)
case two(String)
}
改为:
enum Choice {
case one(String)
case two(Bool)
}
在我看来,我正在打开枚举,然后将一个案例的关联值绑定到文本字段,而另一个绑定到切换按钮?在我需要绑定到 bool 的情况下,
我能想到的就是复制并粘贴 属性 并将所有 String
更改为 Bool
但是在我的实际代码中我有很多不同的类型所以方法是越来越傻了
任何想法将不胜感激,谢谢!
当前代码:
import SwiftUI
@main
struct MyApp: App {
@StateObject private var model = Model()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(model)
}
}
}
struct ContentView: View {
@EnvironmentObject private var model: Model
var body: some View {
VStack {
ScrollView {
Text(model.jsonString)
}
List($model.data, id: \.name, children: \.children) { $node in
HStack {
switch node.value {
case .none:
Spacer()
case .bool:
Toggle(node.name, isOn: $node.value.bool)
case let .int(value):
Stepper("\(value)", value: $node.value.int)
case let .float(value):
Text(value.description)
case let .string(value):
Text(value)
}
}
}
}
}
}
class Model: ObservableObject {
@Published var data: [Node] = [
Node(name: "My Bool", value: .bool(false)),
Node(name: "My Int", value: .int(25)),
Node(name: "My float", value: .float(3.123)),
Node(name: "My parent node", value: .none, children: [
Node(name: "Default name", value: .string("Untitled")),
Node(name: "Fluid animations", value: .bool(true)),
Node(name: "More children??", value: .none, children: [
Node(name: "Hello", value: .string("There"))
])
])
]
private let encoder = JSONEncoder()
init() {
encoder.outputFormatting = .prettyPrinted
}
var jsonString: String {
String(data: try! encoder.encode(data), encoding: .utf8)!
}
}
struct Node: Codable {
var name: String
var value: Value
var children: [Node]? = nil
}
enum Value: Codable {
case none
case bool(Bool)
case int(Int)
case float(Float)
case string(String)
var bool: Bool {
get {
switch self {
case let .bool(value): return value
default: fatalError("Unexpected value \(self)")
}
}
set {
switch self {
case .bool: self = .bool(newValue)
default: fatalError("Unexpected value \(self)")
}
}
}
var int: Int {
get {
switch self {
case let .int(value): return value
default: fatalError("Unexpected value \(self)")
}
}
set {
switch self {
case .int: self = .int(newValue)
default: fatalError("Unexpected value \(self)")
}
}
}
var float: Float {
get {
switch self {
case let .float(value): return value
default: fatalError("Unexpected value \(self)")
}
}
set {
switch self {
case .float: self = .float(newValue)
default: fatalError("Unexpected value \(self)")
}
}
}
var string: String {
get {
switch self {
case let .string(value): return value
default: fatalError("Unexpected value \(self)")
}
}
set {
switch self {
case .string: self = .string(newValue)
default: fatalError("Unexpected value \(self)")
}
}
}
}
更新:在下面添加了代码,我在更改答案并解决了一个奇怪的 swiftui 动画问题后得到了这些代码。
import SwiftUI
enum Value: Codable, Equatable {
case none
case bool(Bool)
case int(Int)
case float(Float)
case string(String)
}
struct Node: Codable {
var name: String
var value: Value
var children: [Node]? = nil
}
struct ValueView: View {
let name: String
@Binding var value: Value
var body: some View {
HStack {
switch value {
case .none:
Spacer()
case let .bool(bool):
Toggle(name, isOn: Binding(get: { bool }, set: { value = .bool([=13=]) } ))
case let .int(int):
Stepper("\(int)", value: Binding(get: { int }, set: { value = .int([=13=]) }))
case let .float(float):
Text(float.description)
case let .string(string):
Text(string)
}
}
}
}
class Model: ObservableObject {
@Published var data: [Node] = [
Node(name: "My Bool", value: .bool(false)),
Node(name: "My Int", value: .int(25)),
Node(name: "My float", value: .float(3.123)),
Node(name: "My parent node", value: .none, children: [
Node(name: "Default name", value: .string("Untitled")),
Node(name: "Fluid animations", value: .bool(true)),
Node(name: "More children??", value: .none, children: [
Node(name: "Hello", value: .string("There"))
])
])
]
private let encoder = JSONEncoder()
init() {
encoder.outputFormatting = .prettyPrinted
}
var jsonString: String {
String(data: try! encoder.encode(data), encoding: .utf8)!
}
}
struct ContentView: View {
@StateObject private var model = Model()
var body: some View {
VStack {
ScrollView {
Text(model.jsonString)
}
List($model.data, id: \.name, children: \.children) { $node in
ValueView(name: node.name, value: $node.value)
.animation(.default, value: node.value)
}
}
}
}
@main
struct MyApp: App {
@StateObject private var model = Model()
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
事实上,拥有几个非常相似的属性是一种代码味道,而且添加这些属性只是因为 UI 层需要它们。
但是,由于您已经切换了值类型,您可以将绑定逻辑推送到该 (UI) 层。这是一个可能的实现:
struct ValueView: View {
let name: String
@Binding var value: Value
var body: some View {
HStack {
switch value {
case .none:
Spacer()
case let .bool(bool):
Toggle(name, isOn: Binding(get: { bool }, set: { value = .bool([=10=]) } ))
case let .int(int):
Stepper("\(int)", value: Binding(get: { int }, set: { value = .int([=10=]) }))
case let .float(float):
Text(float.description)
case let .string(string):
Text(string)
}
}
}
}
我还冒昧地将代码提取到专用视图并将该视图与 Node
类型解耦,这在 Swift 中更加惯用 UI 并使您的代码更易读和更容易维护。
考虑到以上几点,ContentView
就变成了:
用法:
struct ContentView: View {
@StateObject private var model = Model()
var body: some View {
VStack {
ScrollView {
Text(model.jsonString)
}
List($model.data, id: \.name, children: \.children) { $node in
ValueView(name: node.name, value: $node.value)
}
}
}
}
,您可以安全地从 Value
枚举中删除“重复的”属性。