Reputation: 32143
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
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()
}
}
}
Upvotes: 0
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)
Upvotes: 10
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)
}
}
}
Upvotes: 0
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
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