Skip to content

Instantly share code, notes, and snippets.

@Thomvis
Last active August 4, 2021 21:41
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Thomvis/8a78d49a662f708311e0440f1f43a204 to your computer and use it in GitHub Desktop.
Save Thomvis/8a78d49a662f708311e0440f1f43a204 to your computer and use it in GitHub Desktop.
Rough attempt at creating a container view that lays out its children in a circle #SwiftUI
struct ContentView: View {
@State var count: Int = 3
var body: some View {
return NavigationView {
VStack(spacing: 50) {
HStack {
Button(action: { self.count += 1 }) {
Text("Add")
}
Button(action: { self.count -= 1 }) {
Text("Remove")
}
}
Circular {
ForEach(0..<max(0, count)) { i in
Color.green
.frame(width: 30, height: 30)
.clipShape(Circle())
.overlay(Text("\(i)").font(.headline).foregroundColor(.white).allowsTightening(true))
.shadow(radius: 3)
}
}
}.navigationBarTitle(Text("Full Circle"))
}
}
}
struct Circular<Items: View>: View {
let items: [AnyView]
let radius: Double = 100
init<D, C0: View>(@ViewBuilder content: () -> Items) where Items == ForEach<D, C0> {
let fe = content()
self.items = fe.data.map { AnyView(fe.content($0.identifiedValue)) }
}
var body: some View {
return ZStack {
ForEach(0..<items.count) { idx in
self.items[idx].modifier(self.positionModifier(at: idx))
}
}
.background(Circle().foregroundColor(.gray).opacity(0.2))
.frame(width: Length(2*radius), height: Length(2*radius))
.animation(.fluidSpring())
}
func positionModifier(at index: Int) -> _PositionLayout {
let r = (2*Double.pi / Double(items.count)) * Double(index)
return _PositionLayout(position: CGPoint(x: sin(r)*radius+radius, y: cos(r)*radius+radius))
}
}
@mastermakrela
Copy link

Hey :)
Thank you for this - it really helped me :D

I also updated it for Swift 5.3, SwiftUI 2 (Xcode 12 Beta 4)

struct Circular<Items: View>: View {
    let items: [AnyView]
    let radius: Double = 100

    init<D : RandomAccessCollection, I : Hashable, C: View>(@ViewBuilder content: () -> Items) where Items == ForEach<D, I, C> {
        let fe = content() as ForEach<D, I, C>
        items = fe.data.map { AnyView(fe.content($0.self))}
    }

    var body: some View {
        return ZStack {
            ForEach(0..<items.count) { idx in
                self.items[idx].modifier(self.positionModifier(at: idx))
            }
        }
        .background(Circle().foregroundColor(.gray).opacity(0.2))
        .frame(width: (2*CGFloat(radius)), height: (2*CGFloat(radius)))
        .animation(.interactiveSpring())
    }

    func positionModifier(at index: Int) -> _PositionLayout {
        let r = (2*Double.pi / Double(items.count)) * Double(index)
        return _PositionLayout(position: CGPoint(x: sin(r)*radius+radius, y: cos(r)*radius+radius))
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment