โจTestLittleSquares
โฌ๏ธ ้่ฆ๏ผ VGridForEach, .dimension(), .shadowedBorder(), .clamped(in:)
// 2022.02.14
import SwiftUI
// ----------------------------
// ๐ TestLittleSquares
// ----------------------------
struct TestLittleSquares: View {
// view state
@State private var width: CGFloat = 300 // offered width
var body: some View {
VStack {
controls // slider
.padding(.horizontal)
.padding(.bottom)
LittleSquaresView() // ๐ LittleSquaresView
.frame(width: width, height: 200) // โญ๏ธ parent's offered width
.dimension(.top, arrow: .blue) // ๐View+.dimension()
.shadowedBorder() // ๐View+.shadowedBorder()
}
}
}
extension TestLittleSquares {
/// view controls
var controls: some View {
VStack(spacing: 20) {
// ๐ `Slider` to control the offered width
Slider(value: $width, in: 200...500, step: 1)
.frame(width: 300)
// label for offered width
Text("offered width: \(Int(width))")
.font(.system(.caption, design: .monospaced))
.foregroundColor(.secondary)
}
.padding()
}
}
struct TestLittleSquares_Previews: PreviewProvider {
static var previews: some View {
TestLittleSquares()
}
}
// ----------------------------
// ๐ LittleSquaresView
// ----------------------------
struct LittleSquaresView: View {
var count: Int = 25 // squares count
var size: CGFloat = 50 // square size
var spacing: CGFloat = 5 // spacing between squares
var adaptiveCols: [GridItem] {
[GridItem(
.adaptive(minimum: size, maximum: size),
spacing: spacing // spacing between cols
)]
}
var body: some View {
GeometryReader { geo in
// ๐ VGridForEach = LazyVGrid + ForEach
VGridForEach(
0 ..< count,
columns: adaptiveCols, // adaptive columns
spacing: spacing // spacing between rows
) { i in
(
i < maxItemCount(in: geo.size)// max item count inside frame
? Color.yellow // inside frame
: Color.pink.opacity(0.7) // outside frame
)
.frame(self.size) // ๐View+.frame()
.overlay {
Text("\(i+1)").font(.caption).foregroundColor(.black)
}
}
}// end: GeometryReader
}
}
extension LittleSquaresView {
/// calculate how many columns can fit in the offered size.
func cols(in size: CGSize) -> Int {
Int(((size.width + spacing) / (self.size + spacing)))
.clamped(in: 0...count)
}
/// max rows that can fit into the offered size.
func maxRows(in size: CGSize) -> Int {
Int(((size.height + spacing) / (self.size + spacing)))
.clamped(in: 0...)
}
/// max item count that can fit into the offered size.
func maxItemCount(in size: CGSize) -> Int {
(cols(in: size) * maxRows(in: size)).clamped(in: 0...count)
}
}
Last updated