Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

depth page Transform on UIView,Swift

how to create the depth page Transform animation (Top to bottom, bottom to Top) like this https://youtu.be/c2ccXwwmcnA . I searched in Google but I am not get any idea for how to Implement in iOS.

Sample Output Like this:

in depth effect

like image 267
Shangari C Avatar asked Nov 19 '15 08:11

Shangari C


2 Answers

I followed the tutorial Animated Transitions in Swift and I got this effect:

in depth effect

If you don't want to read the whole tutorial, here is basically what you have to do:

1. Create the TrasitionManager class

//
//  TransitionManager.swift
//  Aleph Retamal
//
//  Created by Aleph Retamal on 4/19/15.
//  Copyright (c) 2015 Aleph Retamal. All rights reserved.
//

import UIKit

class TransitionManager: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate  {

    private var presenting:Bool = true
    var leftSide:Bool = true

    // MARK: UIViewControllerAnimatedTransitioning protocol methods

    // animate a change from one viewcontroller to another
    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {

        // get reference to our fromView, toView and the container view that we should perform the transition in
        let container = transitionContext.containerView()
        let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)!
        let toView = transitionContext.viewForKey(UITransitionContextToViewKey)!


        let offScreenRight = CGAffineTransformConcat(CGAffineTransformMakeTranslation(container!.frame.width, 0), CGAffineTransformMakeScale(0.1, 0.1))
        let offScreenLeft = CGAffineTransformMakeTranslation(-container!.frame.width, 0)

        // start the toView to the right of the screen
        if !leftSide {
            if self.presenting {
                toView.transform = offScreenRight
            } else {
                toView.transform = offScreenLeft
            }
        } else {
            if self.presenting {
                toView.transform = offScreenLeft
            } else {
                toView.transform = offScreenRight
            }
        }

        // add the both views to our view controller
        container!.addSubview(toView)
        container!.addSubview(fromView)

        // get the duration of the animation
        // DON'T just type '0.5s' -- the reason why won't make sense until the next post
        // but for now it's important to just follow this approach
        let duration = self.transitionDuration(transitionContext)

        // perform the animation!
        // for this example, just slid both fromView and toView to the left at the same time
        // meaning fromView is pushed off the screen and toView slides into view
        // we also use the block animation usingSpringWithDamping for a little bounce
        UIView.animateWithDuration(duration, animations: { () -> Void in
            if !self.leftSide {
                if self.presenting {
                    fromView.transform = offScreenLeft
                } else {
                    container!.sendSubviewToBack(fromView)
                    fromView.transform = offScreenRight
                }
            } else {
                if self.presenting {
                    fromView.transform = offScreenRight
                    container!.sendSubviewToBack(fromView)
                } else {
                    fromView.transform = offScreenLeft
                }
            }

            toView.transform = CGAffineTransformIdentity
            }) { (finished) -> Void in
                // tell our transitionContext object that we've finished animating
                transitionContext.completeTransition(true)
        }

    }

    // return how many seconds the transiton animation will take
    func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
        return 1.0
    }

    // MARK: UIViewControllerTransitioningDelegate protocol methods

    // return the animataor when presenting a viewcontroller
    // remmeber that an animator (or animation controller) is any object that aheres to the UIViewControllerAnimatedTransitioning protocol
    func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        self.presenting = true
        return self
    }

    // return the animator used when dismissing from a viewcontroller
    func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        self.presenting = false
        return self
    }

}

This is where you set how the views should transform

let offScreenRight = CGAffineTransformConcat(CGAffineTransformMakeTranslation(container!.frame.width, 0), CGAffineTransformMakeScale(0.1, 0.1))
let offScreenLeft = CGAffineTransformMakeTranslation(-container!.frame.width, 0)

In this case, one view will Translate + Scale while the other one will only Translate

2. Instantiate the TransitionManager inside your ViewController

let transitionManager = TransitionManager()

3. Set the transition delegate

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    transitionManager.leftSide = false
    let toViewController = segue.destinationViewController
    toViewController.transitioningDelegate = self.transitionManager
}

If you want the view to come from the right, leftSide = false

transitionManager.leftSide = false

That's it!

Edit:

To achieve this effect:

in depth effect 2

Here's a git with the project: https://github.com/alaphao/indepth

Use constraints for alignment an sizing:

  1. Create 2 views with same width / height of their parent view

  2. Align both with CenterX

  3. Set both distance from bottom = 0 and create an outlet for the constraints

  4. Create a PanGestureRecognizer

  5. Animate the front view bottom constraint accordingly to the pan translationInView.y

like image 128
alephao Avatar answered Sep 19 '22 13:09

alephao


Here is the solution make new swift file and copy this code

import UIKit

class UltravisualLayout: UICollectionViewLayout {

    private var contentWidth:CGFloat!
    private var contentHeight:CGFloat!
    private var yOffset:CGFloat = 0

    var maxAlpha:CGFloat = 1
    var minAlpha:CGFloat = 0

     //DEFAULT VALUES
    //var widthOffset:CGFloat = 35
    // var heightOffset:CGFloat = 35

    var widthOffset:CGFloat = 100
    var heightOffset:CGFloat = 100
    private var cache = [UICollectionViewLayoutAttributes]()

    private var itemWidth:CGFloat{
        return (collectionView?.bounds.width)!
    }
    private var itemHeight:CGFloat{
        return (collectionView?.bounds.height)!
    }
    private var collectionViewHeight:CGFloat{
        return (collectionView?.bounds.height)!
    }


    private var numberOfItems:Int{
        return (collectionView?.numberOfItemsInSection(0))!
    }


    private var dragOffset:CGFloat{
        return (collectionView?.bounds.height)!
    }
    private var currentItemIndex:Int{
        return max(0, Int(collectionView!.contentOffset.y / collectionViewHeight))
    }

    var nextItemBecomeCurrentPercentage:CGFloat{
        return (collectionView!.contentOffset.y / (collectionViewHeight)) - CGFloat(currentItemIndex)
    }

    override func prepareLayout() {
        cache.removeAll(keepCapacity: false)
        yOffset = 0

        for item in 0 ..< numberOfItems{

            let indexPath = NSIndexPath(forItem: item, inSection: 0)
            let attribute = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
            attribute.zIndex = -indexPath.row

            if (indexPath.item == currentItemIndex+1) && (indexPath.item < numberOfItems){

                attribute.alpha = minAlpha + max((maxAlpha-minAlpha) * nextItemBecomeCurrentPercentage, 0)
                let width = itemWidth - widthOffset + (widthOffset * nextItemBecomeCurrentPercentage)
                let height = itemHeight - heightOffset + (heightOffset * nextItemBecomeCurrentPercentage)

                let deltaWidth =  width/itemWidth
                let deltaHeight = height/itemHeight

                attribute.frame = CGRectMake(0, yOffset, itemWidth, itemHeight)
                attribute.transform = CGAffineTransformMakeScale(deltaWidth, deltaHeight)

                attribute.center.y = (collectionView?.center.y)! +  (collectionView?.contentOffset.y)!
                attribute.center.x = (collectionView?.center.x)! + (collectionView?.contentOffset.x)!
                yOffset += collectionViewHeight

            }else{
                attribute.frame = CGRectMake(0, yOffset, itemWidth, itemHeight)
                attribute.center.y = (collectionView?.center.y)! + yOffset
                attribute.center.x = (collectionView?.center.x)!
                yOffset += collectionViewHeight
            }
            cache.append(attribute)
        }
    }

    //Return the size of ContentView
    override func collectionViewContentSize() -> CGSize {
        contentWidth = (collectionView?.bounds.width)!
        contentHeight = CGFloat(numberOfItems) * (collectionView?.bounds.height)!
        return CGSizeMake(contentWidth, contentHeight)
    }

    //Return Attributes  whose frame lies in the Visible Rect
    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        var layoutAttributes = [UICollectionViewLayoutAttributes]()
        for attribute in cache{
            if CGRectIntersectsRect(attribute.frame, rect){
                layoutAttributes.append(attribute)
            }
        }
        return layoutAttributes
    }


    override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
        return true
    }

    override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
        let itemIndex = round(proposedContentOffset.y / (dragOffset))
        let yOffset = itemIndex * (collectionView?.bounds.height)!
        return CGPoint(x: 0, y: yOffset)
    }
    override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {

        // Logic that calculates the UICollectionViewLayoutAttributes of the item
        // and returns the UICollectionViewLayoutAttributes
        return UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
    }

}

In Main.storyboard file use UICollectionView or UICollectionViewController component select UICollectionView go to attributes inspector and change Layout from Flow to custom and set class attribute to UltravisualLayout

here is the project link and this link to the tutorial

Hope This might help GOOD LUCK..

like image 36
Anirudh R.Huilgol. Avatar answered Sep 20 '22 13:09

Anirudh R.Huilgol.