dimanche 25 janvier 2015

How do you create a UICollectionView Transition Animation in Swift without Storyboards?

I'm trying to achieve literally this effect shown on Facebook's photo collectionview:


Video: http://d.pr/v/YCDB


I'm trying to have the following:


Animating to and from the indexpath of the cell Pan up and down to fade the view out and dismiss it Pretty much what's in the video.


Here's what I've done so far and it's a little janky on the animations and I can't figure out the to and from the index path part at all.


My CollectionView:



import UIKit

class Feed: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {

var collectionView: UICollectionView?
var myArray = ["cool", "neat", "fun", "cool", "neat", "fun", "cool", "neat", "fun", "cool", "neat", "fun", "cool", "neat", "fun", "cool", "neat", "fun", "cool", "neat", "fun", "cool", "neat", "fun"]


override func viewDidLoad() {
super.viewDidLoad()

self.view.backgroundColor = UIColor.yellowColor()
self.title = "Feed"

let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
layout.itemSize = CGSize(width: 100, height: 100)

collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionView!.dataSource = self
collectionView!.delegate = self
collectionView!.registerClass(Cell.self, forCellWithReuseIdentifier: "Cell")
collectionView!.backgroundColor = UIColor.whiteColor()

self.view.addSubview(collectionView!)

}


func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return myArray.count
}

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as Cell
cell.textLabel.text = myArray[indexPath.row]
return cell

}

func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as Cell

cell.textLabel.text = myArray[indexPath.row]

let vc:Detail = Detail()
let navController:CustomNavigationController = CustomNavigationController(rootViewController: vc)

println(cell.textLabel.text)
vc.myLabel.text = cell.textLabel.text
presentViewController(navController, animated: true, completion: nil)
}


}


My Transition



import UIKit

@objc protocol CustomNavigationControllerDelegate {
func pushToNextScene()
}

class CustomNavigationController: UINavigationController, UIViewControllerTransitioningDelegate, UINavigationControllerDelegate {

override func viewDidLoad() {
super.viewDidLoad()

transitioningDelegate = self // for presenting the original navigation controller
delegate = self // for navigation controller custom transitions

let top = UIPanGestureRecognizer(target: self, action: "handleSwipeFromTop:")
self.view.addGestureRecognizer(top);

}

var interactionController: UIPercentDrivenInteractiveTransition?

func handleSwipeFromTop(gesture: UIPanGestureRecognizer) {

let percent = gesture.translationInView(gesture.view!).y / gesture.view!.bounds.size.height

if gesture.state == .Began {
interactionController = UIPercentDrivenInteractiveTransition()
if viewControllers.count > 1 {
popViewControllerAnimated(true)
} else {
dismissViewControllerAnimated(true, completion: nil)
}
} else if gesture.state == .Changed {
interactionController?.updateInteractiveTransition(percent)
} else if gesture.state == .Ended {
if percent > 0.2 {
interactionController?.finishInteractiveTransition()
} else {
interactionController?.cancelInteractiveTransition()
}
interactionController = nil
}
}



// MARK: - UIViewControllerTransitioningDelegate

func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return ForwardAnimator()
}

func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return BackAnimator()
}

func interactionControllerForPresentation(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactionController
}

func interactionControllerForDismissal(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactionController
}

// MARK: - UINavigationControllerDelegate

func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {

if operation == .Push {
return ForwardAnimator()
} else if operation == .Pop {
return BackAnimator()
}
return nil
}

func navigationController(navigationController: UINavigationController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactionController
}

}

class ForwardAnimator : NSObject, UIViewControllerAnimatedTransitioning {

func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval {
return 0.3
}

func animateTransition(context: UIViewControllerContextTransitioning) {
let toView = context.viewControllerForKey(UITransitionContextToViewControllerKey)?.view
let fromView = context.viewControllerForKey(UITransitionContextFromViewControllerKey)?.view

context.containerView().addSubview(toView!)

toView?.alpha = 0.0

UIView.animateWithDuration(transitionDuration(context), animations: {
toView?.alpha = 1.0
return
}, completion: { finished in
context.completeTransition(!context.transitionWasCancelled())
})
}

}

class BackAnimator : NSObject, UIViewControllerAnimatedTransitioning {

func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval {
return 0.2
}

func animateTransition(context: UIViewControllerContextTransitioning) {
let toView = context.viewControllerForKey(UITransitionContextToViewControllerKey)?.view
let fromView = context.viewControllerForKey(UITransitionContextFromViewControllerKey)?.view

context.containerView().insertSubview(toView!, belowSubview: fromView!)

UIView.animateWithDuration(transitionDuration(context), animations: {
fromView?.alpha = 0.0
return
}, completion: { finished in
context.completeTransition(!context.transitionWasCancelled())
})
}
}


And if you want it/ease to recreate:


My Cell



import UIKit

class Cell: UICollectionViewCell {

let textLabel: UILabel!

required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}

override init(frame: CGRect) {
super.init(frame: frame)

self.backgroundColor = UIColor.greenColor()

let textFrame = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height)
textLabel = UILabel(frame: textFrame)
textLabel.font = UIFont.systemFontOfSize(UIFont.smallSystemFontSize())
textLabel.textAlignment = .Center
contentView.addSubview(textLabel)


}

}


My Detail View



import UIKit

class Detail: UIViewController, CustomNavigationControllerDelegate {

var myLabel : UILabel = UILabel()

override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor(white: 0, alpha: 0.7)

let myBtn: UIButton = UIButton()
myBtn.frame = CGRectMake(100, 100, 100, 100)
myBtn.backgroundColor = UIColor.greenColor()
myBtn.addTarget(self, action: "goNext", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(myBtn)

myLabel.textColor = UIColor.whiteColor()
myLabel.frame = self.view.bounds
self.view.addSubview(myLabel)

}

func pushToNextScene() {
self.dismissViewControllerAnimated(true, completion: nil)
}

func goNext () {
println("button")
self.dismissViewControllerAnimated(true, completion: nil)
}
}



Aucun commentaire:

Enregistrer un commentaire