Skip to content

Instantly share code, notes, and snippets.

@MaximKotliar
Last active February 20, 2023 16:15
Show Gist options
  • Save MaximKotliar/8098ef074f8c03ea907cda7a619dfe06 to your computer and use it in GitHub Desktop.
Save MaximKotliar/8098ef074f8c03ea907cda7a619dfe06 to your computer and use it in GitHub Desktop.
import UIKit
import PlaygroundSupport
import CoreGraphics
extension Numeric {
/// Linear interpolation
/// - Parameters:
/// - a: a value to interpolate from
/// - b: a value to interpolate to
/// - delta: interpolation delta value (0 - 1)
@inlinable static func lerp(from a: Self, to b: Self, delta: Self) -> Self {
a + delta * (b - a)
}
}
public extension CGAffineTransform {
@inlinable static func lerp(from a: CGAffineTransform, to b: CGAffineTransform, delta: CGFloat) -> CGAffineTransform {
return CGAffineTransform(tx: .lerp(from: a.tx, to: b.tx, delta: delta),
ty: .lerp(from: a.ty, to: b.ty, delta: delta),
sx: .lerp(from: a.scaleX, to: b.scaleX, delta: delta),
sy: .lerp(from: a.scaleY, to: b.scaleY, delta: delta),
rotation: .lerp(from: a.rotation, to: b.rotation, delta: delta))
}
}
public extension CGAffineTransform {
var scaleX: CGFloat { sqrt(a * a + c * c) }
var scaleY: CGFloat { sqrt(b * b + d * d) }
var rotation: CGFloat { atan2(b, a) }
/// - parameter tx: translation on x axis.
/// - parameter ty: translation on y axis.
/// - parameter sx: scale factor for width.
/// - parameter sy: scale factor for height.
/// - parameter deg: degrees.
init(tx: CGFloat, ty: CGFloat, sx: CGFloat, sy: CGFloat, rotation: CGFloat) {
let translationTransform = CGAffineTransform(translationX: tx, y: ty)
let scaleTransform = CGAffineTransform(scaleX: sx, y: sy)
let rotationTransform = CGAffineTransform(rotationAngle: rotation)
self = rotationTransform.concatenating(scaleTransform).concatenating(translationTransform)
}
}
PlaygroundPage.current.needsIndefiniteExecution = true
let main = UIView()
main.backgroundColor = .white
main.frame = CGRect(origin: .zero, size: CGSize(width: 400, height: 400))
class CheckerBoard: UIView {
let aColor = UIColor.black
let bColor = UIColor.systemPink
let dimension = 8
var squares: [[CALayer]] = [[]]
override init(frame: CGRect) {
super.init(frame: frame)
createSquares()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
createSquares()
}
private func createSquares() {
for x in 0..<dimension {
for y in 0..<dimension {
let xIsEven = x.isMultiple(of: 2)
let yIsEven = y.isMultiple(of: 2)
let color = xIsEven ? (yIsEven ? aColor : bColor) : (!yIsEven ? aColor : bColor)
let layer = CALayer()
layer.backgroundColor = color.cgColor
var xRow = squares.indices.contains(x) ? squares[x] : []
xRow.append(layer)
squares.indices.contains(x) ? squares[x] = xRow : squares.append(xRow)
self.layer.addSublayer(layer)
}
}
}
private func layoutSquares() {
guard !squares.isEmpty else { return }
let width = frame.width / CGFloat(dimension)
let height = frame.height / CGFloat(dimension)
for x in 0..<dimension {
for y in 0..<dimension {
squares[x][y].frame = CGRect(x: CGFloat(x) * width,
y: CGFloat(y) * height,
width: width,
height: height)
}
}
}
override func layoutSubviews() {
super.layoutSubviews()
layoutSquares()
}
}
let view = CheckerBoard()
view.frame = CGRect(origin: .zero, size: CGSize(width: 200, height: 200))
view.center = main.center
main.addSubview(view)
let transforms = [
CGAffineTransform(rotationAngle: CGFloat.pi / 2),
CGAffineTransform(translationX: 100, y: 100),
CGAffineTransform(scaleX: 1, y: 2),
]
let targetTransform = transforms.reduce(CGAffineTransform.identity) { $0.concatenating($1) }
let slider = UISlider(frame: CGRect(x: 20, y: 360, width: main.frame.width - 40, height: 40))
main.addSubview(slider)
class Obj: NSObject {
static let shared = Obj()
@objc func sliderValueChanged() {
if slider.value == 1 {
view.transform = targetTransform
} else {
view.transform = .lerp(from: .identity, to: targetTransform, delta: CGFloat(slider.value))
}
}
}
slider.addTarget(Obj.shared, action: #selector(Obj.sliderValueChanged), for: .valueChanged)
PlaygroundPage.current.liveView = main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment