3

I have a widget list in scrollview, each row have 3 elements. Some elements will show a stopwatch by SwiftUI Text timer. The Text timer will increase by 1 every second. Every thing work well at first, but If I scroll the list too fast, the Text timer will be freeze, won't update anymore, unless scroll the list again.

Bellow recording is my screen recording at iPhone 12 Pro Max. For the first time, slowly scroll from the "Category 2" position to the top, and the stopwatch refreshes normally. For the second and third times, quickly scroll to the top, and the stopwatch stops and never refreshes.

enter image description here

Here is my code:

import SwiftUI

extension Date {
    func formatDate(_ format: String) -> String {
        let formatter = DateFormatter()
        formatter.dateFormat = format
        return formatter.string(from: self)
    }
    
    func getZeroData() -> Date {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        
        var startDateStr = self.formatDate("yyyy-MM-dd")
        startDateStr = "\(startDateStr) 00:00:00"

        return dateFormatter.date(from: startDateStr) ?? Date()
    }
}

struct SectionHeaderView: View {
    let title: String
    var body: some View {
        HStack() {
            Text(title)
                .font(.system(size: 19.0))
                .fontWeight(.bold)
            Spacer()
        }
        .padding(.leading, 10.0)
        .frame(height: 44.0)
    }
}

struct CellView: View {
    let date: Date
    let categoryIndex: Int
    let rowIndex: Int
    
    init(date: Date, categoryIndex: Int, rowIndex: Int) {
        self.date = date
        self.categoryIndex = categoryIndex
        self.rowIndex = rowIndex
        //print("CellView init is \(self.categoryIndex)->\(self.rowIndex)")
    }
    
    var body: some View {
        print("CellView show is \(self.categoryIndex)->\(self.rowIndex)")
        // 1. cellview has a very complex layout, so I need to use GeometryReader to get size for inner complex component.
        // 2. In order to show the problem conveniently, only the test code is shown here, but the problem in the video above can also be reproduced
        return GeometryReader { geo in
            VStack() {
                Text("\(categoryIndex) -> \(rowIndex)")
                if((self.categoryIndex==0 || self.categoryIndex==2) && self.rowIndex<=4) {
                    Text(date.getZeroData(), style: .timer)
                    Text(date.formatDate("HH:mm:ss"))
                }
            }
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
            .background(Color.gray.opacity(0.2))
        }
    }
}

struct ContentView: View {
    let date: Date = Date()
    let widgetGap: CGFloat = 10.0
    let widgetWidth: CGFloat
    let widgetHeight: CGFloat
    let columns: [GridItem]
    //let columns: [GridItem] = Array(repeating: .init(.fixed(UIScreen.main.bounds.width/3.0), spacing: 0), count: 3)
    
    init() {
        let col: Int = 3
        widgetWidth = (UIScreen.main.bounds.width - widgetGap * CGFloat(col + 1))/CGFloat(col)
        //let widgetSize = CGSize(width: 160, height: 160)
        widgetHeight = widgetWidth
        columns = Array(repeating: .init(.fixed(widgetWidth), spacing: widgetGap), count: col)
    }
    
    var body: some View {
        print("ContentView show")
        return ScrollView() {
            ForEach(0 ..< 6, id: \.self) { categoryIndex in
                Section(header: SectionHeaderView(title: "Category \(categoryIndex)") ){
                    LazyVGrid(columns: columns, spacing: widgetGap + 5.0) {
                        ForEach(0 ..< 13, id: \.self) { rowIndex in
                        // 1. cellview has a very complex layout, so I need to use compositingGroup before cornerRadius to get a high accuracy radius
                            CellView(date: date, categoryIndex: categoryIndex, rowIndex: rowIndex)
                                .frame(width: widgetWidth, height: widgetHeight, alignment: .center)
                                .compositingGroup()
                                .cornerRadius(10)
                                .shadow(color: .black.opacity(0.05), radius: 6)
                        }
                    }
                }
            }
        }
    }
}

I also try to use List to replace Scrollview + Lazyvgrid. This situation has improved. But List is hard to custom style, such as remove seperateline , remove padding.

Is there any way to solve this problem?

SOS!!

mars
  • 109
  • 5
  • 21
  • 1
    Try getting rid of `.cornerRadius(10)` and use a `RoundedRectangle` instead. Also, the code you have in the `ContentView` `init` should be put it in `onAppear` – lorem ipsum Sep 29 '21 at 12:56

0 Answers0