[英]unwrapping optional @State in SwiftUI when applied to a Binding
I'm looking for a clean solution to resolve this SwiftUI challenge.我正在寻找一个干净的解决方案来解决这个 SwiftUI 挑战。
The following code compiles but do not work since @State
property is outside the ContentView
scope.以下代码编译但不工作,因为
@State
属性位于ContentView
scope 之外。
import SwiftUI
struct ContentView: View {
var state: LocalState?
var body: some View {
if let state = state {
Toggle("Toggle", isOn: state.$isOn)
}
}
}
extension ContentView {
struct LocalState {
@State var isOn: Bool
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
VStack {
ContentView(
state: .init(isOn: false)
)
.border(Color.red)
ContentView()
.border(Color.red)
}
}
}
The following code doesn't compile since the following reasons:由于以下原因,以下代码无法编译:
Value of optional type 'ContentView.LocalState?'
可选类型“ContentView.LocalState?”的值must be unwrapped to refer to member 'isOn' of wrapped base type 'ContentView.LocalState'
必须解包以引用已包装基类型“ContentView.LocalState”的成员“isOn”
It seems that $
in $state.isOn
refer to the original state
and not to the unwrapped one.似乎
$
$state.isOn
中的 $ 指的是原始的state
而不是未包装的。
import SwiftUI
struct ContentView: View {
@State var state: LocalState!
var body: some View {
if let state = state {
Toggle("Toggle", isOn: $state.isOn)
}
}
}
extension ContentView {
struct LocalState {
var isOn: Bool
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
VStack {
ContentView(
state: .init(isOn: false)
)
.border(Color.red)
ContentView()
.border(Color.red)
}
}
}
What I do NOT want is:我不想要的是:
isOn
property outside LocalState
.isOn
属性移到LocalState
之外。 How can I achieve those?我怎样才能实现这些?
This works for me:这对我有用:
var body: some View {
if let isOn = Binding($state)?.isOn {
Toggle("Toggle", isOn: isOn)
}
}
Breaking it down: $state
is a Binding<LocalState?>
, and we use the Binding
initialiser (hopefully that's not the failable initialiser that you don't want to use) to convert it to a Binding<LocalState>?
分解:
$state
是一个Binding<LocalState?>
,我们使用Binding
初始化程序(希望这不是您不想使用的可失败初始化程序)将其转换为Binding<LocalState>?
. . Then we can use optional chaining and
if let
to get a Binding<Bool>
out of it.然后我们可以使用可选链和
if let
来得到一个Binding<Bool>
。
Related: How can I unwrap an optional value inside a binding in Swift?相关:如何在 Swift 的绑定中解开可选值?
I believe this can be solved with two techniques.我相信这可以通过两种技术来解决。 1. using the Binding constructor that can create a non-optional binding from an optional.
1. 使用 Binding 构造函数,该构造函数可以从可选项创建非可选绑定。 And 2. use of a constant binding in previews, eg
以及 2. 在预览中使用常量绑定,例如
import SwiftUI
struct Config {
var isOn: Bool
}
struct ContentView: View {
@State var config: Config?
var body: some View {
if let config = Binding($config) { // technique 1
ContentView2(config: config)
}
}
}
struct ContentView2: View {
@Binding var config: Config
var body: some View {
Toggle("Toggle", isOn: $config.isOn)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView2(config: .constant(Config(isOn: false))) // technique 2
}
}
$state
is syntactic sugar for _state.projectedValue
, which gives you a Binding<LocalState?>
. $state
是_state.projectedValue
的语法糖,它为您提供Binding<LocalState?>
。 And from here on things are ugly.从这里开始,事情就变得丑陋了。
You might be able to get away with a wrapped binding:您可能能够摆脱包装绑定:
var wrappedIsOn: Binding<Bool> {
let stateBinding = $state
return Binding {
stateBinding.wrappedValue?.isOn ?? false
} set: {
stateBinding.wrappedValue?.isOn = $0
}
}
And then:接着:
Toggle("Toggle", isOn: wrappedIsOn)
And alternative, inspired by @Sweeper's answer:另一种选择,灵感来自@Sweeper 的回答:
Toggle("Toggle", isOn: Binding($state)?.isOn ?? Binding.constant(false))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.