Skip to content

Instantly share code, notes, and snippets.

@kieranb662
Created July 24, 2021 00:46
Show Gist options
  • Save kieranb662/bd439fc1bd43ab560f6885e56067efd4 to your computer and use it in GitHub Desktop.
Save kieranb662/bd439fc1bd43ab560f6885e56067efd4 to your computer and use it in GitHub Desktop.
Animated Grid that can collapse from any number of rows and columns to a 2x2 square. Made With SwiftUI.
// Swift toolchain version 5.0
// Running macOS version 12.0
// Created on 6/24/21.
//
// Author: Kieran Brown
//
import SwiftUI
struct CollapsibleGridLines: View {
var thickness: Double
var columns: Double
var rows: Double
var collapsedSize: CGFloat?
init?(columns: Int = 7, rows: Int = 5, collapsedSize: CGFloat? = nil, lineWidth: Double = 1.0) {
guard columns > 1, rows > 1 else {
return nil
}
self.columns = Double(columns)
self.rows = Double(rows)
self.thickness = lineWidth
self.collapsedSize = collapsedSize
for i in 0...columns {
lines.append(Line(number: i, axis: .vertical))
}
for i in 0...rows {
lines.append( Line(number: i, axis: .horizontal))
}
}
@State var collapsed = true
var lines: [Line] = []
var body: some View {
let size = collapsed ? collapsedSize : nil
ZStack {
GeometryReader { geometry in
ZStack {
ForEach(lines) { line in
line.makeLine(proxy: geometry,
isCollapsed: collapsed,
columns: columns,
rows: rows,
thickness: thickness)
}
}
}
}
.frame(width: size, height: size)
.contentShape(Rectangle())
.onTapGesture {
withAnimation(.spring()) {
collapsed.toggle()
}
}
}
struct Line: Identifiable {
var number: Int
var axis: Axis
var id = UUID()
enum Axis: Equatable {
case horizontal, vertical
}
func calculateMagicNumber(isCollapsed: Bool, columns: Double, rows: Double) -> CGFloat {
guard isCollapsed else {
return CGFloat(number)
}
let numberOfLines = axis == .vertical ? columns : rows
let middleLine = Int(0.5 * numberOfLines)
switch number {
case ..<middleLine: // before the middle line.
return 0
case middleLine: // is the middle line.
return 1
case middleLine...: // after the middle line.
return 2
default: return 0
}
}
func position(
width: CGFloat, height: CGFloat, isCollapsed: Bool, columns: Double, rows: Double
) -> CGPoint {
let countRatio = rows / columns
let number = calculateMagicNumber(isCollapsed: isCollapsed, columns: columns, rows: rows)
var xMultiplier: CGFloat
var yMultiplier: CGFloat
switch (axis, isCollapsed) {
case (.vertical, true):
xMultiplier = number * 0.5
yMultiplier = 0.5
case (.horizontal, true):
xMultiplier = 0.5
yMultiplier = number * 0.5
case (.vertical, false):
xMultiplier = number / columns
yMultiplier = 0.5 * countRatio
case (.horizontal, false):
xMultiplier = 0.5
yMultiplier = number / columns
}
return CGPoint(x: xMultiplier * width, y: yMultiplier * width)
}
func makeLine(
proxy geometry: GeometryProxy, isCollapsed: Bool, columns: Double, rows: Double, thickness: CGFloat
) -> some View {
let countRatio = isCollapsed ? 1 : rows / columns
let width = .vertical == axis ? thickness : nil
let height = .vertical == axis
? geometry.size.width * countRatio
: thickness
return Rectangle()
.frame(width: width, height: height)
.position(position(width: geometry.size.width,
height: isCollapsed ? geometry.size.width : geometry.size.height,
isCollapsed: isCollapsed,
columns: columns,
rows: rows))
}
}
}
// MARK: - Editor for playing around
struct GridEditor: View {
@State var columns = 20
@State var rows = 15
@State var collapsedSize = 50
var body: some View {
VStack {
CollapsibleGridLines(columns: columns, rows: rows, collapsedSize: CGFloat(collapsedSize))
Spacer()
Form {
TextFieldLabel("Rows", value: $rows)
TextFieldLabel("Columns", value: $columns)
TextFieldLabel("Size When Collapsed", value: $collapsedSize)
}.frame(height: 250)
}
}
func TextFieldLabel(_ placeholder: String, value: Binding<Int>) -> some View {
HStack {
Text(placeholder)
TextField(placeholder, value: value, format: .number)
}
}
}
struct CollapsibleGridLines_Previews: PreviewProvider {
static var previews: some View {
GridEditor()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment