如何在 SwiftUI 视图中执行非视图代码
How to execute non-view code inside a SwiftUI view
我一遍又一遍地为此苦苦挣扎,所以我想我错过了什么。我需要做数学运算、进行设置、分配一个值或响应某些用户操作的许多简单操作中的任何一个,例如此处显示的示例,而 SwiftUI 想要一个我不需要视图的视图。必须有办法绕过 ViewBuilder 的规则。我通过创建一个不必要的视图并在视图的 init() 中执行我需要的代码来解决这个问题,但这看起来非常尴尬。
import SwiftUI
struct ContentView: View
{
@State var showStuff = false
var body: some View
{
VStack
{
Toggle(isOn: $showStuff)
{
Text("Label")
}
if showStuff
{
UserDefaults.standard.set(true, forKey: "Something")
}
}
}
}
你不能做你想做的事,因为实际上 body
中的每个视图块都是一个 ViewBuidler.buildBlock
函数参数。 IE。您在函数 参数 space 中。我希望你不会期望像
这样的表达
foo(Toggle(), if showStuff { ... } )
会起作用(假设 foo
是 func foo(args: View...)
。但这是您在 body
中尝试做的事情。
因此 SwiftUI 中的表达式必须在 ViewBuilder 块之外(ViewBuilder 本身支持视图的一些例外情况)。
这是针对您的情况的解决方案:
SwiftUI 2.0
struct ContentView: View {
@AppStorage("Something") var showStuff = false
var body: some View {
VStack {
Toggle(isOn: $showStuff) {
Text("Label")
}
}
}
}
SwiftUI 1.0
在已经解决的地方找到SwiftUI toggle switches
注:View.body
(不包括一些动作修饰符)等同于UIView.draw(_ rect:)
...你不把UserDefaults存入draw(_rect:),你呢?
在 SwiftUI 2.0 中,有一个新的 ViewModifier onChange(of:perform:)
,它允许您对值的变化做出反应。
但是你可以用一个巧妙的技巧来创建类似的东西(我忘记了我在哪里看到的,所以不幸的是我不能留下正确的归属),通过用 onChange
扩展 Binding
方法:
extension Binding {
func onChange(perform action: @escaping (Value, Value) -> Void) -> Self {
.init(
get: { self.wrappedValue },
set: { newValue in
let oldValue = self.wrappedValue
DispatchQueue.main.async { action(newValue, oldValue) }
self.wrappedValue = newValue
})
}
}
你可以这样使用它:
Toggle(isOn: $showStuff.onChange(perform: { (new, old) in
if new {
UserDefaults.standard.set(true, forKey: "Something")
}
}))
方式 1(最佳):
struct ExecuteCode : View {
init( _ codeToExec: () -> () ) {
codeToExec()
}
var body: some View {
return EmptyView()
}
}
用法:
HStack {
ExecuteCode {
print("SomeView1 was re-drawn!")
}
SomeView1()
}
方式二:
(我的第一种方法更好 - 你在这里只能编写简单的代码)
带有 let _ =
的代码在 View 内部工作!
HStack {
let _ = print("SomeView1 was re-drawn!")
SomeView1()
}
方式三:
(我的第一种方法更好-代码结构太难了)
HStack {
// here is the magic
{ () -> SomeView1() in
// here is code to execute
print("SomeView1 was re-drawn!")
// here is the magic
return SomeView1()
}
}
视图实际上是所谓的Function Builders,视图主体的内容用作buildBlock
函数的参数,如@Asperi所述.
如果必须 运行 在此上下文中编写代码,则另一种解决方案是使用 returns 所需视图的闭包:
VStack {
// ... some views ...
{ () -> Text in
// ... any code ...
return Text("some view") }()
// ... some views ...
}
我一遍又一遍地为此苦苦挣扎,所以我想我错过了什么。我需要做数学运算、进行设置、分配一个值或响应某些用户操作的许多简单操作中的任何一个,例如此处显示的示例,而 SwiftUI 想要一个我不需要视图的视图。必须有办法绕过 ViewBuilder 的规则。我通过创建一个不必要的视图并在视图的 init() 中执行我需要的代码来解决这个问题,但这看起来非常尴尬。
import SwiftUI
struct ContentView: View
{
@State var showStuff = false
var body: some View
{
VStack
{
Toggle(isOn: $showStuff)
{
Text("Label")
}
if showStuff
{
UserDefaults.standard.set(true, forKey: "Something")
}
}
}
}
你不能做你想做的事,因为实际上 body
中的每个视图块都是一个 ViewBuidler.buildBlock
函数参数。 IE。您在函数 参数 space 中。我希望你不会期望像
foo(Toggle(), if showStuff { ... } )
会起作用(假设 foo
是 func foo(args: View...)
。但这是您在 body
中尝试做的事情。
因此 SwiftUI 中的表达式必须在 ViewBuilder 块之外(ViewBuilder 本身支持视图的一些例外情况)。
这是针对您的情况的解决方案:
SwiftUI 2.0
struct ContentView: View {
@AppStorage("Something") var showStuff = false
var body: some View {
VStack {
Toggle(isOn: $showStuff) {
Text("Label")
}
}
}
}
SwiftUI 1.0
在已经解决的地方找到SwiftUI toggle switches
注:View.body
(不包括一些动作修饰符)等同于UIView.draw(_ rect:)
...你不把UserDefaults存入draw(_rect:),你呢?
在 SwiftUI 2.0 中,有一个新的 ViewModifier onChange(of:perform:)
,它允许您对值的变化做出反应。
但是你可以用一个巧妙的技巧来创建类似的东西(我忘记了我在哪里看到的,所以不幸的是我不能留下正确的归属),通过用 onChange
扩展 Binding
方法:
extension Binding {
func onChange(perform action: @escaping (Value, Value) -> Void) -> Self {
.init(
get: { self.wrappedValue },
set: { newValue in
let oldValue = self.wrappedValue
DispatchQueue.main.async { action(newValue, oldValue) }
self.wrappedValue = newValue
})
}
}
你可以这样使用它:
Toggle(isOn: $showStuff.onChange(perform: { (new, old) in
if new {
UserDefaults.standard.set(true, forKey: "Something")
}
}))
方式 1(最佳):
struct ExecuteCode : View {
init( _ codeToExec: () -> () ) {
codeToExec()
}
var body: some View {
return EmptyView()
}
}
用法:
HStack {
ExecuteCode {
print("SomeView1 was re-drawn!")
}
SomeView1()
}
方式二:
(我的第一种方法更好 - 你在这里只能编写简单的代码)
带有 let _ =
的代码在 View 内部工作!
HStack {
let _ = print("SomeView1 was re-drawn!")
SomeView1()
}
方式三:
(我的第一种方法更好-代码结构太难了)
HStack {
// here is the magic
{ () -> SomeView1() in
// here is code to execute
print("SomeView1 was re-drawn!")
// here is the magic
return SomeView1()
}
}
视图实际上是所谓的Function Builders,视图主体的内容用作buildBlock
函数的参数,如@Asperi所述.
如果必须 运行 在此上下文中编写代码,则另一种解决方案是使用 returns 所需视图的闭包:
VStack {
// ... some views ...
{ () -> Text in
// ... any code ...
return Text("some view") }()
// ... some views ...
}