SwiftUI 中自定义网格视图的额外不必要渲染问题
Posted
技术标签:
【中文标题】SwiftUI 中自定义网格视图的额外不必要渲染问题【英文标题】:Extra unneeded rendering issue with Custom Grid View in SwiftUI 【发布时间】:2021-12-26 23:00:58 【问题描述】:我在不使用 Apple Grid 的情况下制作 CustomGridView,我的代码运行良好,但是当我添加新行时它有一个额外的渲染问题!只要我在我的代码中看到,如果我添加新行,Xcode 应该只为新项目渲染!但事实上它渲染了所有的项目!我找不到问题!如果我添加新列,Xcode 会渲染所有项目,我没问题,因为大小会影响每个项目,但是添加行后,对项目没有一般影响!
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))
【问题讨论】:
【参考方案1】:您需要使 cell 视图可相等,以便 SwiftUI 知道是否应该重新渲染它。
使用 Xcode 13.2 / ios 15.2 测试
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
然后在网格内部将其用作
ForEach(gridItemColumn) columnItem in
EquatableView(content: GridItemView(rowItem: rowItem,
columnItem: columnItem, size: size))
【讨论】:
我对我的方法几乎感到失望,但多亏了你,它正在发挥作用。我使用了您的代码并更新为static func == (lhs: GridItemView, rhs: GridItemView) -> Bool return (lhs.string == rhs.string) && (lhs.size == rhs.size)
现在它也适用于列。以上是关于SwiftUI 中自定义网格视图的额外不必要渲染问题的主要内容,如果未能解决你的问题,请参考以下文章
如何在 SwiftUI WidgetKit 占位符中自定义占位符外观?
SwiftUI:如何在 MapKit 中自定义地理区域的位置?