如何在geometryReader中设置@State变量
这是我的代码
@State var isTest:Int = 0
var body: some View {
VStack{
ForEach(self.test, id: \.id) { Test in
VStack{
GeometryReader { geometry in
self.isTest = 1
我尝试使用某个功能,但不起作用
@State var isTest:Int = 0
func testValue(){
self.isTest = 1
}
var body: some View {
VStack{
ForEach(self.test, id: \.id) { Test in
VStack{
GeometryReader { geometry in
testValue()
有什么主意吗?谢谢!
答案 0 :(得分:1)
昨天我也有类似的问题。但是我试图从GeometryReader内部传递一个值。
我尝试了几种方法,但是没有用。
当我使用@State var
声明变量时,编译器再次在紫线中抱怨说,在更新过程中修改视图将使其变为未定义。
当我尝试仅使用var
声明变量时,编译器只是告诉我它是不可变的。
然后,我尝试将其存储到我的@EnvironmentObject
中。我只是死循环了。
所以,我最后的希望是使用通知方式及其一些工作方式。但是我不知道这是否是标准的实现方式。
@State private var viewPositionY:CGFloat = 0
首先,通过通知发布值frame.origin.y
。
GeometryReader{ geometry -> Text in
let frame = geometry.frame(in: CoordinateSpace.global)
NotificationCenter.default.post(name: Notification.Name("channelBlahblahblah"), object:nil, userInfo:["dict":frame.origin.y])
return Text("My View Title")
}
然后声明发布者以接收通知。
private let myPublisher = NotificationCenter.default.publisher(for: Notification.Name("channelBlahblahblah"))
最后,使用.onReceive修饰符接收通知。
.onReceive(myPublisher) { (output) in
viewPositionY = output.userInfo!["dict"] as! CGFloat
//you can do you business here
}
答案 1 :(得分:0)
虽然将代码放入函数是一个很好的接触,但可能会出现另一个问题,那就是在更新阶段改变 @State 变量:
[SwiftUI] Modifying state during view update, this will cause undefined behavior
使用 NotificationCenter 移动 @State 变量更新 视图更新阶段之后会有所帮助,但可以使用更简单的解决方案,例如使用 DispatchQueue
在渲染阶段之后立即执行变量更新。< /p>
@State var windowSize = CGSize()
func useProxy(_ geometry: GeometryProxy) -> some View {
DispatchQueue.main.async {
self.windowSize = geometry.size
}
return EmptyView()
}
var body: some View {
return GeometryReader { geometry in
self.useProxy(geometry)
Text("Hello SwiftUI")
}
}
答案 2 :(得分:0)
因此完全有可能在 @State
中更新 GeometryReader
。解决方法很简单。但是,有一个警告:
你可能会陷入无限循环 (没什么太麻烦的,我会在这里提出解决方案)
您只需要一个 DispatchQueue.main.async
并在 GeometryReader
内显式声明视图的类型。如果您执行下面的视图(不要忘记停止它),您会看到它永远不会停止更新 Text
的值。
不是最终解决方案:
struct GenericList: View {
@State var timesCalled = 0
var body: some View {
GeometryReader { geometry -> Text in
DispatchQueue.main.async {
timesCalled += 1 // infinite loop
}
return Text("\(timesCalled)")
}
}
}
发生这种情况是因为 View 将“绘制”GeometryReader
,这将更新 View 的 @State
。因此,新的 @State
使视图无效,导致视图被重绘。因此回到第一步(绘制 GeometryReader
并更新状态)。
要解决这个问题,您需要在 GeometryReader
的绘制中添加一些约束。不要在 GeometryReader
内返回您的视图,而是绘制它然后添加 GeometryReader
作为透明叠加层或背景。这将产生相同的效果,但您可以在演示文稿中设置约束条件。
就我个人而言,我更愿意使用叠加层,因为您可以添加任意数量的叠加层。请注意,叠加层不允许在其中包含 if else
,但可以调用函数。这就是下面有 func geometryReader()
的原因。另一件事是,为了返回不同类型的视图,您需要在它之前添加 @ViewBuilder
。
在这段代码中,GeometryReader 只被调用一次,并且更新了 @State var timesCalled。
最终解决方案:
struct GenericList: View {
@State var timesCalled = 0
@ViewBuilder
func geometryReader() -> some View {
if timesCalled < 1 {
GeometryReader { geometry -> Color in
DispatchQueue.main.async {
timesCalled += 1
}
return Color.clear
}
} else {
EmptyView()
}
}
var body: some View {
Text("\(timesCalled)")
.overlay(geometryReader())
}
}
注意:您不需要放置相同的约束,例如,in my case,我想使用拖动手势移动视图。因此,我将约束设置为在用户触摸时开始并在用户结束拖动手势时停止。