如何使用 SwiftUI (macOS) 制作自定义 WebView 的动态 LazyVGrid?

How to make a dynamic LazyVGrid of custom WebViews with SwiftUI (macOS)?

我目前正在开发一个应用程序,它在一个列中显示不同 SwiftUI 文件的代码。我正在使用多个 WKWebView 实例来显示每个文件(便于托管 + built-in 语法突出显示支持)。 WebView 的大小适合带有填充的容器:

对于一些较大的项目——包含大量文件——我正在尝试实现一个 LazyVGrid 来显示较小的支持文件 side-by-side,如下所示:

问题

但是,当 window 变小时,我无法将网格调整为 single-column(如果用户使用 super-wide 监控;虽然,这不是一个优先级)。 LazyVGrid 是解决此问题的最佳方法,还是我应该采用不同的方法?我觉得我很亲近。

GridLayout.swift

struct GridLayout: View {
  let data: [String]
  enum dataType { case title; case url }
  let columns = [GridItem(.flexible()), GridItem(.flexible())]
  
  func get(_ forType: dataType, _ string: String) -> String {
    if forType == .title { return string.components(separatedBy: "*")[0] }
    if forType == .url { return string.components(separatedBy: "*")[1] }
    return ""
  }
  
  var body: some View {
    VStack {
      LazyVGrid(columns: columns) {
        ForEach(data, id: \.self) { i in
          CodeView(title: get(.title, i), height: 500, url: get(.url, i))
          .frame(minWidth: 300, idealWidth: 400, maxWidth: .infinity, alignment: .center)
        }
      }
    }
  }
}

我能够使用 r/SwiftUI 中的 this reference 让动态 LazyVGrid 工作。我无法将网格限制为两列;但是,通过对代码进行以下修改,我得到了动态网格:

struct DoubleLayout: View {
  ...

  private let size: CGFloat = 300  // min width
  
  private var columns: [GridItem] {
    return [
      .init(.adaptive(minimum: size, maximum: .infinity))
    ]
  }
  
  var body: some View {
      LazyVGrid(columns: columns) {
        ForEach(data, id: \.self) { i in
          CodeView(title: get(.title, i), height: 500, url: get(.url, i))
            .frame(minWidth: 300, idealWidth: 400, maxWidth: .infinity, alignment: .center)
        }
      }
    }
  }
}

我仍然对如何将布局限制为 2 列感兴趣。

GeometryReader 有几种方法可以做到这一点。这里我使用 .flexible() 网格项,但根据父视图的整体宽度改变列数:如果宽度小于 800px,则设置 1 列;如果宽度在 800 到 1200px 之间,则做 2 列;否则做 3 列。

var body: some View {
    GeometryReader { geometry in
        let gridItem = GridItem(.flexible())
        var columnCount: Int = {
            switch geometry.size.width {
            case 0..<800:
                return 1
            case 800..<1200:
                return 2
            default:
                return 3
            }
        }()
        
        LazyVGrid(columns: Array(repeating: gridItem,
                                 count: columnCount)) {
            ForEach(1..<5) { _ in
                Rectangle()
                    .fill(Color.gray)
                    .frame(height: 500)
            }
        }
    }
}