How to Build Animation Transition Like Instagram Stories Swift Ios

How to Build Animation Transition Like Instagram Stories Swift Ios

Create transition and interaction similar iOS Photos app

Amazing illustration by @pablostanley!

iOS Photos app transition and interaction

The user feel of iOS Photos app transition and interaction is nice.

The photo zoom in past tap the jail cell, and zoom out by pull down.

I created a clone of this transition and interaction.

What you tin can practise with this article

The sample code is in the GitHub repository.

masamichiueta/FluidPhoto

Implementation

Permit's implement information technology by iii steps.

  1. Build the structure of the ViewControllers
  2. Implement zoom animation
  3. Implement interactive transition by pan gesture

ane. Build the structure of the ViewControllers

The structure of the ViewControllers is like this.

In that location are iv ViewControllers.

  1. CollectionViewController: List photos
  2. PageViewController: Paging photos
  3. ZoomImageViewController: Zoom in and out the paradigm in 2
  4. ContainerViewController: Wrap 2

The ContainerViewController is non required, only when you want to identify a toolbar on PageViewController, you cannot put information technology on the Storyboard, and so employ ContainerViewController.

Tap on the CollectionViewCell makes a screen transition and after the transition, PageViewController paginate the photos.

PageViewController instantiate the ZoomImageViewController at UIPageViewControllerDelegate and display information technology.

This is the real storyboard in this project.

2. Implement the zoom animation

UIViewControllerTransitioningDelegat and UIViewControllerAnimatedTransitioning are used to create custom animations.

I created two classes.

  1. ZoomTransitionController・・・implements UIViewControllerTransitioningDelegate or UINavigationControllerDelegate and manage transition.
  2. ZoomAnimator・・・implements UIViewControllerAnimatedTransitioning and zoom animation logic.

ZoomAnimator is a property of ZoomTransitionController.

The reason why I split up ZoomAnimator and ZoomTransitionController in ii classes is to manage interactive transition and normal transition.

ZoomAnimator is non interactive transition.

ZoomAnimator

ZoomAnimator is responsible for the logic of zoom animation. In other words, ZoomAnimator acquires the UIImageView to be zoomed and breathing from the transition source frame to the transition destination frame.

Use the consul to get the transition paradigm and its frame. This consul is implemented by transition source / transition destination ViewController.

          protocol ZoomAnimatorDelegate: form {
func transitionWillStartWith(zoomAnimator: ZoomAnimator)
func transitionDidEndWith(zoomAnimator: ZoomAnimator)
func referenceImageView(for zoomAnimator: ZoomAnimator) -> UIImageView?
func referenceImageViewFrameInTransitioningView(for zoomAnimator: ZoomAnimator) -> CGRect?
}

ZoomAnimator has these properties.

  • fromDelegate...Source
  • toDelegate...Destination
  • isPresenting...Determine whether you are zooming in from the list to testify details or zoom out from the details.
  • transitionImageView...Animated image
          class ZoomAnimator: NSObject {

weak var fromDelegate: ZoomAnimatorDelegate?
weak var toDelegate: ZoomAnimatorDelegate?

var transitionImageView: UIImageView?
var isPresenting: Bool = true

From here, I explicate the zoom animation logic.

First, this is the 2 methods of UIViewControllerAnimatedTransitioning.

          extension ZoomAnimator: UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
if cocky.isPresenting {
return 0.five
} else {
render 0.25
}
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
if cocky.isPresenting {
animateZoomInTransition(using: transitionContext)
} else {
animateZoomOutTransition(using: transitionContext)
}
}
}

transitionDuration is the duration of animation.

animateTransition is where blitheness really execute. In this method, isPresenting property is used to check zoom-in or zoom-out.

Blitheness Logic

Next allow's check the actual animation part.

The logic is constructed 4 parts.

  1. Hide the source and destination image
  2. Create an image to animate from the source image
  3. Calculate the frame of the destination image
  4. Breathing image from source frame to destination frame

Zoom-in

          fileprivate func animateZoomInTransition(using transitionContext: UIViewControllerContextTransitioning) {

let containerView = transitionContext.containerView

guard let toVC = transitionContext.viewController(forKey: .to),
let fromVC = transitionContext.viewController(forKey: .from),

// Get the source imageView
let fromReferenceImageView = cocky.fromDelegate?.referenceImageView(for: self),

// Become the destination imageView
let toReferenceImageView = self.toDelegate?.referenceImageView(for: cocky),

// Go the frame of source imageView
let fromReferenceImageViewFrame = cocky.fromDelegate?.referenceImageViewFrameInTransitioningView(for: self)
else {
return
}

self.fromDelegate?.transitionWillStartWith(zoomAnimator: cocky)
self.toDelegate?.transitionWillStartWith(zoomAnimator: self)

toVC.view.blastoff = 0

// Hide the destination imageView
toReferenceImageView.isHidden = truthful
containerView.addSubview(toVC.view)

let referenceImage = fromReferenceImageView.epitome!

// Generate imageView to employ for zoom blitheness
if cocky.transitionImageView == nil {
let transitionImageView = UIImageView(image: referenceImage)
transitionImageView.contentMode = .scaleAspectFill
transitionImageView.clipsToBounds = true
transitionImageView.frame = fromReferenceImageViewFrame
cocky.transitionImageView = transitionImageView
containerView.addSubview(transitionImageView)
}

// Hide the source imageView
fromReferenceImageView.isHidden = truthful

// Summate destination imageView frame after animation
permit finalTransitionSize = calculateZoomInImageFrame(image: referenceImage, forView: toVC.view)

UIView.animate(withDuration: transitionDuration(using: transitionContext),
delay: 0,
usingSpringWithDamping: 0.eight,
initialSpringVelocity: 0,
options: [UIViewAnimationOptions.transitionCrossDissolve],
animations: {

// Update the frame of imageView
self.transitionImageView?.frame = finalTransitionSize
toVC.view.alpha = 1.0
fromVC.tabBarController?.tabBar.alpha = 0
},
completion: { completed in

// Remove the imageView
self.transitionImageView?.removeFromSuperview()

// Show source and destination imageView
toReferenceImageView.isHidden = false
fromReferenceImageView.isHidden = simulated

cocky.transitionImageView = zero

transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
cocky.toDelegate?.transitionDidEndWith(zoomAnimator: cocky)
self.fromDelegate?.transitionDidEndWith(zoomAnimator: self)
})
}

Zoom-out

          fileprivate func animateZoomOutTransition(using transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView

guard let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to),
let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
permit fromReferenceImageView = self.fromDelegate?.referenceImageView(for: self),
let toReferenceImageView = self.toDelegate?.referenceImageView(for: self),
let fromReferenceImageViewFrame = self.fromDelegate?.referenceImageViewFrameInTransitioningView(for: cocky),
let toReferenceImageViewFrame = cocky.toDelegate?.referenceImageViewFrameInTransitioningView(for: self)
else {
return
}

cocky.fromDelegate?.transitionWillStartWith(zoomAnimator: self)
self.toDelegate?.transitionWillStartWith(zoomAnimator: self)

toReferenceImageView.isHidden = true

allow referenceImage = fromReferenceImageView.image!

if self.transitionImageView == nil {
let transitionImageView = UIImageView(epitome: referenceImage)
transitionImageView.contentMode = .scaleAspectFill
transitionImageView.clipsToBounds = true
transitionImageView.frame = fromReferenceImageViewFrame
self.transitionImageView = transitionImageView
containerView.addSubview(transitionImageView)
}

containerView.insertSubview(toVC.view, belowSubview: fromVC.view)
fromReferenceImageView.isHidden = true

let finalTransitionSize = toReferenceImageViewFrame

UIView.breathing(withDuration: transitionDuration(using: transitionContext),
delay: 0,
options: [],
animations: {
fromVC.view.alpha = 0
self.transitionImageView?.frame = finalTransitionSize
toVC.tabBarController?.tabBar.blastoff = ane
}, completion: { completed in

self.transitionImageView?.removeFromSuperview()
toReferenceImageView.isHidden = false
fromReferenceImageView.isHidden = false

transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
self.toDelegate?.transitionDidEndWith(zoomAnimator: cocky)
cocky.fromDelegate?.transitionDidEndWith(zoomAnimator: self)

})
}

ZoomTransitionController

ZoomTransitionController has the same property as ZoomAnimator to refer to the source and destination every bit a delegate.

          class ZoomTransitionController: NSObject {

allow animator: ZoomAnimator

weak var fromDelegate: ZoomAnimatorDelegate?
weak var toDelegate: ZoomAnimatorDelegate?

...

ZoomTransitionController returns ZoomAnimator at the showtime of the transition.

          extension ZoomTransitionController: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
self.animator.isPresenting = truthful
self.animator.fromDelegate = fromDelegate
self.animator.toDelegate = toDelegate
return cocky.animator
}

func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
cocky.animator.isPresenting = faux
let tmp = self.fromDelegate
self.animator.fromDelegate = self.toDelegate
self.animator.toDelegate = tmp
return self.animator
}
}

extension ZoomTransitionController: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {

if functioning == .push {
self.animator.isPresenting = truthful
cocky.animator.fromDelegate = fromDelegate
cocky.animator.toDelegate = toDelegate
} else {
self.animator.isPresenting = fake
allow tmp = self.fromDelegate
self.animator.fromDelegate = self.toDelegate
self.animator.toDelegate = tmp
}

return self.animator
}
}

The zoom blitheness setting is now completed.
Let'south use ZoomTransitionController on the ViewController to make zoom transitions.

ViewController

ContainerViewController has ZoomTransitionController as property.

          class PhotoPageContainerViewController: UIViewController, UIGestureRecognizerDelegate {
var transitionController = ZoomTransitionController()
...
}

In CollectionView, when the cell is tapped, it executes Segue and start transition. Set animation delegates in set method.

          override func ready(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowPhotoPageView" {
let nav = self.navigationController
let vc = segue.destination as! PhotoPageContainerViewController

// Set navigationController consul to ZoomTransitionController
nav?.delegate = vc.transitionController
vc.transitionController.fromDelegate = self
vc.transitionController.toDelegate = vc

...
}
}

Now the transition by ZoomTransitionController is executed when UINavigationController transition occurs.

The balance is completed by implementing ZoomAnimatorDelegate on each ViewController and telling ZoomAnimator the prototype and frame of the source and destination.

CollectionViewController

          // CollectionView側
extension CollectionViewController: ZoomAnimatorDelegate {
func transitionWillStartWith(zoomAnimator: ZoomAnimator) {

}

func transitionDidEndWith(zoomAnimator: ZoomAnimator) {

// CollectionViewのCellの位置を調整する
let cell = cocky.collectionView.cellForItem(at: self.selectedIndexPath) as! PhotoCollectionViewCell

let cellFrame = self.collectionView.convert(jail cell.frame, to: self.view)

if cellFrame.minY < self.collectionView.contentInset.pinnacle {
self.collectionView.scrollToItem(at: self.selectedIndexPath, at: .top, animated: false)
} else if cellFrame.maxY > self.view.frame.peak - cocky.collectionView.contentInset.bottom {
self.collectionView.scrollToItem(at: self.selectedIndexPath, at: .lesser, blithe: false)
}
}

func referenceImageView(for zoomAnimator: ZoomAnimator) -> UIImageView? {
// CollectionViewの画像を返す
let cell = cocky.collectionView.cellForItem(at: self.selectedIndexPath) as! PhotoCollectionViewCell
return prison cell.imageView
}

func referenceImageViewFrameInTransitioningView(for zoomAnimator: ZoomAnimator) -> CGRect? {
// CollectionViewの画像のフレームを返す

let cell = self.collectionView.cellForItem(at: self.selectedIndexPath) every bit! PhotoCollectionViewCell

let cellFrame = cocky.collectionView.convert(jail cell.frame, to: cocky.view)

if cellFrame.minY < self.collectionView.contentInset.top {
render CGRect(x: cellFrame.minX, y: self.collectionView.contentInset.summit, width: cellFrame.width, elevation: cellFrame.height - (self.collectionView.contentInset.top - cellFrame.minY))
}

render cellFrame
}
}

ContainerViewController

          extension ContainerViewController: ZoomAnimatorDelegate {
func transitionWillStartWith(zoomAnimator: ZoomAnimator) {
}

func transitionDidEndWith(zoomAnimator: ZoomAnimator) {
}

func referenceImageView(for zoomAnimator: ZoomAnimator) -> UIImageView? {
return cocky.currentViewController.imageView
}

func referenceImageViewFrameInTransitioningView(for zoomAnimator: ZoomAnimator) -> CGRect? {
return self.currentViewController.scrollView.convert(self.currentViewController.imageView.frame, to: self.currentViewController.view)
}
}

OK, zoom-in and zoom-out blitheness is completed.

iii. Implement interactive transition with Pan gesture

To dorsum to the collection view by pulling down like iOS Photo app, information technology is necessary to implement an interactive transition by pan gesture.

In this project, I add UIPanGestureRecognizer to ContainerViewController, and notice the gesture of pulling downwardly, and perform interactive screen transition.

Interactive transition of iOS Photo app

The characteristics of iOS Photo app transition

  • If you pull the photo downward while not zooming, the photo gradually becomes smaller. Minimum size exists
  • The photograph comes with the position of the finger when yous pull the photo downwards
  • The background becomes transparent according to the amount of pulling the photograph
  • When you take your finger upwards, the photo returns to its original position

Implementation strategy

  1. I innovate a new form ZoomDismissalInteractionController that responsible for animation of interactive transitions.
  2. Nosotros add a property of ZoomDismissalInteractionController and delegate method to ZoomTransitionController to perform interactive transitions
  3. Information technology is PageViewController that performs interactive transitions using pan gestures. Therefore, UIPanGestureRecognizer is added to PageViewController.

ZoomDismissalInteractionController

ZoomDismissalInteractionController implements UIViewControllerInteractiveTransitioning and is responsible for the logic of interactive transitions.

ZoomDismissalInteractionController has a method chosen each time a user did a pan gesture and controls the animation of the photo according to the country of the electric current pan gesture.

          form ZoomDismissalInteractionController: NSObject {
...

// Chosen each time a user did a pan gesture
func didPanWith(gestureRecognizer: UIPanGestureRecognizer) {

// The eye point of source image
let anchorPoint = CGPoint(x: fromReferenceImageViewFrame.midX, y: fromReferenceImageViewFrame.midY)

// The translation of the image
allow translatedPoint = gestureRecognizer.translation(in: fromReferenceImageView)

// The vertical translation of the image
allow verticalDelta = translatedPoint.y < 0 ? 0 : translatedPoint.y

let backgroundAlpha = backgroundAlphaFor(view: fromVC.view, withPanningVerticalDelta: verticalDelta)

let scale = scaleFor(view: fromVC.view, withPanningVerticalDelta: verticalDelta)

// Update image size co-ordinate to translation of the pan gesture
transitionImageView.transform = CGAffineTransform(scaleX: calibration, y: scale)

// Calculate the position of image to use blitheness
let newCenter = CGPoint(x: anchorPoint.ten + translatedPoint.10, y: anchorPoint.y + translatedPoint.y - transitionImageView.frame.height * (1 - scale) / 2.0)
transitionImageView.center = newCenter

// Update interactive transition by scale
transitionContext.updateInteractiveTransition(1 - scale)

if gestureRecognizer.country == .concluded {

let velocity = gestureRecognizer.velocity(in: fromVC.view)

// If the user take the image upwards, cancel the transition
if velocity.y < 0 || newCenter.y < anchorPoint.y {

// Cancel
}

// Animate
}
}

}

extension ZoomDismissalInteractionController: UIViewControllerInteractiveTransitioning {
func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) {
self.transitionContext = transitionContext

let containerView = transitionContext.containerView

guard let animator = self.animator as? ZoomAnimator,
let fromVC = transitionContext.viewController(forKey: .from),
permit toVC = transitionContext.viewController(forKey: .to),
let fromReferenceImageViewFrame = animator.fromDelegate?.referenceImageViewFrameInTransitioningView(for: animator),
let toReferenceImageViewFrame = animator.toDelegate?.referenceImageViewFrameInTransitioningView(for: animator),
let fromReferenceImageView = animator.fromDelegate?.referenceImageView(for: animator)
else {
return
}

animator.fromDelegate?.transitionWillStartWith(zoomAnimator: animator)
animator.toDelegate?.transitionWillStartWith(zoomAnimator: animator)

self.fromReferenceImageViewFrame = fromReferenceImageViewFrame
self.toReferenceImageViewFrame = toReferenceImageViewFrame

let referenceImage = fromReferenceImageView.image!

containerView.insertSubview(toVC.view, belowSubview: fromVC.view)
if animator.transitionImageView == nix {
let transitionImageView = UIImageView(image: referenceImage)
transitionImageView.contentMode = .scaleAspectFill
transitionImageView.clipsToBounds = true
transitionImageView.frame = fromReferenceImageViewFrame
animator.transitionImageView = transitionImageView
containerView.addSubview(transitionImageView)
}
}

Add ZoomDismissalInteractionController and delegate methods to ZoomTransitionController

Add ZoomDismissalInteractionController to the belongings and use this transition logic for interactive transitions.

          class ZoomTransitionController: NSObject {

// Add
permit interactionController: ZoomDismissalInteractionController

//
var isInteractive: Bool = fake

// Add, wrapper to call didPanWith in ZoomDismissalInteractionController
func didPanWith(gestureRecognizer: UIPanGestureRecognizer) {
self.interactionController.didPanWith(gestureRecognizer: gestureRecognizer)
}
}

As well, in lodge to implement interactive transitions, you need to implement additional methods of UIViewControllerTransitioningDelegate or UINavigationControllerDelegate.

UIViewControllerTransitioningDelegate

  • func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?
  • func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?

UINavigationControllerDelegate

  • func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?
          extension ZoomTransitionController: UIViewControllerTransitioningDelegate {
...

// Add
func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
if !cocky.isInteractive {
return null
}

self.interactionController.animator = animator
return self.interactionController
}

}

That is the footing for interactive transitions. Afterwards that we just utilise this gesture to really perform this screen transition.

Actually perform interactive transitions with UIPanGesgureRecognizer

To be able to pull the zoomed prototype downwards, you demand to add a UIPanGestureRecognizer to the PageViewController to recognize pan gestures.

Requite UIPanGestureRecognizer to PageViewController from ContainerViewController.

          form ContainerViewController: UIViewController, UIGestureRecognizerDelegate {
override func viewDidLoad() {
...

cocky.panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(didPanWith(gestureRecognizer:)))
self.panGestureRecognizer.consul = cocky
cocky.pageViewController.view.addGestureRecognizer(self.panGestureRecognizer)

...

}
}

Now PageViewController can recognize pan gestures.

The catamenia of the processing part of pan gesture is like follows.

  1. At the start of processing, ready interactive transition
  2. When pan gesture is executing, call the didPanWith method of ZoomTransitionController to process interactive transition
  3. When the gesture is over, make settings to go out the transition with ZoomTransitionController
          @objc func didPanWith(gestureRecognizer: UIPanGestureRecognizer) {
switch gestureRecognizer.country {
case .began:
// ane
cocky.currentViewController.scrollView.isScrollEnabled = false
cocky.transitionController.isInteractive = true
let _ = self.navigationController?.popViewController(animated: true)
case .ended:
// 3.
if cocky.transitionController.isInteractive {
self.currentViewController.scrollView.isScrollEnabled = true
self.transitionController.isInteractive = false
cocky.transitionController.didPanWith(gestureRecognizer: gestureRecognizer)
}
default:
// 2.
if self.transitionController.isInteractive {
self.transitionController.didPanWith(gestureRecognizer: gestureRecognizer)
}
}
}

Summary

Although it may be hard to sympathise since all the source lawmaking is not included in the article, delight try actually running the sample lawmaking.
It is a result of trial and error to realize this transition and interaction, so I think that it will exist helpful.

masamichiueta/FluidPhoto

Thank you!

If you lot similar it, please follow me @masamichiueta

How to Build Animation Transition Like Instagram Stories Swift Ios

Source: https://medium.com/@masamichiueta/create-transition-and-interaction-like-ios-photos-app-2b9f16313d3

Comments




banner



pos populer

R Silver Starlets / SILVER-STARS KHLOE - CHECK SKIRT 1 - 177 AND COVER IMAGEP ...

Download Driver Canon Ip2870 Windows 8.1 / Canon Pixma Ip8720 Driver Printer Download Ij Canon Drivers

2021 Gmc Truck Colors / 2021 Gmc Sierra 1500 Review Autotrader

Featured Post

What Animals Live in Indiana and Not Georgia

Image
What Animals Live in Indiana and Not Georgia Indiana, known as the "Hoosier State," is a midwestern state that is part of the Nifty Lakes Region. Information technology borders Michigan to the north, Ohio to the due east, Illinois to the west, and Kentucky to the Due south. Its major rivers are the Ohio River, Whitewater River, and Wabash River, which is the longest river eastward of the Mississippi. Indiana has a varied topography that includes dense forests, grassland prairies, riverbanks, lakeshores, and rocky hills. Nigh of the land in Indiana is defended to farming. The principal crops are corn, soybeans, wheat, and dairy products. Indiana also has major population centers and many manufacturing industries are based there. It all the same has wild places, however, and they are abode to a healthy population of native wildlife. Wild fauna in Indiana