'Visualize Table in SwiftUI

How can I visualize this data as table in SwiftUI:

I have an invoice object with pages and tables and cells.

The cells do have information about rowIndex and columnIndex:

struct Cell: Codable, Hashable, Identifiable {
    let id: UUID = UUID()
    let rowIndex, columnIndex: Int
    let text: String
    let boundingBox: [Double]
    let isHeader: Bool?
}

Currently I have no better idea then this do get a "new" row:

if !invoice.invoice.isEmpty {
    ScrollView{
        HStack {
            ForEach (invoice.invoice) {invoice in
                ForEach (invoice.analyzeResult.pageResults) {pageresult in
                    ForEach (pageresult.tables) {table in
                        ForEach (table.cells) {cell in
                            if (cell.rowIndex == 0) {
                                Text(cell.text)
                            }
                        }
                    }
                }
            }
        }
        HStack {
            ForEach (invoice.invoice) {invoice in
                ForEach (invoice.analyzeResult.pageResults) {pageresult in
                    ForEach (pageresult.tables) {table in
                        ForEach (table.cells) {cell in
                            if (cell.rowIndex == 1) {
                                Text(cell.text)
                            }
                        }
                    }
                }
            }
        }
    }
}

Any idea how to solve this?

Thanks a lot



Solution 1:[1]

You can use either a combination of VStack and HStack or a LazyVGrid, the latter seems more appropriate. In both cases you have to find the nr of columns (and rows for 1. case).

Here is an example with both approaches:

// create dummy data
let tableData: [Cell] = {
    var result = [Cell]()
    for row in 0..<10 {
        for col in 0..<5 {
            result.append(Cell(rowIndex: row, columnIndex: col, text: "cell \(row), \(col)", boundingBox: [], isHeader: nil))
        }
    }
    return result
}()


struct ContentView: View {
    // find row & col nrs
    let rowNr = tableData.max(by: {$0.rowIndex < $1.rowIndex})?.rowIndex ?? 0
    let colNr = tableData.max(by: {$0.columnIndex < $1.columnIndex})?.columnIndex ?? 0
    
    var body: some View {
        
        VStack {
            ScrollView {
                
                // Option 1: VStack and HStack
                VStack {
                    ForEach(0..<rowNr+1) { row in
                        HStack {
                            ForEach(0..<colNr+1) { col in
                                // find cell with row + col numbers
                                if let cell = tableData.first(where: { $0.rowIndex == row && $0.columnIndex == col }) {
                                    Text(cell.text)
                                }
                            }
                        }
                    }
                }
                
                Divider()
                
                // Option 2: LazyVGrid View
                let columns = [GridItem].init(repeating: GridItem(.flexible(minimum: 10)), count: colNr+1)
                    
                LazyVGrid(columns: columns) {
                    ForEach(tableData
                        .sorted(by: {$0.columnIndex < $1.columnIndex})
                        .sorted(by: {$0.rowIndex < $1.rowIndex}))
                    { cell in
                        Text(cell.text)
                    }
                }
            }
        }
    }
}

EDIT: As @loremipsum pointed out there might be the case, that there is no value for every combination of row/columns. In this case you could map the data to an array first, filling the empty data points:

    func dataMappedToArray() -> [Cell] {
        
        let rowNr = tableData.reduce(0, { max($0, $1.rowIndex) })
        let colNr = tableData.reduce(0, { max($0, $1.columnIndex) })

        var result = [Cell]()
        for row in 0...rowNr {
            for col in 0...colNr {
                if let cell = tableData.first(where: { $0.rowIndex == row && $0.columnIndex == col }) {
                    result.append(cell)
                } else {
                    // "empty" cell
                    result.append(Cell(rowIndex: row, columnIndex: col, text: "–", boundingBox: [], isHeader: nil) )
                }
            }
        }
        return result
    }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1