Issue with moving text button element to different position on screen using Geometry Reader

35 views Asked by At

So this all started with me needing to figure out how to read an elements coordinates (element a) on a view so that I could move element b to element a's position. Its a fill in the blank type questions and I want the button with the correct answer to move on the screen to the black space inside the question.

I have been using https://swiftui-lab.com/communicating-with-the-view-tree-part-1/ to help me achieve this. I have it somewhat working. When I have the button sitting alone inside the ZStack of the main content view it works fine. However, I want to move that button to another position on the screen and group it in a HStack and VStack with other buttons/choices however, whenever I start added padding or putting it inside with other views it screws everything up and it no longer moves the correct position.

You won't be able to run my code right off that bat as I have some custom objects in there but I'm hoping by looking at my code and seeing what I'm trying to accomplish you may spot the problem

    import SwiftUI

struct MyTextPreferenceKey: PreferenceKey {
    typealias Value = [MyTextPreferenceData]
    
    static var defaultValue: [MyTextPreferenceData] = []
    
    static func reduce(value: inout [MyTextPreferenceData], nextValue: () -> [MyTextPreferenceData]) {
        value.append(contentsOf: nextValue())
    }
}

struct MyTextPreferenceData: Equatable {
    let viewIdx: Int
    let rect: CGRect
}

struct shortStoryPlugInQuestions: View {
    
    var shortStoryObjIn: shortStoryObject
    
    @State private var activeIdx: Int = 0
    @State private var fontOpacity: CGFloat = 0.0
    @State private var rects: [CGRect] = Array<CGRect>(repeating: CGRect(), count: 12)
    @State private var toggled: Bool = false
    
    var body: some View {
        
        var rightButtonXOff: CGFloat = 45
        var rightButtonYOff: CGFloat = 425
        
        ZStack(alignment: .topLeading) {
            
            //this button works as expected
            correctButton(sSO: shortStoryObjIn, activeIdx: $activeIdx, fontOpacity: $fontOpacity, rects: $rects, toggled: $toggled).zIndex(1)
            
            
            
            //when I put the button inside this stack, it no longer functions properly
            VStack{
                HStack{
                    correctButton(sSO: shortStoryObjIn, activeIdx: $activeIdx, fontOpacity: $fontOpacity, rects: $rects, toggled: $toggled)
                    
                    shortStoryPlugInChoiceButton(choiceString: "test2")
                    
                }.zIndex(1)
                HStack{
                    shortStoryPlugInChoiceButton(choiceString: "longerANSWER")
                    shortStoryPlugInChoiceButton(choiceString: "test4")
                    shortStoryPlugInChoiceButton(choiceString: "test5")
                }.zIndex(1)
            }.padding(.top, 400)

            
                
            
            VStack {
                
                progressBarSSPlugInQuestions(counter: $activeIdx, totalCards: shortStoryObjIn.plugInQuestionlist.count).padding(.bottom, 100)
   
                shortStoryScrollViewBuilder(sSO: shortStoryObjIn, fontOpacity: $fontOpacity, rects: $rects, activeIdx: $activeIdx, toggled: $toggled)
                

            }.onPreferenceChange(MyTextPreferenceKey.self) { preferences in
                for p in preferences {
                    self.rects[p.viewIdx] = p.rect
                }
            }
        }.coordinateSpace(name: "myZstack")
    }
}



struct shortStoryScrollViewBuilder: View {
    
    var sSO: shortStoryObject
    
    @Binding var fontOpacity: CGFloat
    @Binding var rects: [CGRect]
    @Binding var activeIdx: Int
    @Binding var toggled: Bool
    
    var body: some View{
        ScrollViewReader {scrollView in
            ScrollView(.horizontal){
                HStack{
                    ForEach(0..<sSO.plugInQuestionlist.count, id: \.self) {i in
                        MonthView(fontOpacity: $fontOpacity, sSO: sSO, questionNumber: i, idx: i)
                            .padding([.leading, .trailing], 30)

                    }
                }
                
            }.onChange(of: activeIdx) { newValue in
                scrollView.scrollTo(activeIdx)}.scrollDisabled(true)

        }
    }
}

struct MonthView: View {
    @Binding var fontOpacity: CGFloat
    var sSO: shortStoryObject
    var questionNumber: Int
    let idx: Int
    
    var body: some View {
        VStack{
            VStack{
                Text(sSO.plugInQuestionlist[questionNumber].questionPart1)
                    .font(Font.custom("Arial Hebrew", size: 18))
                    .padding([.leading, .trailing], 10)
                
                Text("fff" + sSO.plugInQuestionlist[questionNumber].missingWord + "fff")
                    .padding(4)
                    .font(Font.custom("Arial Hebrew", size: 18))
                    .foregroundColor(.black.opacity(fontOpacity))
                    .padding(.bottom, 2) // <- play with distance
                    .overlay(
                        RoundedRectangle(cornerRadius: 20, style: .continuous)
                            .fill(Color.black).frame(height: 2), // <- thickness of line
                        alignment: .bottom
                    )
                //.underline(true, color: .black.opacity(1))
                    .background(MyPreferenceViewSetter(idx: idx))
                    .padding([.leading, .trailing], 10)
                
                Text(sSO.plugInQuestionlist[questionNumber].questionPart2)
                    .font(Font.custom("Arial Hebrew", size: 18))
                    .padding([.leading, .trailing], 10)
                
                
            }.frame(width: 335, height: 200)
                .background(.teal)
            

        }

    }
}

struct correctButton: View {
    
    var sSO: shortStoryObject
    
    @Binding var activeIdx: Int
    @Binding var fontOpacity: CGFloat
    @Binding var rects: [CGRect]
    @Binding var toggled: Bool
    
    var rightButtonXOff: CGFloat = 0
    var rightButtonYOff: CGFloat = 0
    
    var body: some View {
        Button(action: {
            
            DispatchQueue.main.asyncAfter(deadline: .now() + 3.5) {
                if activeIdx < sSO.plugInQuestionlist.count - 1 {
                    activeIdx = activeIdx + 1
                    toggled.toggle()
                }
            }
            withAnimation(.easeInOut(duration: 1.5)) {
                toggled.toggle()
            }
        }, label: {
            Text("Test")
                .foregroundColor(.white)
            
        }).frame(width: 100, height: 30)
            .background(.blue)
            .offset(x: toggled ? rects[activeIdx].minX :rightButtonXOff, y: toggled ? rects[activeIdx].minY - 5: rightButtonYOff)
    }
}

struct choices: View {
    var body: some View {
        HStack{
            shortStoryPlugInChoiceButton(choiceString: "test2")
            shortStoryPlugInChoiceButton(choiceString: "longerANSWER")
        }
        HStack{
            shortStoryPlugInChoiceButton(choiceString: "test4")
            shortStoryPlugInChoiceButton(choiceString: "test5")
        }
    }
}

struct MyPreferenceViewSetter: View {
    let idx: Int
    
    var body: some View {
        GeometryReader { geometry in
            Rectangle()
                .fill(Color.clear)
                .preference(key: MyTextPreferenceKey.self,
                            value: [MyTextPreferenceData(viewIdx: self.idx, rect: geometry.frame(in: .named("myZstack")))])
        }
    }
}

struct shortStoryPlugInChoiceButton: View {
    var choiceString: String
    
    @State var defColor = Color.white
    @State private var pressed: Bool = false
    @State var selected = false
    var body: some View {
        Button(action: {
            
            defColor = Color.red
            
            withAnimation((Animation.default.repeatCount(5).speed(6))) {
                selected.toggle()
                
            }
            
            SoundManager.instance.playSound(sound: .wrong)
            selected.toggle()
            
        }, label: {
            Text(choiceString)
                .font(Font.custom("Arial Hebrew", size: 18))
                .padding(.top, 6)
                .padding([.leading, .trailing], 2)
            
        }).frame()
            .padding([.top, .bottom], 5)
            .padding([.leading, .trailing], 20)
             .background(defColor)
             .foregroundColor(.black)
             .cornerRadius(10)
             .shadow(radius: 5)
             .offset(x: selected ? -5 : 0)
  

    }
}

struct progressBarSSPlugInQuestions: View {
    
    @Binding var counter: Int
    let totalCards: Int
    
    var body: some View {
        VStack {
            
            Text(String(counter + 1) + "/" + String(totalCards)).offset(y:20)
                .font(Font.custom("Arial Hebrew", size: 28))
                .bold()
            
            ProgressView("", value: Double(counter), total: Double(totalCards - 1))
                .tint(Color.orange)
                .frame(width: 300)
                .scaleEffect(x: 1, y: 4)
                
            
            
        }
    }
}

struct preview: View{
    var storyData2: shortStoryData { shortStoryData(chosenStoryName: "Cristofo Columbo")}
    
    var storyObj2: shortStoryObject {storyData2.collectShortStoryData(storyName: "Cristofo Columbo")}
    var body: some View{
        shortStoryPlugInQuestions(shortStoryObjIn: storyObj2)
    }
}


struct shortStoryPlugInQuestions_Previews: PreviewProvider {
    
    static var previews: some View {
 
        
        preview()
    }
}
0

There are 0 answers