You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 22:15:27