|
import SwiftUI |
|
|
|
struct ContentView: View { |
|
@State var isExpanded = [true] + Array(repeating: false, count: 10) |
|
|
|
var body: some View { |
|
ZStack { |
|
ForEach(1..<isExpanded.count, id: \.self) { offset in |
|
CircleView( |
|
color: .color(offset: offset), |
|
isExpanded: $isExpanded[offset], |
|
isExpandedPrevious: isExpanded[offset-1] |
|
) |
|
} |
|
} |
|
.ignoresSafeArea() |
|
} |
|
} |
|
|
|
struct CircleView: View { |
|
var color: Color |
|
@Binding var isExpanded: Bool |
|
var isExpandedPrevious: Bool |
|
|
|
var body: some View { |
|
GeometryReader { geometry in |
|
let width = geometry.size.width |
|
let height = geometry.size.height |
|
let diagonal = sqrt(width * width + height * height) |
|
let diameter = isExpanded ? diagonal : (min(width, height) / 2) |
|
ZStack { |
|
Circle() |
|
.foregroundStyle(color) |
|
.frame(width: diameter, height: diameter) |
|
} |
|
.offset( |
|
x: isExpanded ? -(diagonal - width)/2 : 0.2 * diameter, |
|
y: isExpanded ? -(diagonal - height)/2 : 0.1 * diameter |
|
) |
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomTrailing) |
|
.scaleEffect(isExpandedPrevious ? 1 : 0, anchor: .bottomTrailing) |
|
.offset(x: isExpandedPrevious ? 0 : diameter) |
|
.animation(animation, value: isExpandedPrevious) |
|
} |
|
.animation(animation, value: isExpanded) |
|
.onTapGesture { |
|
isExpanded.toggle() |
|
} |
|
} |
|
|
|
var animation: Animation { |
|
return .interpolatingSpring(duration: 0.9, bounce: 0.3) |
|
} |
|
} |
|
|
|
extension Color { |
|
static func color(offset: Int) -> Self { |
|
let hue = Double((111 * offset) % 360) / 360.0 |
|
return Color(hue: hue, saturation: 0.4, brightness: 0.96) |
|
} |
|
} |
|
|
|
#Preview { |
|
ContentView() |
|
} |