2

I'm testing out Prafulla Singh's marquee text in Swift and I am currently encountering and error where a variable is claiming to not be in the scope even though elsewhere in what would appear to be the same scope, it is called and working. I'm attaching the whole code because I feel as if that's necessary to see the variable scope.

struct test_marquee: App {
    var body: some Scene {
        WindowGroup {
            struct Marque: View {
                let text: String
                @State private var moveView = false
                @State private var stopAnimation = false
                // ####
                @State private var textFrame: CGRect = CGRect() //DECLARATION FOR ERROR VARIABLE
                public init(text: String) {
                    self.text = text
                }
                var body: some View {
                    GeometryReader { proxy in
                        ScrollView(.horizontal, showsIndicators: false, content: {
                            Text(text)
                                .lineLimit(1)
                                .background(GeometryGetter(rect: $textFrame)).offset(moveView ? CGSize(width: -1 * textFrame.width, height: 0) : CGSize(width: proxy.size.width, height: 0))
                                .onAppear() {
                                    self.stopAnimation = false
                                    animateView()
                                    moveViewOnAnimationEnd()///scrollViewProxy.scrollTo("Identifier") /// does not animate
                                }.onDisappear() {
                                    self.stopAnimation = true
                                }
                        }).mask(LinearGradient(gradient: Gradient(colors: [Color.clear, Color.black, Color.black, Color.clear]), startPoint: .leading, endPoint: .trailing))
                        .padding([.top, .bottom], 100)
                    }
                }
                private func animateView() {
                    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1, execute: { //after 0.5 sec
                        withAnimation(Animation.linear(duration: Double(textFrame.width) * 0.01)) {
                            // ######
                            //textFrame IS CALLED HERE WITH NO ISSUES
                            moveView = true
                        }//no on completion so need to add another time bound method to restart animation from start
                    })
                }
                private func moveViewOnAnimationEnd() {
                    // ######
                    let timeToAnimate = (Double(textFrame.width) * 0.01) + 0.2
                    // ERROR OCCURS ON THIS LINE ON THE textFrame rect
                    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + timeToAnimate, execute: { //after 0.5 sec
                        moveView = false
                        if stopAnimation == false {
                            animateView()
                            moveViewOnAnimationEnd()
                        }
                    })
                }
                
                
                struct GeometryGetter: View {
                    @Binding var rect: CGRect
                    
                    var body: some View {
                        GeometryReader { (proxy) -> Path in
                            DispatchQueue.main.async {
                                self.rect = proxy.frame(in: .global)
                            }
                            return Path()
                        }
                    }
                }
                
                struct Marque_Previews: PreviewProvider {
                    
                    static var previews: some View {
                        Marque(text: "If you don't provide your own init with an explicit public modifier the generated constructor will be marked internal")
                    }
                }
            }
        }
    }
}
AnderCover
  • 2,488
  • 3
  • 23
  • 42
Gavin Tynan
  • 103
  • 1
  • 6
  • Press Control-I and see how the indentation and delimiter balancing works out. Perhaps the scope is not where you think it is. – matt May 12 '21 at 02:42
  • Just tried that, both functions are in the same scope of the Marque struct/where the rect is defined. – Gavin Tynan May 12 '21 at 02:46
  • Your code does not compile on Xcode Version 12.5 (12E262): `Closure containing a declaration cannot be used with result builder 'ViewBuilder'` – AnderCover May 12 '21 at 08:55

1 Answers1

2

It's possibly a bug of the ViewBuilder (or _functionBuilder) which is what WindowGroup is using for its content... But why would you declare Marque type inside WindowGroup { } content closure?

Declare it normally, outside of WindowGroup's closure, but inside the outer struct, if you'd like:

struct TestMarquee: App {
   struct Marque: View {
      // the rest of your code...
   }

   var body: some Scene {
      WindowGroup {
         Marque(text: "whatever") // instantiate here; don't declare
      }
   }
}

The { } of WindowGroup signify a trailing closure syntax. But this is a "special" type of closure used by @ViewBuilder which extracts (read here for more info) Views from each block of code inside the closure. These aren't { } used for to define a scope like that of a struct or a function.

New Dev
  • 48,427
  • 12
  • 87
  • 129