GridLayout
Last updated
Was this helpful?
Last updated
Was this helpful?
協助 🌅 Grid 計算最佳的佈局方式:
當收到來自母視件 (通常是 ) 所提議的佈局大小 (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.
- objc.io