Skip to content

Instantly share code, notes, and snippets.

@raheelahmad
Last active February 27, 2022 06:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save raheelahmad/68a97876eb3dfeed44aaa4ef6a70b806 to your computer and use it in GitHub Desktop.
Save raheelahmad/68a97876eb3dfeed44aaa4ef6a70b806 to your computer and use it in GitHub Desktop.
// Basic idea from https://swiftui-lab.com/communicating-with-the-view-tree-part-1/
import SwiftUI
// --- Provides support for a child to propogate its frame
struct ChildPreferenceData: Equatable {
let idx: Int
let bounds: CGRect
}
struct ChildPreferenceKey: PreferenceKey {
static var defaultValue: [ChildPreferenceData] = []
static func reduce(value: inout Value, nextValue: () -> Value) {
value.append(contentsOf: nextValue())
}
}
struct ChildView: View {
let label: String
let idx: Int
@Environment(\.childSelection) var selection
var body: some View {
Text(label)
.font(.caption)
.padding(10)
.background(
// read in the background and "set" the frame in the environment
// If environment sends information down to children, preference sends information up to parent
GeometryReader { proxy in
Color.clear
.preference(
key: ChildPreferenceKey.self,
value: [ChildPreferenceData(idx: idx,
bounds: proxy.frame(in: .named("Calendar")))]
)
}
)
}
}
struct Month: Identifiable {
var id: Int { idx }
let idx: Int
let text: String
}
struct ContentView: View {
let months = ["January", "Februrary", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"].enumerated()
.map {
Month(idx: $0, text: $1)
}
@State var selection = 0
@State var frames: [Int: CGRect] = [:]
var monthArray: [[Month]] {
months.reduce([[Month]].init(repeating: [], count: 3)) { partialResult, month in
var result = partialResult
let index = month.idx / 4
var array = result[index]
array.append(month)
result[index] = array
return result
}
}
var body: some View {
ZStack(alignment: .topLeading) {
VStack(spacing: 20) {
ForEach(Array(monthArray.enumerated()), id: \.0) { array in
HStack(spacing: 10) {
ForEach(array.element) { month in
ChildView(label: month.text, idx: month.idx)
.onTapGesture {
selection = month.idx
}
}
}
}
}
RoundedRectangle(cornerRadius: 6, style: .continuous)
.stroke(lineWidth: 3)
.foregroundColor(.green)
.frame(width: frames[selection]?.width, height: frames[selection]?.height)
.offset(x: frames[selection]?.minX ?? 0, y: frames[selection]?.minY ?? 0)
.animation(.easeInOut(duration: 0.2), value: frames[selection])
}.onPreferenceChange(ChildPreferenceKey.self) { values in
for value in values {
frames[value.idx] = value.bounds
}
}
.coordinateSpace(name: "Calendar")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.previewLayout(.fixed(width: 500, height: 340))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment