I want to spread the card set around, but I can't make it into that shape even if I try it. I need help.
GPT says to use sin and cos with it, but I'm not familiar with sin and cos and SwiftUI.
var body: some View {
HStack(spacing: 10) {
Spacer()
ForEach(0..<10) { num in
VStack {
GeometryReader { geo in
ZStack {
CardFront(width: width, height: height, num: num, degree: $frontDegrees[num])
CardBack(width: width, height: height, degree: $backDegrees[num])
}.onTapGesture {
flipCard(num)
}
// spread the cards based on the center
.offset(x: ((Double(num) + 0.6) * 90), y: 600).rotationEffect(.degrees(Double(num) * 6))
}
}
Spacer()
}
.navigationBarBackButtonHidden(true)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
CardFlipView()
CardFlipView()
.previewInterfaceOrientation(.landscapeLeft)
}
}
How it should look:
I had a go at getting an example to work. Tap the cards to toggle the fanned state:
import SwiftUI
/// Encapsulates the layout logic for the cards
class Layout {
/// The width available for showing the fanned cards
let viewWidth: CGFloat
/// The number of cards in the pack
static let nCardsInPack = 10
/// The width of a single card
static let cardWidth = CGFloat(50)
/// The height of a single card
static let cardHeight = CGFloat(cardWidth * 1.4)
/// The number of degrees for the arc of the spread.
private static let arcDegrees = CGFloat(40)
/// The number of radians for the arc of the spread.
/// NB: 180 degrees = Pi radians
private static let arcRadians = (arcDegrees * CGFloat.pi) / 180
/// Creates the layout for showing the fanned cards, dependent on the supplied view width
init(viewWidth: CGFloat) {
self.viewWidth = viewWidth
}
/// - Returns the angle for the specified card when the pack is shown fanned
func angleForCard(n: Int) -> CGFloat {
let nGaps = max(CGFloat(Layout.nCardsInPack) - 1, 1)
let fraction = (CGFloat(n) - (nGaps / 2)) / nGaps
return fraction * Layout.arcRadians
}
/// - Returns the radius of the circle on which the fanned cards are
/// spread out. Computed from the view width and arc for the spread
private lazy var fanRadius: CGFloat = {
let sinAngle = sin(Layout.arcRadians / 2.0)
let availableWidth = (viewWidth - Layout.cardWidth) / 2.0
return sinAngle == 0 ? availableWidth : availableWidth / sinAngle
}()
/// - Returns the x offset for card n, which may be negative
func xOffsetForCard(n: Int) -> CGFloat {
return sin(angleForCard(n: n)) * fanRadius
}
/// - Returns the y offset for card n
func yOffsetForCard(n: Int) -> CGFloat {
return fanRadius - (cos(angleForCard(n: n)) * fanRadius)
}
}
/// View of an individual card
struct CardView: View {
/// The index of this card in the pack, 0-based
private let n: Int
private let layout: Layout
@Binding private var showSpreadOut: Bool
init(n: Int, layout: Layout, showSpreadOut: Binding<Bool>) {
self.n = n
self.layout = layout
self._showSpreadOut = showSpreadOut
}
private var angle: CGFloat {
showSpreadOut ? layout.angleForCard(n: n) : 0
}
private var xOffset: CGFloat {
showSpreadOut ? layout.xOffsetForCard(n: n) : 0
}
private var yOffset: CGFloat {
showSpreadOut ? layout.yOffsetForCard(n: n) : 0
}
var body: some View {
Text("\(n)")
.font(.largeTitle)
.foregroundColor(.white)
.frame(width: Layout.cardWidth, height: Layout.cardHeight)
.background(
Color.blue
.opacity(0.2)
.cornerRadius(Layout.cardWidth / 10)
)
// Important: rotation must come before offsets!
.rotationEffect(Angle(radians: angle))
.offset(x: xOffset, y: yOffset)
.animation(.easeInOut, value: showSpreadOut)
}
}
/// View of a collection of cards
struct PackView: View {
private let layout: Layout
@Binding private var showSpreadOut: Bool
init(layout: Layout, showSpreadOut: Binding<Bool>) {
self.layout = layout
self._showSpreadOut = showSpreadOut
}
var body: some View {
ZStack() {
ForEach(0..<Layout.nCardsInPack, id: \.self) { n in
CardView(n: n, layout: layout, showSpreadOut: $showSpreadOut)
}
}
}
}
struct ContentView: View {
@State private var showSpreadOut = false
var body: some View {
GeometryReader { proxy in
PackView(
layout: Layout(viewWidth: proxy.size.width),
showSpreadOut: $showSpreadOut
)
.onTapGesture { showSpreadOut.toggle() }
.frame(width: proxy.size.width, height: proxy.size.height)
}
}
}