Список большого количества элементов вылетает при прокрутке в SwiftUI, как я могу это исправить?

#swiftui #swiftui-list #swiftui-foreach

#swiftui #swiftui-список #swiftui-foreach

Вопрос:

У меня есть простой список элементов в SwiftUI следующим образом:

                     ForEach((1...50), id: .self) {
                        Text("($0)….........")
                        Text("($0)….........")
                        Text("($0)….........")
                        Text("($0)….........")
                    }
 

Я сталкиваюсь с той же проблемой с более сложными представлениями, но я упростил ее здесь, используя только текстовое представление. это порождает ту же проблему

Ошибка сбоя:

 2021-02-13 21:03:07.124039 1100 rawateb[1696:71245] [ServicesDaemonManager] interruptionHandler is called. -[FontServicesDaemonManager connection]_block_invoke
2021-02-13 21:03:07.163944 1100 rawateb[1696:71245] XPC connection interrupted
Message from debugger: Terminated due to signal 9
 

Итак, как я могу обработать большой список элементов без сбоев

Обновить

Мой полный код

 //
//  CalendarView.swift
//  rawateb
//
//  Created by Hatim Hoho on 7/2/21.
//

import SwiftUI
import Combine
import QGrid


struct CalendarView: View {
    
    
    
    //@ObservedObject var viewModel = CalendarViewModel()
    @EnvironmentObject var settings: UserSettings
    @Environment(.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass?
    @Environment(.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass?
    @State var saleries = [FutureSalery]()

    var body: some View {
        ScrollView {
            VStack {
                GeometryReader { geometry in
                    
                    
                    HStack{
                        Button(action: {
                            print("Button was tapped")
                            // add new future salery
                            self.addFutureSalery()
                        }) {
                            Image(systemName: "plus.circle")
                                .resizable()
                                .foregroundColor(.blue)
                                
                        }
                        .padding()
                        .frame(width: 70, height: 70, alignment: .center)


                        Text("رواتب الأشهر القادمة")
                            .font(.title2)
                            .bold()
                            .multilineTextAlignment(.center)
                            .foregroundColor(Color("labelColor"))
                            
                        Button(action: {
                            print("Button was tapped")
                            self.removeFutureSalery()
                        }) {
                            Image(systemName: "minus.circle")
                                .resizable()
                                .foregroundColor(.blue)
                                
                        }
                        .padding()
                        .frame(width: 70, height: 70, alignment: .center)

                    }
                    .padding([.top, .bottom], 38)
                    .offset(y: geometry.frame(in: .global).minY > 38 ? -geometry.frame(in: .global).minY 38 : 0)
                    
                    .frame(width: geometry.size.width)
                    .blur(radius: -geometry.frame(in: .global).minY * 0.38)
                }
                .frame(height: 150)
                
                
                Spacer()
                
                //List {
                    ForEach((1...50).reversed(), id: .self) {
                        Text("($0)….........")
                        Text("($0)….........")
                        Text("($0)….........")
                        Text("($0)….........")
                    }

              
               // }
//                QGrid(self.saleries, columns: 2) { salery in
//                    SaleryCellItem2(salery: salery)
//                }
//                .padding()
            }
        }
    }
}

struct SaleryCellItem : View {
    var salery: FutureSalery
    
    var body: some View {
        GeometryReader(content: { geometry in
            HStack {
                Text("(salery.remainingDays)")
                    
                    .frame(width: geometry.size.width / 3, height: geometry.size.height, alignment: .center)
                
                
                VStack(alignment: .trailing) {
                    Text(salery.hjDateString)
                    Text(salery.acDateString)
                }
                .frame(width: geometry.size.width / 3 * 2, height: geometry.size.height, alignment: .trailing)
                .offset(CGSize(width: -45, height: 0))
                
            }
        })
        .frame(width: .none, height: 90, alignment: .center)
        .background(Color("listItemBGColor"))
        .cornerRadius(8)
    }
}


struct SaleryCellItem2 : View {
    var salery: FutureSalery
    
    var body: some View {
            VStack {
                Text("(salery.remainingDays)")
                    
                
                
                VStack(alignment: .trailing) {
                    Text(salery.hjDateString)
                    Text(salery.acDateString)
                }
                
            }
        }
}

extension CalendarView {
    func addFutureSalery() {
        // get the last salery in list
        var lastDate = Date()
        if (saleries.count > 0) {
            lastDate = saleries.last!.dateObject
        }
        
        var dayOfSalery = 1
        switch settings.organizationType {
        case SaleryOrgType.gov.rawValue:
            dayOfSalery = 27
        case SaleryOrgType.privateSector.rawValue:
            dayOfSalery = Int(UserDefaults.standard.double(forKey: "saleryDayIfPrivateSector"))
        case SaleryOrgType.taqaod.rawValue:
            dayOfSalery = 20
        default:
            dayOfSalery = 1
        }
        
        // if list is empty -> get the soonest salery from today date
        // get the next salery after that salery
        // append
        var nearestDate = Date()
        if (saleries.count > 0) {
            nearestDate = lastDate.nextMonthDate(withDayNumber: dayOfSalery)
            
        } else {
            // first item
            nearestDate = lastDate.nearestDate(withNumber: dayOfSalery)
            
        }
        
        self.saleries.append(FutureSalery(dateObject: nearestDate))
        print("count (self.saleries.count)")
        
    }
    
    func removeFutureSalery() {
        if(self.saleries.count > 0) {
            self.saleries.removeLast()
        }
        print("count (self.saleries.count)")
        
    }

}

struct DateItem: Identifiable {
    var id = UUID()
    var title:String
}

struct CalendarView_Previews: PreviewProvider {
    static var previews: some View {
        Group {

            CalendarView()
                .previewDevice(PreviewDevice(rawValue: "iPhone 11 Pro Max"))
               .previewDisplayName("iPhone 11 Pro Max")

        }

    }
}

 

Комментарии:

1. Можете ли вы показать больше кода? Я не вижу никаких проблем, если я вставлю приведенный выше код в список.

2. ScrollView не имеет собственной внутренней геометрии, поэтому ни spacer, ни geometry reader не должны находиться на верхнем уровне. Это может быть причиной сбоя (движок рендеринга скорее задействован в поиске некоторой привязки к макету).

Ответ №1:

Спасибо @Asperi за предоставление решения в комментариях.

Проблема заключается в чтении геометрии.

GeometryReader не должен находиться внутри ScrollView, потому что это вызовет утечку памяти.

Итак, всякий раз, когда у нас есть такой код, как этот

         ScrollView {
            VStack {
                 GeometryReader(content: { geometry in   // <- look at this
                 ...

 

он должен быть изменен на этот

     GeometryReader(content: { geometry in   // <- look at this
        ScrollView {
            VStack {
                 ...

 

Спасибо