每个选项卡包含网格,如何填充每个网格

With each tab containing grid, how to fill each grid

我想要一个 TabView,其中每个选项卡都是一个 LazyVGrid。我想在创建另一个选项卡之前填充特定选项卡中的每个网格,并且我希望它能够适应所看到的任何屏幕(特别是 iPad 与 iPhone)。

我有类似下面的内容:

struct ContentView: View {
    var items: Items
    
    var columns = [GridItem(.adaptive(minimum: 100))]
    
    var body: some View {
        VStack {
            TabView {
                ForEach((1...items.getNumPages(???)), id: \.self) {page in
                    VStack {
                        LazyVGrid(columns: columns, alignment: .leading, spacing: 10) {
                            ForEach(items.getItems(page: page, ???) { item in
                                ItemView(item: item)
                            }.tabItem{}.tag(page)
                        }.padding(.leading).padding(.trailing)
                    }
                }
            }.tabViewStyle(.page)
            .indexViewStyle(.page(backgroundDisplayMode: .always))
        }
    }
}

请注意,对于任何项目,ItemView 的大小始终相同。但是根据屏幕大小,您可能有一个包含 2 列和 40 个元素的视图,或者 3 列包含 50 个元素的视图,等等。在上面我输入了 ???传递一些会告诉我的东西:1)有多少标签(页面),2)每个标签(页面)有多少元素。我根本不需要任何滚动,只需要从一个选项卡移动到另一个选项卡的滑动行为。我想我可以加入一个 GeometryReader 并进行一系列大小计算来计算出每页适合多少项目,但是有没有更简单的方法?

谢谢。

我想到了这样的东西,我唯一要做的改变就是计算 Items struct init () 上所有与分页相关的东西,如果你已经知道会有多少项目的话。

因为我不知道网格内项目的大小,所以我只是在每页中放置一个任意数字

import SwiftUI

struct Items{
    let quantity: Int
    
    var pager: Int = 0
    var column = GridItem(.flexible(minimum: 100))
    
    func getNumPages() -> Int{
        switch UIDevice.current.userInterfaceIdiom {
        case .phone:
            return Int(ceil(Double(self.quantity / 50)))
        case .pad:
            return Int(ceil(Double(self.quantity / 100)))
            
        @unknown default:
            return Int(ceil(Double(self.quantity / 100)))
        }
    }
    
    func getColums() -> [GridItem] {
        
        switch UIDevice.current.userInterfaceIdiom {
        case .phone:
            return [column, column]
        case .pad:
            return [column, column, column]
            
        @unknown default:
            return [column, column, column]
        }
    }
    
    mutating func getItems(page: Int) -> Int {
        pager = page + (UIDevice.current.userInterfaceIdiom == .pad ? 100 : 50)
        return pager
    }
    
}


struct gridProblem: View {
    @State var items = Items(quantity: 500)
    
    var body: some View {
        VStack {
            TabView {
                ForEach((0...items.getNumPages()), id: \.self) {page in
                    VStack {
                        LazyVGrid(columns: items.getColums(), alignment: .leading, spacing: 10) {
                            ForEach(items.pager..<items.getItems(page: Int(page))) { item in
                                HStack{
                                    Spacer()
                                    
                                    Text("\(item + self.items.pager)")
                                    
                                    Spacer()
                                }.background(Color.gray)
                                
                            }
                        }.padding(.leading).padding(.trailing)
                    }
                    .id(UUID())
                    .tabItem{
                        
                    }.tag(page)
                }
            }.tabViewStyle(.page)
                .indexViewStyle(.page(backgroundDisplayMode: .always))
        }
    }
    
}

解决方案是使用 GeometryReader,您必须计算尺寸。还不错,关键概念是计算每列的项目数,以及根据 GeometryReader 大小计算每页的列数。绝对假定项目是固定大小的。

在我的示例中,项目只是随机生成的单词。以下适用于各种 ipad 和手机,并且在旋转设备时效果很好。



import SwiftUI

struct Items {
    private let alphabet = "abcdefhijklmnopqrstuvwxyz"
    private var alphaArray: [String] { return alphabet.map { String([=10=]) }}
    var items: [String] = []
    
    static let column = GridItem(.fixed(itemWidth))
    
    static let verticalSpacing: CGFloat = 10
    static let itemHeight: CGFloat = 20
    static let itemWidth: CGFloat = 160
    static let horizontalSpacing: CGFloat = 20
    
    init(quantity: Int) {
        for _ in 1...quantity {
            let nchars = Int.random(in: 4..<16)
            let tempArray = alphaArray.shuffled()
            items.append( tempArray[0...nchars].joined() )
        }
        items.sort()
    }
    private func wordsPerColumn(height: CGFloat) -> Int {
        var retval = Int( (height - Items.verticalSpacing) / (Items.itemHeight + Items.verticalSpacing))
        // Let's remove 1, keep it roomy below.
        retval -= 1
        return retval
    }
    private func columnsPerPage(width: CGFloat) -> Int {
        return Int( width / (Items.itemWidth + Items.horizontalSpacing))
    }
                
    func getPageSize(size: CGSize) -> Int {
        return wordsPerColumn(height: size.height) * columnsPerPage(width: size.width)
    }
    
    func getNumPages(size: CGSize) -> Int {
        var retval = items.count / getPageSize(size: size) + 1
        if items.count % getPageSize(size: size) == 0 {
            retval = retval - 1
        }
        return retval
    }
    
    func getColumns(size: CGSize) -> [GridItem] {
        return [GridItem](repeating: Items.column, count: columnsPerPage(width: size.width))
    }
    
    func getItems(size: CGSize, page: Int) -> [String] {
        let pageSize = getPageSize(size: size)
        let startIdx = (page-1) * pageSize
        let endIdx = min(startIdx + pageSize-1, items.count-1)
        return Array(items[startIdx...endIdx])
    }
}
struct ItemView: View {
    var item: String
    
    var body: some View {
        Text(item.capitalized).frame(width: Items.itemWidth, height: Items.itemHeight, alignment: .leading).background(.orange)
    }
}
struct ContentView: View {
    @State var items = Items(quantity: 210)
    
    var body: some View {
        VStack {
            Text("TOP")
            GeometryReader { reader in
                TabView {
                    ForEach((1...items.getNumPages(size: reader.size)), id: \.self) {page in
                        VStack {
                            LazyVGrid(columns: items.getColumns(size: reader.size), alignment: .leading, spacing: Items.verticalSpacing) {
                                ForEach(items.getItems(size: reader.size, page: page), id: \.self) { item in
                                    ItemView(item: item)
                                }.background(Color.gray)
                                
                            }.padding().border(.red, width: 3)
                            Spacer()
                        }
                        .id(UUID())
                        .tabItem{
                            
                        }.tag(page)
                    }
                }.tabViewStyle(.page)
                    .indexViewStyle(.page(backgroundDisplayMode: .always))
            }
            Spacer()
        }
    }
}