Ky -
Ky -

Reputation: 32143

How can I create a custom view that only displays a grid of lines?

I'm making a test app to see what it's like to use SwiftUI, and I want my test app to have a custom view which is a grid of perpendicular lines, with spacing specified as state variables.

However, I can't figure out how to do this in a UI system that seems to have no custom drawing methods.

import SwiftUI



struct GridBackgroundView : View {

    @State var horizontalSpacing: CGFloat = 48
    @State var verticalSpacing: CGFloat = 48
    @State var anchor: Anchor<CGPoint>.Source = .center


    var numberOfHorizontalGridLines: UInt {
        return // Something?
    }

    var numberOfVerticalGridLines: UInt {
        return // Something?
    }


    var body: some View {
        Group {
            ForEach(0 ... numberOfHorizontalGridLines) { _ in
                // Something?
            }
            ForEach(0 ... numberOfVerticalGridLines) { _ in
                // Something?
            }
        }
    }
}

#if DEBUG
struct GridView_Previews : PreviewProvider {
    static var previews: some View {
        GridBackgroundView()
    }
}
#endif

I don't know what to put in the // Something? areas. There's no line view built into SwiftUI, and I can't for the life of me find out what the width of the view is (possibly because that's not part of a View in SwiftUI?)

Upvotes: 3

Views: 4717

Answers (5)

Andrew
Andrew

Reputation: 11427

Draws 3x3 cell grid like in videocameras:

struct VideoGridView : View {
    let gridCells = 3
    
    var body: some View {
        GeometryReader { geometry in
            Path { path in
                for index in 1...(gridCells-1) {
                    let vOffset: CGFloat = geometry.size.width / CGFloat(gridCells) * CGFloat(index)
                    path.move(to: CGPoint(x: vOffset, y: 0))
                    path.addLine(to: CGPoint(x: vOffset, y: geometry.size.height))
                }
                for index in 1...(gridCells-1) {
                    let hOffset: CGFloat = geometry.size.height / CGFloat(gridCells) * CGFloat(index)
                    path.move(to: CGPoint(x: 0, y: hOffset))
                    path.addLine(to: CGPoint(x: geometry.size.width, y: hOffset))
                }
            }
            .stroke()
        }
    }
}

enter image description here

Upvotes: 0

Rabel Ahmed
Rabel Ahmed

Reputation: 1411

In order to draw grid view with SwiftUI using Path Drawing Paths and Shapes

struct GridView: View {

    let rows: CGFloat
    let cols: CGFloat
    let gridColor: Color

    var body: some View {
    
        GeometryReader { geometry in
        
            let width = geometry.size.width
            let height = geometry.size.height
            let xSpacing = width / cols
            let ySpacing = height / rows
        
            Path { path in
            
                for index in 0...Int(cols) {
                    let vOffset: CGFloat = CGFloat(index) * xSpacing
                    path.move(to: CGPoint(x: vOffset, y: 0))
                    path.addLine(to: CGPoint(x: vOffset, y: height))
                }
                for index in 0...Int(rows) {
                    let hOffset: CGFloat = CGFloat(index) * ySpacing
                    path.move(to: CGPoint(x: 0, y: hOffset))
                    path.addLine(to: CGPoint(x: width, y: hOffset))
                }
            }
            .stroke(gridColor)
        }
    }
}

GridView(rows: 9.0, cols: 9.0, gridColor: .white)

Output Grid View

Upvotes: 10

MASHED TRACKS
MASHED TRACKS

Reputation: 150

This can be achieved with a ZStack which allows you to render the views inside it on top of each other.

     ZStack {
        HStack(spacing: horizontalSpacing) {
            ForEach(0 ..< Int(numberOfVerticalGridLines)) { _ in
                Rectangle().fill(Color.gray).frame(width: 1)
            }
        }
        VStack(spacing: verticalSpacing) {
            ForEach(0 ..< Int(numberOfHorizontalGridLines)) { _ in
                Rectangle().fill(Color.gray).frame(height: 1)
            }
        }
    }

This is what the code would generate

Upvotes: 0

Marcio
Marcio

Reputation: 2117

You can do custom drawing with SwiftUI using Path (Path Documentation, Tutorial)

To draw a grid you can use something like the following:

struct ContentView : View {
    var horizontalSpacing: CGFloat = 48
    var verticalSpacing: CGFloat = 48

    var body: some View {
        GeometryReader { geometry in
            Path { path in
                let numberOfHorizontalGridLines = Int(geometry.size.height / self.verticalSpacing)
                let numberOfVerticalGridLines = Int(geometry.size.width / self.horizontalSpacing)
                for index in 0...numberOfVerticalGridLines {
                    let vOffset: CGFloat = CGFloat(index) * self.horizontalSpacing
                    path.move(to: CGPoint(x: vOffset, y: 0))
                    path.addLine(to: CGPoint(x: vOffset, y: geometry.size.height))
                }
                for index in 0...numberOfHorizontalGridLines {
                    let hOffset: CGFloat = CGFloat(index) * self.verticalSpacing
                    path.move(to: CGPoint(x: 0, y: hOffset))
                    path.addLine(to: CGPoint(x: geometry.size.width, y: hOffset))
                }
            }
            .stroke()
        }
    }
}

Upvotes: 12

Argas
Argas

Reputation: 1537

You can do it by combination Spacers and Dividers

   var body: some View {
           ZStack {
               HStack {
                   Spacer()
                   Rectangle().frame(width: 1)
                   Spacer()
                   Divider()
                   Spacer()
               }
               VStack {
                   Spacer()
                   Divider()
                   Spacer()
                   Divider()
                   Spacer()
               }
           }
       }

You can also use Rectangle() instead of Divider() and look at https://developer.apple.com/tutorials/swiftui/drawing-paths-and-shapes

upd: if you need a grid with a fixed size cell, you can use GeometryReader to calculate your count of lines, and organise your ForEach correspondingly

    var cellSize: CGFloat = 48
    var body: some View {
        GeometryReader { geometry in
            ZStack {
                HStack {
                    ForEach(0..<Int(geometry.size.width / self.cellSize)) { _ in
                        Spacer()
                        Divider()
                    }
                    Spacer().frame(width: geometry.size.width.truncatingRemainder(dividingBy: self.cellSize))
                }
                VStack {
                    ForEach(0..<Int(geometry.size.height / self.cellSize)) { _ in
                        Spacer()
                        Divider()
                    }
                    Spacer().frame(width: geometry.size.height.truncatingRemainder(dividingBy: self.cellSize))
                }
            }
        }
    }

Upvotes: 5

Related Questions