• Stars
    star
    441
  • Rank 98,861 (Top 2 %)
  • Language
    Swift
  • License
    MIT License
  • Created over 6 years ago
  • Updated almost 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

iOS 11 App Store Transition

iOS 11 App Store Transition

Just another attempt to simulate App Store's Card transition:

demo

Implementation details are available in slides under the MobileConf folder, in the last section ('5 Phases of Interaction').

My previous repo is here. The new one is a total rewrite. It has better effect/performance, better code organization, and has fixes for some issues found in the previous repo.

Overview

All is done with native APIs (UIViewControllerAnimatedTransitioning, etc.), no external libraries. This is NOT a library to install or ready to use, it's an experiementation/demo project to show how such App Store presentation might work.

Features (that you might not know exist)

  • Status bar animation
  • Very responsive card cell highlighting animation
  • Card bouncing up animation (Two animations at work: spring for moving to place, linear for card expansion)
  • Damping and duration depends on how far the card needs to travel on screen
  • Drag down to dismiss when reach the top of content page
    • Scroll back up to cancel the dismissal!
  • Left screen edge pan to dismiss

Interesting code

  • Transition/PresentCardAnimator: Animation code for presentation,
  • Transition/DismissCardAnimator: Animation code for dismissal,
  • Transition/CardPresentationController: Blur effect view and overall aspect of the presentation,
  • ViewControllers/CardDetailViewController: Interactive shrinking pan gesture code.
  • ViewControllers/HomeViewController: Home page, preparation code before presentation is at collectionView's didSelect delegate method.
  • Misc/StatusBarAnimatableViewController: Status bar animation (quick & dirty though, need to inherit from this parent vc.)

TODOs/Defects

  • Fix layout/top area on iPhone X
  • Support continuous video/gif playing from home to detail page (This requires some work to use a whole view controller as a card cell content from the first page!)
  • Add blurry close button at the top right of detail page
  • Perfecting card bouncing up animation (still can't figure out how to achieve that smooth bounciness like the App Store.)

Here are some implementation details:

5 Phases of Interaction

1. Highlighting

  • The card cell needs to be very responsive to touch, so we must set collectionView.delaysContentTouch = false (it's true by default, to prevent premature cell highlighting, e.g., on table view).
  • Put scaling down animation in touchesBegan and touchesCancellled/Ended.
  • .allowsUserInteraction is needed in animation options, so that you can always continue to scroll immediately while the unhighlighted animation is taking place.

2. Before Presenting

  • Need to stop all animations, using cardCell.layer.removeAllAnimations. Also prevent any future highlighting animation with a flag.
  • Get current card frame (that is currently animated scaling down) with cardCell.layer.presentation().frame, then convert it to screen coordinates.
  • Get presented view controller (CardDetailViewController)'s view and position it with AutoLayout at the original card cell's position.
  • Hide original card cell's position.

3. Presenting*

  • Simply animating frame/AutoLayout constraints with Spring animation won't work.
  • Best alternative (that I can think of right now) is to animate with two different animation curves: linear for card expansion, and spring for moving up to place.

Wait, how to animate different AutoLayout constraints with different animation curves?

  • Turns out you can animate different constraints in two animation blocks, like this:
// Animate constraints on the same view with different animation curves
UIView.animate(withDuration: 0.6 * 0.8) {
  self.widthAnchor.constant = 200
  self.heightAnchor.constant = 320
  self.targetView.layoutIfNeeded()
}
UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0, options: [], animations: {
  self.topAnchor.constant = -200
  self.targetView.layoutIfNeeded()
}) { (finished) in ... }

4. Interactively Dismissing

  • Need to handle left screen edge pan and drag down pan.
    • For drag down we'll add a new pan gesture. Make it able to detect simultaneously with scrollView's pan.
      • This means we need to carefully handle when the dragDownMode begins, save the starting drag point to calculate dragging progress, as it usually begins on .change, not .began.
    • For left edge pan just use UIScreenEdgePanGestureRecognizer.
  • Give priority to left edge pan by:
dragDownPan.require(toFail: leftEdgePan)
scrollView.panGestureRecognizer.require(toFail: leftEdgePan)
  • Note that the method a.require(toFail: b) is confusingly named. It actually means a must wait for b to fail first before it can start. So just read it like a.wait(toFail: b) when you see that.
  • To smoothly transition to shrinking mode when reach the top of scroll view, just use scrollView's delegate:
var draggingDownToDismiss = false // A flag to check mode

func scrollViewDidScroll(_ scrollView: UIScrollView) {
  if draggingDownToDismiss || (scrollView.isTracking && scrollView.contentOffset.y < 0) {
    draggingDownToDismiss = true
    scrollView.contentOffset = .zero // * This is important to make it stick at the top
  }
  scrollView.showsVerticalScrollIndicator = !draggingDownToDismiss
}
  • Handle shrinking on drag using UIViewPropertyAnimator:
let shrinking = UIViewPropertyAnimator(duration: 0, curve: .linear, animations: {
  self.view.transform = .init(scaleX: 0.8, y: 0.8)
  self.view.layer.cornerRadius = 16
})
shrinking.pauseAnimation()
  • Carefully handle progress/fractionComplete of the animator by understaning when corresponding gestures are began! Use a combination of gesture.translation(in: _) and gesture.location(in: nil), etc.
  • Reverse animation on drag down pan gesture ended/cancelled:
shrinking!.pauseAnimation()
shrinking!.isReversed = true

// Disable gesture until reverse closing animation finishes.
gesture.isEnabled = false
shrinking!.addCompletion { [unowned self] (pos) in
  self.didCancelDismissalTransition()
  gesture.isEnabled = true
}
shrinking!.startAnimation()

5. Dismissing

  • Just do animation back to original cell's position.

If you're interested in a more visual guide to '5 Phases of Interaction', checkout MobileConf/slides

Weird Bugs

  • This is hard to explain, but there's some space on card view top edge during presentation despite constant 0 of their topAnchors. What's weirder is that it's already unintentionally fixed by setting a top anchor's constant to value >= 1 (or <= -1). Setting it to any values in the range of (-1, 1) doesn't work.
  • Blur effect view in the back seems to not showing up properly when we're in dismissal pan mode (especially on iOS 12). But sometimes it happens on iOS 11 too! Proobably due to my incomplete understanding of viewWillAppear/beginTransition/redraw life cycle.

More Repositories

1

MovingNumbersView

Moving numbers effect in SwiftUI
Swift
278
star
2

RHLinePlot

Line plot like in Robinhood app in SwiftUI
Swift
269
star
3

TableHeaderViewWithAutoLayout

Example of how to use AutoLayout with table header view.
Swift
78
star
4

AppStoreiOS11InteractiveTransition_old

App Store's Card Interactive Transition
Swift
51
star
5

SwiftyLocalization

A simple localization solution for iOS. Google spreadsheets ~> Localizable.strings ~> Swift's struct.
Python
40
star
6

daily-p5

p5.js development gallery using React
JavaScript
36
star
7

NestedDecodable

Decode deeply nested model with keyPath
Swift
15
star
8

TestButtonOnTabBar

Example of how to create button at the center of UITabBar like Facebook Messenger
Swift
12
star
9

next-p5-template

p5.js template built with Next.js
JavaScript
11
star
10

WirelessParking

iOS UDP broadcast using CocoaAsyncSocket
Swift
11
star
11

ViewElements_Old

Build UITableView declaratively by composing elements.
Swift
6
star
12

CreativeSwift

Creative coding for iOS in Swift
Swift
5
star
13

simple-http-server

Build a Simple HTTP Server in Python
Python
5
star
14

cse120pa4tests

CSE120 PA4 Tests
C
5
star
15

CORSaliency

Corner-based Saliency Model
Python
4
star
16

ViewElements

Build page in iOS apps declaratively
Swift
4
star
17

cse120pa3tests

Testing utilities for PA3
C
3
star
18

EarlGrey-Convenience

Convenience functions for EarlGrey UI testing
Swift
3
star
19

EmojiFaces

Replace faces in a photo with emoji.
JavaScript
3
star
20

open-ideas

A place to talk
JavaScript
2
star
21

FindBundit-iOS

The iOS app to find graduate's location.
Swift
2
star
22

Oreo

Cryptocurrency Portfolio in iOS
Swift
2
star
23

Just

A lightweight URLSession wrapper that does only GET and POST
Swift
2
star
24

SecurityUtil

HMAC with SHA256 and AES-256 CBC for Swift (compatible with PHP)
Swift
1
star
25

hackernews-react-apollo

Made from doing https://www.howtographql.com/ tutorial
JavaScript
1
star
26

platonos

JavaScript
1
star
27

os-tutorial

My study on "Writing a Simple Operating System from Scratch" by Nick Blundell
Assembly
1
star
28

AnimatableStatusBarViewController

A UIViewController base class to enable status bar animation
Swift
1
star
29

shills.lol

The place to shill
JavaScript
1
star
30

vanilla-rnn-pytorch

Build Vanilla RNN in PyTorch
Jupyter Notebook
1
star
31

siteleaf-demo

1
star
32

study-kernel-perceptron-svm

Study on kernel perceptron, multi-class perceptron
Jupyter Notebook
1
star
33

RxViewElementsExperiment

Expermenting ViewElements+RxSwift for data-driven UI
Swift
1
star
34

Resume

1
star