{"id":2882,"url":"https://github.com/HelloElephant/Parade","last_synced_at":"2025-08-03T12:31:44.731Z","repository":{"id":56909963,"uuid":"130887023","full_name":"HelloElephant/Parade","owner":"HelloElephant","description":"Parallax Scroll-Jacking Effects Engine for iOS / tvOS","archived":false,"fork":false,"pushed_at":"2018-04-30T15:31:53.000Z","size":29049,"stargazers_count":772,"open_issues_count":1,"forks_count":42,"subscribers_count":22,"default_branch":"master","last_synced_at":"2024-11-18T03:48:29.275Z","etag":null,"topics":["animation-library","ios","ios-ui","parallax","swift","swift-framework","swift-library","tvos"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/HelloElephant.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-04-24T16:58:47.000Z","updated_at":"2024-10-31T07:48:12.000Z","dependencies_parsed_at":"2022-08-20T19:40:07.268Z","dependency_job_id":null,"html_url":"https://github.com/HelloElephant/Parade","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HelloElephant%2FParade","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HelloElephant%2FParade/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HelloElephant%2FParade/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HelloElephant%2FParade/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HelloElephant","download_url":"https://codeload.github.com/HelloElephant/Parade/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228543186,"owners_count":17934441,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["animation-library","ios","ios-ui","parallax","swift","swift-framework","swift-library","tvos"],"created_at":"2024-01-05T20:16:25.347Z","updated_at":"2024-12-07T00:31:05.970Z","avatar_url":"https://github.com/HelloElephant.png","language":"Swift","readme":"# Parade\n\n[![Build Status](https://travis-ci.org/HelloElephant/Parade.svg?branch=master)](https://travis-ci.org/HelloElephant/Parade)\n[![codecov.io](https://codecov.io/gh/HelloElephant/Parade/branch/master/graphs/badge.svg)](https://codecov.io/gh/HelloElephant/Parade/branch/master)\n[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)]()\n[![Cocoapods Compatible](https://img.shields.io/badge/pod-v1.0.0-blue.svg)]()\n[![Platform](https://img.shields.io/badge/platform-ios%20%7C%20tvos-lightgrey.svg)]()\n[![License](https://img.shields.io/badge/license-MIT-343434.svg)]()\n\n# Introduction\n\nCommunicating to cells inside of UICollectionViews, UITableViews, or UIScrollViews, has always been a challenge. It almost always relies on a messy process of trying to relay the scroll to progress to cells in triggering special scrolling effects. We’ve designed this framework to minimize the effort needed to animate views. With a simple blocks-based builder we’ve made it easy to define view states—**from** where they appear and where they will disappear **to**.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg align=\"center\"  src=\"/Documentation/assets/03_state_changes_intro.png?raw=true\"/\u003e\n  \u003c/p\u003e\n\n# Features\n\n  - Supports UICollectionView, UITableView, UIScrollview\n  - Simple Blocks-Based Syntax\n  - Minimal Integration Requirements\n  - Supports Chaining Animatable Views\n  - Adjustable Progress Ranges\n  - 46 Different Parametric Curves\n\n\n# Interactive Demo\n\nThere is a demo app included as part of the project that contains the following implemented examples for the following scrolling effects within the animated gif below.\n\n|  | Included Examples |\n| ------------- | ------------- |\n|![alt tag](/Documentation/assets/08_animated_demo.gif?raw=true)|The demo contains a single ``ParallaxImageViewController`` as the root, and displays these cells as examples in the following order:\u003cbr/\u003e\u003cbr/\u003e- [``ParallaxIntroCollectionViewCell``](https://github.com/HelloElephant/Parade/blob/master/Parade-Demo/Parade-Demo/ParallaxCells/ParallaxIntroCollectionViewCell.swift#L87) : Scale / Transform / Alpha\u003cbr/\u003e\u003cbr/\u003e- [``ParallaxScaleCollectionViewCell``](https://github.com/HelloElephant/Parade/blob/master/Parade-Demo/Parade-Demo/ParallaxCells/ParallaxScaleCollectionViewCell.swift#L62) : Scale / Center\u003cbr/\u003e\u003cbr/\u003e- [``ParallaxDoubleImageCollectionViewCell``](https://github.com/HelloElephant/Parade/blob/master/Parade-Demo/Parade-Demo/ParallaxCells/ParallaxDoubleImageCollectionViewCell.swift#L98) : Center / Transform\u003cbr/\u003e\u003cbr/\u003e- [``ParallaxImageAppearCollectionViewCell``](https://github.com/HelloElephant/Parade/blob/master/Parade-Demo/Parade-Demo/ParallaxCells/ParallaxImageAppearCollectionViewCell.swift#L115) : Animation Chain Parallax\u003cbr/\u003e\u003cbr/\u003e- [``ParallaxImageCollectionViewCell``](https://github.com/HelloElephant/Parade/blob/master/Parade-Demo/Parade-Demo/ParallaxCells/ParallaxImageCollectionViewCell.swift#L61) : Scale / Alpha / Transform \u003cbr/\u003e\u003cbr/\u003e- [``ParallaxTheEndCollectionViewCell``](https://github.com/HelloElephant/Parade/blob/master/Parade-Demo/Parade-Demo/ParallaxCells/ParallaxTheEndCollectionViewCell.swift#L81) : Transform 3D\u003cbr/\u003e\u003cbr/\u003eNote: The examples also  contain custom ranges discussed later in the documentation.\u003cbr/\u003e|\n\n# Installation\n\n* **Requirements** : XCode 9.0+, iOS 10.0+, tvOS 10.0+\n* [Installation Instructions](/Documentation/installation.md)\n* [Release Notes](/Documentation/release_notes.md)\n\n# Communication\n\n- If you **found a bug**, or **have a feature request**, open an issue.\n- If you **want to contribute**, review the [Contribution Guidelines](/Documentation/CONTRIBUTING.md), and submit a pull request.\n- If you **contribute**, please ensure that the there is **100%** code coverage\n\n# Basic Use\n\nAt its core, this framework defines **start** and **end** states for your animatable views—with the scroll view interpolating between the states accordingly. The **start** state defines where views appear from as they scroll onto screen—while **end** state defines where views will disappear.\n\nThe only requirements beyond defining the states is to implement the PDAnimatableType on views to animate, the rest is seamless. Each animation consists of a **start** or **end** state which is defined by the developer—plus a **snapshot** state that is automatically configured the first time the view begins to scroll.\n\nBefore creating, consider the diagram below to get a sense of the coordinate space that the progress is calculated against. Progress is relative to the bounds of the scrollview itself. When scrolling horizontally—the difference in X value to the center point of the viewport is equal to the width of the scrollview itself. Just as the difference in Y value when scrolling vertically, as demonstrated below.\n\n\n![alt tag](/Documentation/assets/02_all_ranges.png?raw=true)\n\n**NOTE :** There is control to adjust these ranges, and instructions can be found in the *Bounding Progress Range* section below.\n\n## Initialization\n\nInitialize the Parade in ``application(:didFinishLaunchingWithOptions:)`` when the application is launched. Once initialized, the base UIScrollView will begin communicating scrolling progress to animatable views contained within.\n\n```swift\nUIScrollView.initializeParade()\n```\n\n## Create Animatable View\n\nThe first step is to make a view animatable by implement the ``PDAnimatableType`` protocol, and the scrollview will begin to communicate progress to it's subviews accordingly.\n\n```swift\npublic protocol PDAnimatableType {\n\n    // The progress animator definition that\n    // interpolates over animatable properties\n    func configuredAnimator() -\u003e PDAnimator;\n}\n```\n\nAny of the following views, and their subviews, can implement the ``PDAnimatableType`` and be animated:\n\n- `UICollectionViewCell`\n- `UITableViewCell`\n- `UIScrollView`'s subviews\n\nBut before the scrollview can communicate the progress, define an animator with the relative scroll direction that the scrollview will be tracking against. Bundled with the framework is recursive blocks based builder, that allows for simple creation of complex animations to interpolate against while scrolling.\n\n#### Vertical Direction Scroll Animator\n\nThe following is an example to create a vertical animator for scrolling vertically. The closure returns an animator that can be used to create from, and to, state for any specific view within the hierarchy.\n\n```swift\nfunc configuredAnimator() -\u003e PDAnimator {\n\n   /* Create a vertical tracking animator call this class method */\n   return PDAnimator.newVerticalAnimator { (animator) in\n\n   }\n}\n```\n#### Horizontal Direction Scroll Animator\n\nThe following is an example to create a horizontal animator for scrolling horizontally. The closure returns an animator that can be used to create from, and to, state for any specific view within the hierarchy.\n\n```swift\nfunc configuredAnimator() -\u003e PDAnimator {\n\n   /* Create a horizontal tracking animator with this class method */\n   return PDAnimator.newHorizontalAnimator { (animator) in\n\n   }\n}\n```\n\nThe closure returns an animator that can be used to create from, and to, states for a specific view accordingly.\n\n## Configuring Animation States\n\n#### Configure End State\n\nTo have a view fade in relative to the scrolling progress, define a start state by calling the `startState(for:)` method as follows. Each time a state is added, it returns a state maker that can be used to append multiple properties to interpolate over.\n\n```swift\nfunc configuredAnimator() -\u003e PDAnimator {\n\n    let offScreenAlpha : CGFloat = 0.0\n\n    return PDAnimator.newVerticalAnimator { (animator) in\n\n        animator.startState(for: animatedImageView, { (s) in\n          s.alpha(offScreenAlpha)\n        })\n     }\n}\n```\n#### Configure End State\n\nBuilding on the last example, to have a view fade out relative to the scrolling progress off the screen, define an end state by calling the `endState(for:)` method as follows.\n\n```swift\nfunc configuredAnimator() -\u003e PDAnimator {\n\n    let offScreenAlpha : CGFloat = 0.0\n\n    return PDAnimator.newVerticalAnimator { (animator) in\n\n        animator.startState(for: animatedImageView, { (s) in\n          s.alpha(offScreenAlpha)\n        }).endState(for: animatedImageView, { (s) in\n          s.alpha(offScreenAlpha)\n        })\n     }\n}\n```\n\n#### Configure Start \u0026 End State\n\nBuilding on the prior example, it appears that the appearing and disappearing alpha is the same. In the case both **start**, and **end** state values are the same, use the `startEndState(for:)` method to set the value for both states simultaneously.\n\n```swift\nfunc configuredAnimator() -\u003e PDAnimator {\n\n    let offScreenAlpha : CGFloat = 0.0\n\n    return PDAnimator.newVerticalAnimator { (animator) in\n\n        animator.startEndState(for: animatedImageView, { (s) in\n          s.alpha(offScreenAlpha)\n        })\n     }\n}\n```\n\n#### Start \u0026 End State for Multiple Views\n\nIn the case there are multiple views that need to perform the same animation, helper methods have been defined for creating animations for an array of views too. The following is an example of how these can be used.\n\n```swift\nfunc configuredAnimator() -\u003e PDAnimator {\n\n    var animatedViews = [view1, view2, view3, view4]\n\n    let offScreenAlpha : CGFloat = 0.0\n\n    return PDAnimator.newVerticalAnimator { (animator) in\n\n        animator.startEndState(forViews: animatedViews, { (s) in\n          s.alpha(offScreenAlpha)\n        })\n     }\n}\n```\n\nThe following can also be used with multiple views.\n\n```swift\nfunc startState(forViews views:  [UIView], _ callback : ... )    -\u003e PDAnimationMaker\nfunc endState(forViews views:  [UIView], _ callback : ... )      -\u003e PDAnimationMaker\nfunc startEndState(forViews views:  [UIView], _ callback : ... ) -\u003e PDAnimationMaker\n```\n\n#### State Properties\n\nThe framework provides quite a few helpers to define state properties for views, and/or, their backing layer with ease.\n\n```swift   \n/* View Property Setters */\n\npublic func alpha(_ value : CGFloat)               -\u003e PDAnimatablePropertyMaker\npublic func backgroundColor(_ value : UIColor)     -\u003e PDAnimatablePropertyMaker\npublic func bounds(_ value : CGSize)               -\u003e PDAnimatablePropertyMaker\npublic func center(_ value : CGPoint)              -\u003e PDAnimatablePropertyMaker\npublic func size(_ value : CGSize)                 -\u003e PDAnimatablePropertyMaker\npublic func transform(_ value : CGAffineTransform) -\u003e PDAnimatablePropertyMaker\n\n/* Layer Property Setters */\n\npublic func borderColor(_ value : UIColor)         -\u003e PDAnimatablePropertyMaker\npublic func borderWidth(_ value : CGFloat)         -\u003e PDAnimatablePropertyMaker\npublic func contentsRect(_ value : CGRect)         -\u003e PDAnimatablePropertyMaker\npublic func cornerRadius(_ value : CGFloat)        -\u003e PDAnimatablePropertyMaker\npublic func shadowColor(_ value : UIColor)         -\u003e PDAnimatablePropertyMaker\npublic func shadowOffset(_ value : CGSize)         -\u003e PDAnimatablePropertyMaker\npublic func shadowOpacity(_ value : CGFloat)       -\u003e PDAnimatablePropertyMaker\npublic func shadowRadius(_ value : CGFloat)        -\u003e PDAnimatablePropertyMaker\npublic func transform3D(_ value : CATransform3D)   -\u003e PDAnimatablePropertyMaker\npublic func zPosition(_ value : CGFloat)           -\u003e PDAnimatablePropertyMaker\n```\n\nIn the case a setter is not defined, and there is a need to set a specific property to interpolate between, there are two defined setters that use KVC for views, and/or, their backing layer accordingly.\n\n```swift\npublic func viewValue(_ value : Any?, forKey key : String)    -\u003e PDAnimatablePropertyMaker\npublic func layerValue(_ value : Any?, forKey key : String)   -\u003e PDAnimatablePropertyMaker\n```\n#### Parametric Easing\n\nThere are 46 different parametric curves that can be applied to the interpolation of uniquely per property. The framework comes bundled with the following supported parametric curves that can be applied to each property individually.\n\n\u003ctable\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \t\u003ctd\u003e\n       \t\t .inSine\u003cbr\u003e\n       \t\t .inOutSine\u003cbr\u003e\n       \t\t .outSine\u003cbr\u003e\n       \t\t .outInSine\u003c/td\u003e\n      \t\u003ctd\u003e\n       \t\t .inQuadratic\u003cbr\u003e\n       \t\t .inOutQuadratic\u003cbr\u003e\n       \t\t .outQuadratic\u003cbr\u003e\n      \t\t .outInQuadratic\u003c/td\u003e\n   \t  \t\u003ctd\u003e\n   \t  \t\t .inCubic\u003cbr\u003e\n   \t  \t\t .inOutCubic\u003cbr\u003e\n   \t  \t\t .outCubic\u003cbr\u003e\n   \t  \t\t .outInCubic\u003c/td\u003e\n        \u003ctd\u003e\n      \t\t .inQuartic\u003cbr\u003e\n      \t\t .inOutQuartic\u003cbr\u003e\n      \t\t .outQuartic\u003cbr\u003e\n      \t\t .outInQuartic\u003c/td\u003e\n       \u003ctd\u003e\n      \t\t .inQuintic \u003cbr\u003e\n      \t\t .inOutQuintic\u003cbr\u003e\n      \t\t .outQuintic\u003cbr\u003e\n      \t\t .outInQuintic\u003c/td\u003e\n       \u003ctd\u003e\n         .inAtan\u003cbr\u003e\n    \t\t .inOutAtan\u003cbr\u003e\n         .outAtan\u003cbr\u003e*\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e    \t\n      \t\u003ctd\u003e\n    \t\t   .inExponential\u003cbr\u003e\n     \t\t   .inOutExponential\u003cbr\u003e\n    \t\t   .outExponential\u003cbr\u003e\n    \t\t   .outInExponential\u003c/td\u003e\n       \u003ctd\u003e\n      \t\t .inCircular \u003cbr\u003e\n      \t\t .inOutCircular\u003cbr\u003e\n      \t\t .outCircular\u003cbr\u003e\n      \t\t .outInCircular\u003c/td\u003e\n    \t\u003ctd\u003e\n    \t\t .inBack \u003cbr\u003e\n    \t\t .inOutBack\u003cbr\u003e\n    \t\t .outBack\u003cbr\u003e\n    \t\t .outInBack\u003c/td\u003e\n       \u003ctd\u003e\n    \t\t .inElastic \u003cbr\u003e\n    \t\t .inOutElastic\u003cbr\u003e\n    \t\t .outElastic\u003cbr\u003e\n    \t\t .outInElastic \u003c/td\u003e\n       \u003ctd\u003e\n     \t\t .inBounce\u003cbr\u003e\n     \t\t .inOutBounce\u003cbr\u003e\n      \t .outBounce\u003cbr\u003e\n      \t .outInBounce\u003c/td\u003e\n      \u003ctd\u003e\n      \t .linear\u003cbr\u003e\n      \t .smoothStep\u003cbr\u003e\n      \t .smootherStep\u003cbr\u003e*\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\nJust append it to the state's property definition while building the view's state. A good reference for some of the supported parametric curves can be found [here](http://easings.net/)\n\n```swift\nfunc configuredAnimator() -\u003e PDAnimator {\n\n    let offScreenAlpha : CGFloat = 0.0\n    let offScreenTransform = CGAffineTransform.identity.scaledBy(x: 0.5, y: 0.5)\n\n    return PDAnimator.newVerticalAnimator { (animator) in\n\n        animator.startEndState(for: animatedImageView, { (s) in\n          s.alpha(offScreenAlpha).easing.(.inSine)\n          s.transform(offScreenTransform).easing(.inOutCubic)\n        })\n     }\n}\n```\n## Advanced View Animations\n\n#### Chaining Animations\n\nAnimation progress does not have to apply only to the top level view that is being scrolled. If a subview implements the ``PDAnimatableType``, and is part of the subview hierarchy, attaching it to the animator can create a chain.\n\nThe animator will then communicate the progress to the subview's animator, subviews can be attached to subviews, or even have the subviews attach to their subviews, to create chains that can traverse multiple levels in the end to make some fun effects. To attach an animatable view, call the `attachAnimatableView(:)` method as follows.\n\n```swift\nfunc configuredAnimator() -\u003e PDAnimator {\n\n    let offScreenAlpha : CGFloat = 0.0\n    let offScreenTransform = CGAffineTransform.identity.translatedBy(x: 0.0, y: 100.0)\n\n    return PDAnimator.newVerticalAnimator { (animator) in\n\n        animator.startEndState(for: animatedImageView, { (s) in\n          s.transform(offScreenTransform).easing(.inOutCubic)\n        }).attachAnimatableView(animatedImageView)\n     }\n}\n```\n\n#### Bounding Progress Range\n\nThere is sometimes a need to adjust where the progress actually takes place. This is especially useful when animatable view is smaller than the bounding size of the scrollview. Observe the examples of the ranges defined, and how it effects the progress. By defining a range, this is basically telling the scrollview where the progress counts from 0 to 100.\n\n![alt tag](/Documentation/assets/03_range_progress.png?raw=true)\n\nRanges can be defined on a per property basis just like easing – thus allowing for different properties interpolating over different coordinate spaces. The following example defines two different ranges for each property, and can visually be referenced above as to where the interpolation will take place.\n\n\n```swift\nfunc configuredAnimator() -\u003e PDAnimator {\n\n    let offScreenAlpha : CGFloat = 0.0\n    let offScreenTransform = CGAffineTransform.identity.translatedBy(x: 0.0, y: 100.0)\n\n    return PDAnimator.newVerticalAnimator { (animator) in\n\n        animator.startState(for: animatedImageView, { (s) in\n          s.transform(offScreenTransform).easing(.inOutCubic).range(0.5...1.0)\n          s.alpha(offScreenTransform).easing(.inOutCubic).range(0.25...0.75)\n        })\n     }\n}\n```\n\n## License\n\n*Parade is released under the MIT license. See [License](https://github.com/HelloElephant/Parade/blob/master/LICENSE) for details.*\n","funding_links":[],"categories":["UI","HarmonyOS"],"sub_categories":["Table View / Collection View","Windows Manager"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FHelloElephant%2FParade","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FHelloElephant%2FParade","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FHelloElephant%2FParade/lists"}