如何使用 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)
}
}
}
}
我目前正在开发一个应用程序,它在一个列中显示不同 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)
}
}
}
}