Home > Back-end >  Extra unneeded rendering issue with Custom Grid View in SwiftUI
Extra unneeded rendering issue with Custom Grid View in SwiftUI

Time:12-28

I am making a CustomGridView without using Apple Grid, my codes works fine but it has an extra rendering issue when I add new row! As long as I see in my codes, if I add new row, Xcode should render just for new Items! But in the fact it renders all the items! which I can not find the issue! I am okay if Xcode renders all Items if I add new column, because the size put effect on every single Item, but with adding row, there is no general effect on items!

import SwiftUI

struct GridItemView: View {

    let rowItem: GridItemRowType
    let columnItem: GridItemColumnType
    let string: String
    let size: CGFloat
    @State private var color: Color = Color.randomColor

    init(rowItem: GridItemRowType, columnItem: GridItemColumnType, size: CGFloat) {
        self.rowItem = rowItem
        self.columnItem = columnItem
        self.size = size
        self.string = "("   String(describing: rowItem.index)   ","   String(describing: columnItem.index)   ")"
    }

    var body: some View {
        
        print("rendering for:", string)
        
        return RoundedRectangle(cornerRadius: 10.0)
            .fill(color)
            .padding(5.0)
            .frame(width: size, height: size)
            .overlay(
                
                Text(string)
                    .bold()
                    .foregroundColor(Color.white)
                    .shadow(color: .black, radius: 2, x: 0.0, y: 0.0)
                
            )
            .onTapGesture {
                color = Color.white
            }
            .shadow(radius: 20.0)
        
    }

}

extension Color {

    static var randomColor: Color {
        get { return Color(red: Double.random(in: 0...1), green: Double.random(in: 0...1), blue: Double.random(in: 0...1)) }
    }

}

struct CustomGridView: View {

    let gridItemColumn: [GridItemColumnType]
    let gridItemRow: [GridItemRowType]

    var body: some View {

        GeometryReader { proxy in
            
            let size: CGFloat = (proxy.size.width - 5.0)/CGFloat(gridItemColumn.count)
            
            ScrollView {

                VStack(spacing: .zero) {

                    ForEach(gridItemRow) { rowItem in

                        HStack(spacing: .zero) {

                            ForEach(gridItemColumn) { columnItem in

                                GridItemView(rowItem: rowItem, columnItem: columnItem, size: size)
                                
                            }
                            
                        }
                        .transition(AnyTransition.asymmetric(insertion: AnyTransition.move(edge: .top), removal: AnyTransition.move(edge: .top)))
                        
                    }
                    
                    Spacer()
                    
                }
                .transition(AnyTransition.asymmetric(insertion: AnyTransition.move(edge: .trailing), removal: AnyTransition.move(edge: .trailing)))
                
            }
            .position(x: proxy.size.width/2.0, y: proxy.size.height/2.0)
        }
        .animation(Animation.default, value: [gridItemRow.count, gridItemColumn.count])

        
    }
}



struct GridItemColumnType: Identifiable {
    
    let id: UUID = UUID()
    var index: Int
    
    static let initializingGridItemColumn: [GridItemColumnType] = [GridItemColumnType(index: 0),
                                                                   GridItemColumnType(index: 1),
                                                                   GridItemColumnType(index: 2),
                                                                   GridItemColumnType(index: 3),
                                                                   GridItemColumnType(index: 4)]
}


struct GridItemRowType: Identifiable {
    
    let id: UUID = UUID()
    var index: Int
    
    static let initializingGridItemRowType: [GridItemRowType] = [GridItemRowType(index: 0),
                                                                 GridItemRowType(index: 1),
                                                                 GridItemRowType(index: 2)]
}


struct ContentView: View {

    @State private var gridItemColumn: [GridItemColumnType] = GridItemColumnType.initializingGridItemColumn
    @State private var gridItemRow: [GridItemRowType] = GridItemRowType.initializingGridItemRowType

    var body: some View {

        CustomGridView(gridItemColumn: gridItemColumn, gridItemRow: gridItemRow)

        Spacer()
        
        Button("add Column") { gridItemColumn.append(GridItemColumnType(index: gridItemColumn.count)) }
        
        Button("add Row") { gridItemRow.append(GridItemRowType(index: gridItemRow.count)) }
        
    }

}

enter image description here

CodePudding user response:

You need to make cell view equatable, so SwiftUI would know if it should be re-rendered.

Tested with Xcode 13.2 / iOS 15.2

demo

struct GridItemView: View, Equatable {
    // The `string` property is used just for demo, in real you 
    // need some unique identifier which represents that view
    // has same content
    static func == (lhs: GridItemView, rhs: GridItemView) -> Bool {
        lhs.string == rhs.string
    }

// ... other code
}

and then inside grid use it as

ForEach(gridItemColumn) { columnItem in

    EquatableView(content: GridItemView(rowItem: rowItem, 
                                     columnItem: columnItem, size: size))

}
  • Related