Created
July 24, 2021 00:46
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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