SwiftUI蛇形LazyVGrid实现:每行旋转效果技术求助
实现SwiftUI蛇形网格的解决方案
不用旋转视图,而是通过调整数据的排列顺序来实现蛇形布局,这样既能避免旋转带来的交互问题,也不需要多个ForEach,更适合支持拖放操作的场景。
步骤1:添加数组分块扩展
先给Array加一个分块扩展,方便把原始数据按列数拆分成多行:
extension Array { func chunked(into size: Int) -> [[Element]] { stride(from: 0, to: count, by: size).map { Array(self[$0..<Swift.min($0 + size, count)]) } } }
步骤2:生成蛇形排列的数据源
在ContentView里添加计算属性,将原始数据转换成蛇形顺序:
private var snakeOrderedHand: [Card] { let columnCount = columns.count // 按列数拆分出每行数据 let rows = game.hand.chunked(into: columnCount) // 奇数行反转顺序,偶数行保持原样,再合并成一维数组 return rows.enumerated().flatMap { rowIndex, row in rowIndex % 2 == 1 ? row.reversed() : row } }
步骤3:修改LazyVGrid的遍历逻辑
直接用蛇形排列后的数组渲染网格,每行元素会自动呈现蛇形布局:
var body: some View { LazyVGrid(columns: columns, spacing: 8) { ForEach(snakeOrderedHand, id: \.id) { card in CardView(game: game, card: card) } // 支持拖放的索引映射处理 .onMove { sourceIndices, destination in // 把蛇形数组的索引转换成原始数组的索引 let originalSources = sourceIndices.map { snakeToOriginalIndex[$0] } let originalDestination = snakeToOriginalIndex[destination] // 对原始数组执行拖放移动 game.hand.move(fromOffsets: originalSources, toOffset: originalDestination) } } } // 辅助属性:建立蛇形索引到原始数据索引的映射关系 private var snakeToOriginalIndex: [Int] { let columnCount = columns.count let rowIndices = game.hand.indices.chunked(into: columnCount) return rowIndices.enumerated().flatMap { rowIndex, indices in rowIndex % 2 == 1 ? indices.reversed() : indices } }
为什么不推荐旋转视图?
直接对行或卡片应用rotationEffect会带来以下问题:
- 卡片内容会被反转,需要额外再反转回来,增加逻辑复杂度
- 视图坐标系反转后,拖放、点击等交互的实际位置会和视觉位置不匹配,影响用户体验
- LazyVGrid本身不支持直接对整行应用旋转,只能逐个处理元素,效率更低
内容的提问来源于stack exchange,提问作者DannyDevitoPhD
相关产品推荐
相关产品推荐

