Last active
February 20, 2023 16:15
-
-
Save MaximKotliar/8098ef074f8c03ea907cda7a619dfe06 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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