UICollectionViewController Transition like open and close an album. Blog for this: Part I, Part II
Drag files in "Classes" folder into your project.
- In your storyboard, drag a object to your navigation controller, and set its custom class to "SDENavigationControllerDelegate"
- Set this object to be your navigation controller's delegate.
-
At the last, add several lines code in your UICollectionView's delegate:
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { self.selectedIndexPath = indexPath let layoutAttributes = self.collectionView!.layoutAttributesForItemAtIndexPath(indexPath) let areaRect = self.collectionView!.convertRect(layoutAttributes!.frame, toView: self.collectionView!.superview) let toVC = ...... toVC.coverRectInSuperview = areaRect ... }
Now your project supports to pinch to pop.
- iOS 8.0+
- Swift 2.0
Thanks for @CezaryKopacz, @ColinEberhardt. I learn a lot from their repo. And thanks for Vincent Ngo very much, I find the key to resolve the problem of pinching to push. There is a little problem with time delta algorithm, I will update if I find the way to improvement.
Pinching to push is a little complex. ViewController to push must be created before push, and init a ViewController is complex than do something to a existed ViewController, so this is why most of libraries do not support pinch to push.
If you want to support pinch to push, switch to the branch "Pinch-Push-Pop-Transition", drag files in "Classes" folder in this branch into your project, there is a little difference between "Pinch-Push-Pop-Transition" branch with other branches.
Add the below properties to your UICollectionViewController child class:
var transitionDelegate: SDENavigationControllerDelegate?
var pinchGestureRecognizer: UIPinchGestureRecognizer?{
didSet(newValue){
collectionView?.addGestureRecognizer(pinchGestureRecognizer!)
}
}
override viewDidLoad(){
...
pinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: "handlePinch:")
}
deinit{
if pinchGestureRecognizer != nil{
collectionView?.removeGestureRecognizer(pinchGestureRecognizer!)
}
}
//MARK: Pinch Push and Pop
func getIndexPathForGesture(gesture: UIPinchGestureRecognizer) -> NSIndexPath?{
let location0 = gesture.locationOfTouch(0, inView: gesture.view)
let location1 = gesture.locationOfTouch(1, inView: gesture.view)
let middleLocation = CGPointMake((location0.x + location1.x)/2, (location0.y + location1.y)/2)
let indexPath = collectionView?.indexPathForItemAtPoint(middleLocation)
return indexPath
}
func handlePinch(gesture: UIPinchGestureRecognizer){
switch gesture.state{
case .Began:
if gesture.scale >= 1.0{
guard let indexPath = getIndexPathForGesture(gesture) else{
return
}
self.selectedIndexPath = indexPath
let layoutAttributes = collectionView!.layoutAttributesForItemAtIndexPath(indexPath)
let areaRect = collectionView!.convertRect(layoutAttributes!.frame, toView: collectionView?.superview)
if let toVC = ...{
toVC.coverRectInSuperview = areaRect
transitionDelegate = navigationController?.delegate as? SDENavigationControllerDelegate
transitionDelegate?.interactive = true
navigationController?.pushViewController(toVC, animated: true)
}
}else{
//after view controller is poped, UIViewController.navigationController is nil. So you need to keep it somewhere before pop
transitionDelegate = self.navigationController?.delegate as? SDENavigationControllerDelegate
transitionDelegate?.interactive = true
self.navigationController?.popViewControllerAnimated(true)
}
case .Changed:
guard transitionDelegate != nil else{
return
}
guard let interactionController = transitionDelegate?.interactionController else{
return
}
var progress = gesture.scale
if transitionDelegate!.isPush{
progress = gesture.scale - 1.0 >= 0.9 ? 0.9 : gesture.scale - 1.0
}else{
progress = 1.0 - gesture.scale
}
interactionController.updateInteractiveTransition(progress)
case .Ended, .Cancelled:
guard transitionDelegate != nil else{
return
}
guard let interactionController = transitionDelegate?.interactionController else{
return
}
var progress = gesture.scale
if transitionDelegate!.isPush{
progress = gesture.scale - 1.0 >= 0.9 ? 0.9 : gesture.scale - 1.0
}else{
progress = 1.0 - gesture.scale
}
if progress >= 0.4{
interactionController.finishInteractiveTransition()
}else{
interactionController.cancelInteractiveTransition()
}
transitionDelegate?.interactive = false
default:
guard transitionDelegate != nil else{
return
}
transitionDelegate?.interactive = false
}
}