Created
October 22, 2017 09:13
-
-
Save monkeywithacupcake/4fe632c3373b7281359237fb160757af to your computer and use it in GitHub Desktop.
Swift 4 Playground (building off of the CustomUIView.playground) that shows how to setup multiple animations as view extensions to keep the view controller simple to read - somewhat.
This file contains 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
//: Playground - noun: a place where people can play | |
import UIKit | |
import PlaygroundSupport | |
extension UIView { | |
func asCircle(){ | |
self.layer.cornerRadius = self.frame.width / 2; | |
self.layer.masksToBounds = true | |
} | |
func fadeIn(_ duration: TimeInterval = 1.0, delay: TimeInterval = 0.0, completion: @escaping ((Bool) -> Void) = {(finished: Bool) -> Void in}) { | |
UIView.animate(withDuration: duration, delay: delay, options: UIViewAnimationOptions.curveEaseIn, animations: { | |
self.alpha = 1.0 | |
}, completion: completion) } | |
func fadeOut(_ duration: TimeInterval = 1.0, delay: TimeInterval = 0.0, completion: @escaping (Bool) -> Void = {(finished: Bool) -> Void in}) { | |
UIView.animate(withDuration: duration, delay: delay, options: UIViewAnimationOptions.curveEaseIn, animations: { | |
self.alpha = 0.0 | |
}, completion: completion) | |
} | |
func shrinkView(_ duration: TimeInterval = 1.0, delay: TimeInterval = 0.0, x: Double = 0.5, y: Double = 0.5, completion: @escaping (Bool) -> Void = {(finished: Bool) -> Void in}) { | |
UIView.animate(withDuration: duration, delay: delay, options: UIViewAnimationOptions.curveEaseIn, animations: { | |
self.transform = CGAffineTransform(scaleX: CGFloat(x), y: CGFloat(y)) | |
}, completion: completion) | |
} | |
func growView(_ duration: TimeInterval = 1.0, delay: TimeInterval = 0.0, x: Double = 1.2, y: Double = 1.2, completion: @escaping (Bool) -> Void = {(finished: Bool) -> Void in}) { | |
UIView.animate(withDuration: duration, delay: delay, options: UIViewAnimationOptions.curveEaseIn, animations: { | |
self.transform = CGAffineTransform(scaleX: CGFloat(x), y: CGFloat(y)) | |
}, completion: completion) | |
} | |
func popView(){ | |
self.growView(0.2, completion: { | |
(finished: Bool) -> Void in | |
self.backgroundColor = .white | |
self.fadeOut(0.2, completion: { | |
(finished: Bool) -> Void in | |
self.shrinkView(0.1) | |
self.fadeIn(0.1, completion: { | |
(finished: Bool) -> Void in | |
self.transform = .identity | |
self.backgroundColor = .blue | |
}) | |
}) | |
}) | |
} | |
func colorFade(_ duration: TimeInterval = 1.0, delay: TimeInterval = 0.0, swatch: UIColor = .gray, completion: @escaping (Bool) -> Void = {(finished: Bool) -> Void in}) { | |
UIView.animate(withDuration: duration, delay: delay, options: UIViewAnimationOptions.curveEaseIn, animations: { | |
self.backgroundColor = swatch | |
}, completion: completion) | |
} | |
} | |
class MyView: UIView { | |
let label = UILabel() | |
func updateText(_ text:String?) { | |
guard let text = text else { return } | |
label.text = text | |
} | |
override init(frame: CGRect){ | |
super.init(frame: frame) | |
label.translatesAutoresizingMaskIntoConstraints = false | |
label.text = "MyView!" | |
label.numberOfLines = 0 | |
label.textColor = .red | |
label.textAlignment = .center | |
self.addSubview(label) | |
} | |
override func layoutSubviews() { | |
super.layoutSubviews() | |
label.frame = bounds | |
} | |
required init?(coder aDecoder: NSCoder) { | |
fatalError("init(coder:) has not been implemented") | |
} | |
} // end MyView | |
class MyCollectionViewCell: UICollectionViewCell { | |
override func awakeFromNib() { | |
print("awakeFromNib") | |
} | |
override init(frame: CGRect) { | |
super.init(frame: frame) | |
self.asCircle() | |
} | |
required init?(coder aDecoder: NSCoder) { | |
fatalError("init(coder:) has not been implemented") | |
} | |
} | |
class MyViewController : UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate, UICollectionViewDataSource { | |
// MARK: - Properties | |
// views | |
var headerView = MyView() | |
var selView = MyView() | |
var collectionView: UICollectionView? | |
// attributes | |
var numberSelected = 0 // track this | |
override func loadView() { | |
let view = UIView() | |
view.backgroundColor = .black | |
self.view = view | |
} | |
// MARK: - LifeCycle | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
setupHeader() | |
setupCollection() | |
setupSelection() | |
setupStack() | |
} | |
// MARK: Methods | |
func updateSelectedCount(_ upOrDown:String) { | |
switch(upOrDown) { | |
case "up": | |
numberSelected += 1 | |
headerView.updateText("Another one bites the dust") | |
case "down": | |
headerView.updateText("Sorry") | |
if numberSelected > 0 { | |
numberSelected -= 1 | |
} | |
default: | |
print("not up or down") | |
} | |
} | |
// MARK: CollectionView Methods | |
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { | |
return 40 | |
} | |
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { | |
return CGSize(width: 50.0, height: 50.0) | |
} | |
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { | |
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell", for: indexPath) as! MyCollectionViewCell | |
cell.backgroundColor = .gray | |
return cell | |
} | |
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { | |
let selectedCell = collectionView.cellForItem(at: indexPath) | |
// animate that Cell! | |
selectedCell?.popView() | |
updateSelectedCount("up") | |
let selected = String(describing: indexPath.row + 1) | |
let selString = "You selected cell #\(selected) for a total of \(numberSelected)" | |
selView.updateText(selString) | |
} | |
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { | |
let deselectedCell = collectionView.cellForItem(at: indexPath) | |
deselectedCell?.colorFade(0.2) | |
updateSelectedCount("down") | |
let deselected = String(describing: indexPath.row + 1) | |
let selString = "You deselected cell #\(deselected) for a total of \(numberSelected)" | |
selView.updateText(selString) | |
} | |
// MARK: SetUp Methods | |
func setupHeader() { | |
headerView.updateText("Update") | |
} | |
func setupCollection() { | |
let frame = self.view.frame | |
let layout = UICollectionViewFlowLayout() | |
collectionView = UICollectionView(frame: frame, collectionViewLayout: layout) | |
collectionView?.delegate = self | |
collectionView?.dataSource = self | |
self.collectionView?.backgroundColor = .black | |
self.collectionView?.register(MyCollectionViewCell.self, forCellWithReuseIdentifier: "MyCell") | |
collectionView?.allowsMultipleSelection = true | |
} | |
func setupSelection() { | |
selView.updateText("None Selected Yet") | |
} | |
func setupStack() { | |
let stackView = UIStackView(arrangedSubviews: [headerView, collectionView!, selView]) | |
stackView.axis = .vertical | |
stackView.distribution = .fill | |
stackView.alignment = .fill | |
stackView.spacing = 10 | |
stackView.translatesAutoresizingMaskIntoConstraints = false | |
// add some component constraints | |
let headHeight = headerView.heightAnchor.constraint(equalToConstant: 50) | |
let selHeight = selView.heightAnchor.constraint(equalToConstant: 50) | |
stackView.addConstraints([headHeight, selHeight]) | |
view.addSubview(stackView) | |
//autolayout the stack view | |
let sH = NSLayoutConstraint.constraints(withVisualFormat: "H:|-20-[stackView]-20-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["stackView":stackView]) | |
let sV = NSLayoutConstraint.constraints(withVisualFormat: "V:|-30-[stackView]-30-|", options: NSLayoutFormatOptions(rawValue:0), metrics: nil, views: ["stackView":stackView]) | |
view.addConstraints(sH) | |
view.addConstraints(sV) | |
} | |
} | |
PlaygroundPage.current.liveView = MyViewController() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The code above can be pasted into a playground to get a live view. The behavior on select and deselect is different because the animation is based on the view level. If an animation is in the cell, say for a condition like "isHighlighted" - the animation would be the same when selected and deselected.