๐ฆGridLayout
ๅๅฉ ๐ Grid ่จ็ฎๆไฝณ็ไฝๅฑๆนๅผ๏ผ
็ถๆถๅฐไพ่ชๆฏ่ฆไปถ (้ๅธธๆฏ GeometryReader) ๆๆ่ญฐ็ไฝๅฑๅคงๅฐ (proposed size) ๆ๏ผGridLayout ๆๆ นๆไฝฟ็จ่ ๆๅธๆ็ๆ ผๅญๆฏไพ (cellAspectRatio)๏ผ่ชๅ่จ็ฎๅบๆไฝณ็่กๆธ (cols) ่ๅๆธ (rows)๏ผไธฆไธๅฏไปฅๆไพๆ ผๅญ็ๅคงๅฐ (cellSize) ่ๆฏๅๆ ผๅญ็ไธญๅฟ้ปไฝ็ฝฎ (centerOfCell(at: index))ใ
import SwiftUI
import Extensions // for ๐CGSize+aspectRatio, ๐Int+cgfloat
// ๐ฆ GridLayout
public struct GridLayout {
let size: CGSize // size proposed by parent view
private(set) var rows: Int = 0 // best layout in rows x cols
private(set) var cols: Int = 0 // (to be calculated in init)
public init(
itemCount n: Int, // number of items
in size: CGSize, // proposed size
nearAspectRatio r: CGFloat = 1 // desired cell aspect ratio
) {
self.size = size
// if zero width or height or itemCount is not > 0,
// do nothing.
guard size.width != 0, size.height != 0, n > 0 else { return }
// find the bestLayout
// which results in cells whose aspectRatio
// has the smallestVariance from desiredAspectRatio
var bestLayout: (rows: Int, cols: Int) = (1, n)
var smallestVariance: CGFloat?
// start to find
for rows in 1...n {
// calculate how many columns we need
let cols = (n / rows) + (n % rows > 0 ? 1 : 0)
// cellAspectRatio = (width/cols) : (height/rows)
// = size.aspectRatio * rows/cols
// -----------------------------------------------------------
// โญ๏ธ because rows is strictly increasing, cols is decreasing,
// cellAspectRatio will be strictly increasing.
// -----------------------------------------------------------
// - size.aspectRatio: ๐CGSize + aspectRatio
// - row.cgfloat : ๐Int + cgfloat
let cellAspectRatio = size.aspectRatio * (rows.cgfloat/cols.cgfloat)
// current variance with desired aspect ratio
let variance = self.variance(cellAspectRatio, r)
// if variance is not set, or getting smaller,
// set new smallestVariance and new bestLayout
if smallestVariance == nil || variance < smallestVariance! {
smallestVariance = variance
bestLayout = (rows: rows, cols: cols)
} else {
// smallVariance is set, and variance is getting larger
break // break for loop
}
}
// record best layout found
rows = bestLayout.rows
cols = bestLayout.cols
}
/* ------- Public Methods ------- */
// cell size
public var cellSize: CGSize {
(rows == 0 || cols == 0) ? .zero :
CGSize(
width : size.width / CGFloat(cols),
height: size.height / CGFloat(rows)
)
}
// center point of cell at index
public func centerOfCell(at index: Int) -> CGPoint {
CGPoint(
x: (CGFloat(index % cols) + 0.5) * cellSize.width,
y: (CGFloat(index / cols) + 0.5) * cellSize.height
)
}
/* ------- Helper Methods ------- */
// variance between two aspect ratios
func variance(_ r1: CGFloat, _ r2: CGFloat) -> CGFloat {
precondition(r1 > 0 && r2 > 0)
// โญ๏ธ ๅๅฐๆธ่จ็ฎ๏ผๆไธๆ้ ๆ 1/3 = 0.33 ็ๆฏไพๆฏ 2/1 = 2 ็ๆฏไพๆดๆฅ่ฟ 1/1 = 1
return abs(log(r1) - log(r2))
}
}
Grid - help calculate best layout.
helps Grid to calculate the best layout.
SwiftUI's Grid Views - objc.io
Last updated