如何在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()有什么想法吗?谢谢!
发布于 2020-10-29 14:42:56
昨天我也遇到了类似的问题。但是我试图从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
}发布于 2021-03-04 16:34:02
虽然将代码放入函数是一个很好的做法,但可能会遇到另一个问题,那就是在更新阶段更改@State变量:[SwiftUI] Modifying state during view update, this will cause undefined behavior
在视图更新阶段之后使用NotificationCenter移动@State变量更新可能会有所帮助,但可以使用更简单的解决方案,比如使用DispatchQueue在呈现阶段之后立即执行变量更新。
@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")
}
}发布于 2021-06-06 11:40:04
因此,在GeometryReader中更新@State是完全可能的。解决方案很简单。然而,有一个警告:
你可能会得到一个无限循环的(没什么麻烦的,我将在这里给出一个解决方案)
您只需要一个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)")
}
}
}这是因为视图将“绘制”GeometryReader,这将更新视图的@State。因此,新的@State会使视图无效,从而导致视图被重新绘制。因此返回到第一步(绘制GeometryReader并更新状态)。
要解决这个问题,您需要在GeometryReader的绘制中添加一些约束。不是在GeometryReader中返回视图,而是绘制它,然后添加GeometryReader作为透明覆盖或背景。这将具有相同的效果,但您将能够在演示文稿中添加约束。
就我个人而言,我更喜欢使用覆盖,因为你可以添加你想要的数量。请注意,覆盖图不允许在其中包含if else,但可以调用函数。这就是为什么下面有func geometryReader()的原因。另一件事是,为了返回不同类型的视图,您需要在它之前添加@ViewBuilder。
在这段代码中,GeometryReader只被调用一次,您将更新@State。
最终解决方案:
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,我想使用拖动手势移动视图。因此,我将约束设置为在用户按下时启动,并在用户结束拖动手势时停止。
https://stackoverflow.com/questions/61027327
复制相似问题