Skip to content

Instantly share code, notes, and snippets.

@monkeywithacupcake
Created October 22, 2017 09:13
Show Gist options
  • Save monkeywithacupcake/4fe632c3373b7281359237fb160757af to your computer and use it in GitHub Desktop.
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.
//: 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()
@monkeywithacupcake
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment