0

I have come across a very unfortunate problem. I have constructed a view in SwiftUI and it is working perfectly, however only if it is the root view. Navigating to it via NavigationLink leads to a weird problem where only the elements that are visible when the view appears, work. Every element in the ScrollView that is further down than what is initially available doesn't behave correctly.

Here is the code (simplified for easier reading:

    import SwiftUI
    
    struct CameraModelPopupView: View {
        @Binding var isPresented: Bool
        @Binding var selectedCamera: CameraModel?  
      
        @State var addingCamera: Bool = false
        @State private var searchText = ""
    
        @State var cameraModels: [CameraModel] = loadDB("cameras.json")
        
        @State private var selectedItemIndex: Int? = nil
    
        private var filteredCameraModels: [CameraModel] {
    
            //Do rest
            if searchText.isEmpty {
                return cameraModels
            } else {
                return cameraModels.filter { $0.modelName.localizedCaseInsensitiveContains(searchText) || $0.manufacturerName.localizedCaseInsensitiveContains(searchText) }
            }
    
        }
    
        var body: some View {
           NavigationView {
                VStack(spacing:0) {
//Search-bar and other
                    HStack{
                        Spacer()
                        SearchBar(text: $searchText)
                            //.padding(.horizontal)
                        Button(action: {
                                        addingCamera = true
                                    }) {
                                        Image(systemName: "plus.square.dashed").resizable()
                                            .frame(width: 20, height: 20)
                                    }.padding(.horizontal)
                        Spacer()
                    }
                    .background(Material.ultraThick)
                    .background(Color(hex: "#2d4269"))
//Main View (problems here)
            
                    ScrollView {
                            LazyVGrid(columns: [GridItem(.adaptive(minimum: 150), spacing: 10)]) {
                                ForEach(filteredCameraModels, id: \.id) { camera in
                                    CardFlip(camera: camera, nw: $nw, isSelected: $isPresented, selectedItemIndex: $selectedItemIndex, selectedCamera: $selectedCamera)
                                }
                            }
                            .padding(.horizontal)
                        .padding(.vertical, 8)
                        
                    }
                    .background(Material.regularMaterial)
                    .background(Color(hex: "#2d4269"))
                    .navigationTitle("Camera Models")
                    .navigationBarTitleDisplayMode(.inline)
                    .toolbar {
                        ToolbarItem(placement: .navigationBarTrailing) {
                            Button("Cancel") {
                                isPresented = false
                            }
                        }
                    }
                    
                }.onAppear(){
                    let customCameras = loadUserCustomCameras()
                    cameraModels.append(contentsOf: customCameras)
            
                }
                        
                        .fullScreenCover(isPresented: $addingCamera){
                            AddCameraView(cameraModels: $cameraModels, isPresented: $addingCamera)
                        }
            }
        }
    }

When removing the LazyVGrid, everything works fine, this is what confuses me so much. The CardFlip elements are views that flip to reveal a back side with information about the camera. But with the LazyVGrid only the cards that are visible when accessing the view flip and the lower ones don't. When the view is the root view, all cards flip. When I remove the LazyVGrid, all cards flip.

Any and all help would be greatly appreciated!

EDIT: Here is the CardFlip implementation as requested:

import SwiftUI import CachedAsyncImage

//Main Ansicht die Vorne und Hinten verbindet
struct CardFlip: View {
    let camera: CameraModel
    @State var backDegree = -90.0
    @State var frontDegree = 0.0
    @State var isFlipped = false
    @Binding var nw: NetworkMonitor
    @Binding var isSelected: Bool
    @Binding var selectedItemIndex: Int?
    @Binding var selectedCamera: CameraModel?
    @State var timmre: Timer?
    
        
        let width : CGFloat = 250
        let height : CGFloat = 350
        let durationAndDelay : CGFloat = 0.2
    var body: some View {
        ZStack {
            FrontCameraCard(camera:camera, width: width, height: height, degrees: $frontDegree, nw: $nw)
            BackCameraCard(camera: camera, width: width, height: height, degrees: $backDegree, isFlipped: $isFlipped, nw: $nw, isSelected: $isSelected, selectedCamera: $selectedCamera)
                }.onTapGesture {
                    selectedItemIndex = camera.id
                    flipCard ()
                }
                .onAppear(){
                    timmre = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true){timer in
                        if selectedItemIndex != camera.id && isFlipped{
                            flipCard()
                        }
                    }
                }
                .onDisappear(){
                    timmre?.invalidate()
                    timmre = nil
                }
    }
    
    
    
    //Flip funktion
    func flipCard () {
            isFlipped = !isFlipped
            if isFlipped {
                withAnimation(.linear(duration: durationAndDelay)) {
                    frontDegree = 90
                }
                withAnimation(.linear(duration: durationAndDelay).delay(durationAndDelay)){
                    backDegree = 0
                }
            } else {
                withAnimation(.linear(duration: durationAndDelay)) {
                    backDegree = -90
                }
                withAnimation(.linear(duration: durationAndDelay).delay(durationAndDelay)){
                    frontDegree = 0
                }
            }
        }
}



//Ansicht für vorne (Bild und titel)
struct FrontCameraCard: View{
    let camera: CameraModel
    let width : CGFloat
    let height : CGFloat
    @Binding var degrees: Double
    @Binding var nw: NetworkMonitor
    var body: some View{
        VStack(alignment: .leading) {
            if !UserDefaults.standard.bool(forKey: "LoadOverWifiOnly") || !nw.isCellular || !nw.isConnected{
                GeometryReader { geometry in
                    CachedAsyncImage(url: URL(string: camera.modelImage), urlCache: .shared) { image in
                        image
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(width: geometry.size.width, height: geometry.size.height)
                            .background(Color.white)
                    }
                placeholder:  {
                    ZStack{
                        Rectangle()
                    }
                }
                }
            }
            else{
                //WIP
            }

            
            VStack(alignment: .leading){
                Text("\(camera.manufacturerName) \(camera.filmOrDigital)")
                    .font(.system(size: 10))
                
                
                Text(camera.modelName)
                    .font(.headline)
            }
            .padding()
        }
        .background(Material.ultraThickMaterial)
        
    .cornerRadius(12)
        //Das hier muss auf das äußerste view Element/auf die äußerste view gruppe
        .rotation3DEffect(Angle(degrees: degrees), axis: (x: 0, y: 1, z: 0))
    }
}





//Ansicht hinten
struct BackCameraCard: View{
    let camera: CameraModel
    let width : CGFloat
        let height : CGFloat
    @Binding var degrees: Double
    @Binding var isFlipped: Bool
    @Binding var nw: NetworkMonitor
    @Binding var isSelected: Bool
    @Binding var selectedCamera: CameraModel?
    @State var CameraDetai: Bool = false
    
    
 //   zugehöriger command um die kamera dann feszulegen: selectedCamera = camera
    var body: some View{
            VStack(alignment: .leading) {
                ZStack{
                    HStack{
                        Spacer()
                        Text("Formats")
                            .font(.headline)
                        Spacer()
                    }.padding(10)
                    HStack{
                        Spacer()
                        Button(action: {
                                        CameraDetai = true
                                    }) {
                                        Image(systemName: "info.square.fill")
                                            .foregroundColor(.primary)
                                    }
                    }.padding(.horizontal)
                        .opacity(isFlipped ? 0.5 : 0.0)
                }
                
                HStack{
                    Text("Format")
                        .font(.system(size: 10))
                    Spacer()
                    Text("Crop")
                        .font(.system(size: 10))
                   Spacer()
                    Text("Sensor area")
                        .font(.system(size: 10))
                }.padding(.horizontal)
//                Spacer()
                ScrollView{
                    LazyVGrid(columns: [GridItem(.flexible())]) {
                        ForEach(Array(camera.formats.enumerated()), id: \.element) { index, format in
                            if camera.isBodyOnly {
                            CardFlipRow(item: .init(
                                id: index,
                                title: format[0],
                                width: Double(format[1])!,
                                height: Double(format[2])!,
                                cropFactor: Double(43.2666153/sqrt(pow(Double(format[1])!, 2)+pow(Double(format[2])!, 2)))
                            ), camera: camera, selectedCamera: $selectedCamera, showingSheet: $isSelected)
                            .shadow(color: Color.black.opacity(0.1), radius: 10, y: 3)
                            .opacity(isFlipped ? 1 : 0)
                            .animation(.easeIn(duration: 0.5).delay(Double(0.15 + (0.15*Double(index)))))
                        }
                            else{
                                CardFlipRow(item: .init(
                                    id: index,
                                    title: format[0],
                                    width: Double(format[1])!,
                                    height: Double(format[2])!,
                                    cropFactor: Double(43.2666153/sqrt(pow(camera.sensorWidth, 2)+pow(camera.sensorHeight, 2)))
                                ), camera: camera, selectedCamera: $selectedCamera, showingSheet: $isSelected)
                                .shadow(color: Color.black.opacity(0.1), radius: 10, y: 3)
                                .opacity(isFlipped ? 1 : 0)
                                .animation(.easeIn(duration: 0.5).delay(Double(0.15 + (0.15*Double(index)))))
                            }
                        }
                        
                        
                    }.padding(5)
                }
                
                Spacer()
            }
        
      //  .padding()
            .frame(maxWidth: 250, idealHeight: 250, maxHeight: 350)
//        .background(Material.regularMaterial)
            .background(Material.ultraThickMaterial)
    
        .cornerRadius(12)
//        .shadow(color: Color.black.opacity(0.1), radius: 10, y: 3)
//        .shadow(color: Color.black.opacity(0.1), radius: 10, y: 3)
        //Das hier muss auf das äußerste view Element/auf die äußerste view gruppe
        .rotation3DEffect(Angle(degrees: degrees), axis: (x: 0, y: 1, z: 0))
        .fullScreenCover(isPresented: $CameraDetai){
            NavigationView{
                CameraDetail(cameraModel: generic35)
                    .navigationTitle("Camera Specs")
                    .navigationBarTitleDisplayMode(.inline)
                    .navigationBarItems(leading: Button(action: {
                        self.CameraDetai.toggle()
                    }, label: {
                        Text("Close").foregroundColor(.red)
                    }))
            }.edgesIgnoringSafeArea(.all)
        }
    }
}
Herbert
  • 21
  • 5

0 Answers0