swiftuiheaderdiagonal

Trying to create diagonal column headers in SwiftUI


I am trying to create diagonal headers for a table I have created... The screenshot below shows the best I can come up with.

I have tried a number of approaches such as narrowing the width of the frames but I cannot get them to overlap. This could well be a limitation of SwiftUI... in this case I could accept 90 degree rotation - but even that does not work as expected. The View it creates is still too wide and the titles wrap for no apparant reason.

I can supply more examples and screenshots if required. I am just hoping that those more experienced than me will be able to quickly see what I am doing wrong.

var body: some View {
    let map = vm.createStampFeatureMap()
    let nRows = map.map.count
    if (nRows > 0) {
        let nCols = map.map[0].count
        if (nCols > 0) {
            let columns = createGrid( nCols: nCols, width: 10 )
            let rowHeadings = createGrid( nCols: 1, width: 200)
            
            // MARK: --- Show this View
            HStack {
                VStack {
                    // MARK: Layout spacer - do this more elegantly
                    LazyVGrid( columns: columns, alignment: .leading, spacing: 2) {
                        ForEach( (0...nCols-1), id: \.self) { i in
                            let colName = map.colHeadings[ i ]
                            // TODO: Create this space more elegantly
                            Text( colName ).foregroundColor(.blue)
                        }
                    }.frame(height: 150 )
                     .background( .blue )
                    // MARK: Row Headings
                    LazyVGrid( columns: rowHeadings, alignment: .trailing, spacing: 2 ){
                        ForEach( ( 0...nRows-1 ), id: \.self) { i in
                            let rowName = map.rowHeadings[ i ]
                            Text( rowName )//.padding([.leading])
                                .frame( height: 18 )
                        }
                    }.background( .green )
                }
              
                VStack {
                    // MARK: Column Headings
                    HStack ( spacing: 0 ){
                        ForEach( (0...nCols-1), id: \.self) { i in
                            let colName = Array(arrayLiteral: map.colHeadings[ i ])
                            ForEach ( 0...colName.count-1, id: \.self ) { i in
                                let s = colName[i]
                                Text( s ).rotationEffect(.degrees( -45 ))
                                    .background( .white )
                            }
                       }
                    }.frame(height: 150 )
                     .background( .blue )

                    // MARK: The Heatmap itself
                    LazyVGrid( columns: columns, alignment: .leading ) {
                        VStack ( spacing: 0 ) {
                            ForEach( 0...nRows-1, id: \.self ) { i in
                                HeatMapRow(map: map, row: i, nCols: nCols)
                            }
                        }
                    }.background( .yellow )
                }
            }.background( .cyan)
        }
    }
}

Screenshot


Solution

  • This is what worked for me. I hope others might find this useful. Excuse the magic numbers they just worked for my particular layout. A more elegant calculation would be better but the main point was to illustrate the ZStack part of the solution.

    HStack {
        ZStack (alignment: .bottomLeading){
            ForEach( (0...nCols-1), id: \.self) { i in
                let offset = i * 20
                    Text( map.colHeadings[ i ] )
                        .rotationEffect(.degrees( -45 ),anchor: UnitPoint(x: 0, y: 0) )
                        .offset(x:CGFloat(offset), y:75)
            }
        }
        .frame(height: 150 )
        .background( .blue )
        Spacer() // Need this to push the headings to the left
    }