Coder Social home page Coder Social logo

willowtreeapps / spruce-ios Goto Github PK

View Code? Open in Web Editor NEW
3.4K 81.0 204.0 15.3 MB

Swift library for choreographing animations on the screen.

License: MIT License

Swift 95.08% Ruby 4.26% Objective-C 0.54% Shell 0.12%
animation ios framework swift design spruce library

spruce-ios's Introduction

Spruce Logo

Spruce iOS Animation Library (and Android)

CircleCI Build Status Coverage Status Carthage compatible CocoaPods compatible License MIT Public Yes

What is it?

Spruce is a lightweight animation library that helps choreograph the animations on the screen. With so many different animation libraries out there, developers need to make sure that each view is animating at the appropriate time. Spruce can help designers request complex multi-view animations and not have the developers cringe at the prototype.

Here is a quick example of how you can Spruce up your screens!

Example

Installation Instructions

To make everything super easy, we currently support both CocoaPods and Carthage. If you want to just do everything yourself without a package manager, checkout our releases page and download the pre-built frameworks there or you can download the exact source from this Github project.

CocoaPods

Add the following to your Podfile.

pod "Spruce", '~> 1.0.0'

Carthage

Add the following to your Cartfile.

github "willowtreeapps/spruce-ios"

Getting Started

Spruce is entirely written in Swift and currently only supports Swift. Objective C wrappers are coming soon.

Basic Usage

Spruce comes packed with UIView extensions meant to make your life easier when calling an animation. Let's say we want to [.fadeIn, .expand(.slightly)] our view. We will call that array of animations ourAnimations.

Preparing for Animation

If you want a view to fade in, then you need to make sure that it is already faded out. To do that, we need to prepare the view. Spruce makes this easy by calling:

yourView.spruce.prepare(ourAnimations)

This prepare function will go through each view and set the alpha = 0.0 and also shrink the view so that when the animation runs the view will revert to it's original position.

Running the Animation

Use the following command to run a basic animation on your view.

yourView.spruce.animate(ourAnimations)

Checkout the documentation for more functions and how to better use the animate method.

Using a SortFunction

Luckily, Spruce comes with around 8 SortFunction implementations with a wide open possibility to make more! Use the SortFunction to change the order in which views animate. Consider the following example:

let sortFunction = LinearSortFunction(direction: .topToBottom, interObjectDelay: 0.1)

In this example we have created a LinearSortFunction which will have views animate in from the top to bottom. We can change the look and feel of the animation by using a RadialSortFunction instead which will have the views animate in a circular fashion. If we wanted to use this sortFunction in an actual Spruce animate call then that would look something like:

yourView.spruce.animate([.fadeIn, .expand(.slightly)], sortFunction: sortFunction)

Definitely play around with the stock SortFunction implementations until you find the one that is perfect for you! Check out the example app if you want to get previews of what each SortFunction will look like.

Using a Custom Animation

Though Spruce comes with a ton of stock animations, sometimes it is easier to make your own. We definitely encourage customization and Spruce is ready for it! Let's say you want to transform and animate a UIView object. To do let's create a PrepareHandler:

let prepareHandler = { view in
	view.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
}

The prepareHandler will be passed a UIView object by Spruce and then it is your functions job to get the view ready to animate. This way our animation will look clean and quick since the view is already scaled down and ready to grow! Now to setup the function to grow the view we need to create a ChangeFunction:

let changeFunction = { view in
	view.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
}

In changeFunction the same view will also be passed in by Spruce and then your function will be used to animate the actual view itself. These two functions will be called with each subview of the view you are animating. Now that we have both functions we are ready to create an animation:

let animation = StockAnimation.custom(prepareFunction: prepareHandler, animateFunction: changeFunction)

Once we have the animation all we need to do is pass that animation into Spruce and let the animation begin!

yourView.spruce.animate([animation])

Basic Concepts

Animations

Given a change function that specifies how the views are modified, you are able to specify any type of animation that you would like. Feel free to implement the Animation protocol and create your own animation classes.

The Animation Protocol

The protocol has one function and a variable that need to be implemented in your class. First is the changeFunction. This is a void function that takes one parameter of a UIView. The change function will specify all of the modifications that are going to be made to that view and this is what you would use to animate the changes. The function animate is called when Spruce wants to go ahead and run the animations on the view. It's important that the changeFunction is set before this call but Spruce should handle all of that for you. The completion parameter in the function call should be called by your function once the animation is complete.

var changeFunction: ChangeFunction? { get set }

func animate(delay: TimeInterval, view: UIView, completion: CompletionHandler?)

Standard Animation

The StandardAnimation class uses the default UIView.animate function to apply the change function to the view. Use this class if you want to have a stock linear movement of the changes.

Spring Animation

The SpringAnimation class uses the UIView.animate(...usingSpringWithDamping) function. With this class you can edit the springDampening and initialVelocity values so that your views will bounce on the screen.

Sort Functions

With all different types of animations, especially those dealing with subviews, we have to consider a way in which we want to animate them. Some views can have 0 subviews while others may have hundreds. To handle this, we have the notion of a SortFunction. What this will do is take each of the subviews in the animated view, and apply a mapping from the specific subview to the exact delay that it should wait before animating. Some of these will sort in a radial formation while others may actually sort randomly. This is one of the cool features of Spruce, is you can actually define your own SortFunction and then the animation will look completely different. Luckily, Spruce also comes jam packed with a ton of default SortFunction classes to make everything easier on you as the developer. Take a look at some of the default SortFunction classes we have and see if you can use them or branch off of them for your cool and custom animations!

The SortFunction Protocol

A very simple protocol that requires classes to implement the following function

func timeOffsets(view: UIView, recursiveDepth: Int) -> [TimedView]

What the above function needs to do is take in a UIView and generate a list of subviews. This list of subviews can be generated recursively or not depending on what the boolean has set. Once the list of subviews has been generated, you can define your own sort metric to determine in which order the UIView's should animate. To do so, you need to create an array of SpruceTimedView's. This special struct has two properties: (1) view: UIView and (2) timeOffset: TimeInterval. Your SortFunction can define the timeOffset however it likes, but the animation classes will use this to determine how long it should delay the start of that specific view from animating. The best way to learn, is to play around. So why not have some fun and make your own SortFunction!

Stock Sort Functions

To make sure that developers can use Spruce out of the box, we included about 8 stock SortFunction implementations. These are some of the main functions we use at WillowTree and are so excited to see what others come up with!

  • DefaultSortFunction
  • LinearSortFunction
  • CorneredSortFunction
  • RadialSortFunction
  • RandomSortFunction
  • InlineSortFunction
  • ContinousSortFunction
  • ContinuousWeightedSortFunction

Check out the docs here for more information

Stock Animations

To make everybody's lives easier, the stock animations perform the basic UIView animations that a lot of apps use today. Mix and match these animations to get the core motion you are looking for.

  • .fadeIn
  • .slide(<SlideDirection>, <Distance>)
  • .spin(<Angle>)
  • .expand(<Scale>)
  • .contact(<Scale>)
  • .custom(prepareFunction: <PrepareHandler>, animateFunction: <ChangeFunction>)

Experiment which ones work for you! If you think of anymore feel free to add them to the library yourself!

Example App

Use the example app to find the right SortFunction. In the app you will be able to see how each SortFunction is implemented. As you swap among SortFunction types, the code will be displayed on the phone and printed to the Xcode console so that you can start adding Spruce to your own app right away!

Also included in the sample app are the implementations for the two extensibility tests shown above that demonstrate how to use Spruce with a UITableView or UICollectionView.

Contributing to Spruce

Contributions are more than welcome! Please see the Contributing Guidelines and be mindful of our Code of Conduct.

Issues or Future Ideas

If part of Spruce is not working correctly be sure to file a Github issue. In the issue provide as many details as possible. This could include example code or the exact steps that you did so that everyone can reproduce the issue. Sample projects are always the best way :). This makes it easy for our developers or someone from the open-source community to start working!

If you have a feature idea submit an issue with a feature request or submit a pull request and we will work with you to merge it in!

About WillowTree!

WillowTree Logo

We build apps, responsive sites, bots—any digital product that lives on a screen—for the world’s leading companies. Our elite teams challenge themselves to build extraordinary experiences by bridging the latest strategy and design thinking with enterprise-grade software development.

Interested in working on more unique projects like Spruce? Check out our careers page.

spruce-ios's People

Contributors

chrsmys avatar eriklamanna avatar jacksontaylor13 avatar mhk4g avatar rafcabezas avatar thisisandrewh avatar zackdotcomputer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spruce-ios's Issues

Right to Left Sorting Not Working

I am trying to apply sort on tableview cells from right to left but not working

let sortFunction = LinearSortFunction(direction: .topToBottom, interObjectDelay: 0.2)
table.spruce.animate([.fadeIn], sortFunction: sortFunction)

User can type/delete on the sort function field

When selecting the Sort Function, a cursor is present in field allowing the user to type or delete. It doesn't affect the select, since the wheel stays up until a choice is made, and the choice overrides any typing, but the field should not allow for typing.

Standar custom animation doesn't work

Hi, i have downloaded the example app and i put as rootViewController of the window yours ExampleViewController where i put a simple square red view ( code above ). Nothings happen, why?
`
import UIKit
import Spruce

class ExampleViewController: UIViewController {
let animations: [StockAnimation]
var sortFunction: SortFunction?
var animationView: UIView? = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
var timer: Timer?

init(animations: [StockAnimation], nibName: String?) {
    self.animations = [.fadeIn, .expand(.slightly)]

    animationView?.isUserInteractionEnabled = true
    super.init(nibName: nibName, bundle: nil)
    animationView?.backgroundColor = .red
    view.addSubview(animationView!)
    
    view.frame = UIScreen.main.bounds
    view.backgroundColor = .blue
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
    super.viewDidLoad()
    setup()
    
    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(prepareAnimation))
    animationView?.addGestureRecognizer(tapGesture)
}

func setup() {

}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    
    prepareAnimation()
}

func prepareAnimation() {
    animationView?.spruce.prepare(with: animations)
    
    timer?.invalidate()
    timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(callAnimation), userInfo: nil, repeats: false)
}

func callAnimation() {
   
    let animation = SpringAnimation(duration: 0.7)
    DispatchQueue.main.async {
        self.animationView?.spruce.animate(self.animations, animationType: animation)
    }
}

}`

Support for Objective-C wrappers in order to integrate with ReactNative

Hi,

First of all I would like to appreciate for provide such a nice, cool library. Using this we can easily animate views.

I have built a ReactNative Bridge Wrapper: RNSpruce for this library. On Android it is working as expected, but I am not able to build the same on iOS since their are no Objective-C wrappers available.

We need these wrappers because in ReactNative library we can create bridge only using Objective-C classes.

Can you please share roadmap of when are you planning to provide Objective-C wrappers. I would have contributed but I don't have depth understanding of creating wrappers.

Thanks
Pranav

StockAnimation listens to transform on prepare, but not animate

The StockAnimation from DefaultAnimations.swift listens to the view's current transform when preparing for any non-fade animation (concatenating the preparations to it) but resets the transform to the identity when performing the animation.

I'd expect the animation block to leave the view in the same state it had before prepare was called.

Skip some views

Hi,
didn't find the way to skip some subviews from animating. Is possible somehow?

For example in a collectionview how do I skip the first row to animate?

Thanks so much, it's an awesome library by the way.

Can't get view to animate

Hi I'm trying to animate an existing UILabel...

I have this in my viewDidLoad method:

HomeView.spruce.prepare(with: [.slide(.right, .severely)])
HomeView.spruce.animate([.slide(.right, .severely)])

But nothing happens when the view loads. I honestly don't understand what I could be doing wrong the API seems very simple. I tried explicitly casting the Label to a UIView even though it inherits from it...
let v = HomeView as UIView

Swift Style Documentation

Add in comments to the code to make using the methods easier to understand when writing the code.

Add MIT License

  • Add the license icon to the top of the README
  • Add a license copy to the bottom of the README
  • Add the license to the top of each code document

Add Contributing.md File

  • Add in specifications for contributing to this repo

  • Discuss how everything is going to be owned by WillowTree

  • Outline that code must be legally able to be open sourced

  • Check other WT open sourced projects to see their Contributing guidelines

Example App: Initial value of slider not matching code snippet

Upon initial launch of the Example App, the code snippet shows a interObjectDelay of (0.025) or 25% of .1s. However, the actual value of the slider is 30%. Once an adjustment is made on the slider, the value and code snippet are in sync - it is just on the initial launch of the app.

Recursive Layering Unit Tests

  • Add unit tests to verify correctness of different view structures
  • Consider scenarios like:
    • All views positioned in the top left corner
    • Views placed outside of the bounds of superview
    • Views that are 3-4 levels deep in the view hierarchy
  • Make sure that the coordinate space of the subviews is accounted for

Recursive Layering Adjust for Coordinate Space

Currently, the recursive layering does not take into account the different in coordinate spaces for each of the subviews found. We need to make sure that all coordinates of each view are considered to be in the selected animationView's coordinate space. This will allow for the best calculation for the sort functions.

[Feature Request] Ability to include section headers in animation

I'm currently encountering an issue trying to use Spruce in an app where the animations don't "look right" when used on a UITableView that includes section headers because the section headers will appear suddenly at the beginning of the animation rather than being faded in with their cells.

Ideally, we'd implement a solution like the one that @Vyax started on in this possibly abandoned PR from 2017. If necessary, I'm happy to take point on updating the PR to merge cleanly with 2.10 base and add this functionality.

Landscape Mode needs to either be disabled or fixed

Currently if a user tries to go to Landscape mode:

  • Right now only rotates right
  • Also borks the views
    If we're going to allow Landscape mode then it needs to properly work for all rotations. The display should be updated when in landscape to have the display area on the left and the settings on the right.

Example App - Cornered and Inline sorts have extra characters for the Name of the Down-Right and Down-Left arrows

In the Example App, the Name field for the down-left (↙) and down-right (↘) arrows for the Corner selection have extra characters for them for the Cornered and Inline sorts. I'm expecting the names to be "↙" and "↘", like they are for Radial, Continuous and Weighted Continuous, but they are actually coming back with a lot of extra spaces - "↙
". The extra spaces should be removed.

Update README

With new changes to the code, we need to make sure:

  • Everything is up to date on the README doc
  • Include new branding for Spruce on the doc
  • Redesign the structure of the document
  • Shift unnecessary fields out of the main README
  • Do subfolder README documentation for certain parts
  • Add in more graphics
  • Real life demos for the homes page

Add in CircleCI

  • Setup CircleCI for the project for running unit tests
  • Add building icon to README

iPad support

On an iPad:
The scrolling customization menu is not sized appropriately on all iPad devices. You can only see 1 line of options at a time

Overall iPad support needs to be added

Code only shows Delay to the hundredths of seconds. Should show to the thousandths.

If I set the delay to the very minimum for most sorts, the display comes in pretty much instantaneously and the Code shows the interObjectDelay of 0.00. However, if I move the Delay button over just slightly, it is usually enough to notice the display doesn't come in instantaneously, but the Code still shows the interObjectDelay of 0.00. The bar must be adjusting by the thousandths of seconds but is not updated in the Code snippet.

Sort Function Unit Tests

  • Add unit tests for each of the default sort functions
  • Each unit test should test:
    • All possible starting points of the sort function
    • For BaseDistanceSortFunction subclasses, unit tests should also check reversed functionality

Is any way to hide the spruce view with animation?

Now, I can show collection view items with animation.
But when I want to close the collection view screen, I want to hide collection view in opposite way with animation.

Is any way to do this?
Thanks a lot!

Update the Code section

The following changes to the code section should be made:

  • Not obvious you can scroll sideways on the code card. Maybe wrap instead?
  • Allow copy paste

View sorting and animation timing should be separate concerns

Currently sort functions are responsible for determining the timing information in an animation. I think that it would be cleaner to separate the two concepts. This would fix a few issues:

  1. Sorting and timing are orthogonal concerns, so separating them would let each piece have a single responsibility.
  2. Currently you can't use "inter-item time" based sort functions with a total duration timing. So it's not possible to have a random animation specified to last 3 seconds. Instead you'd have to do the math yourself knowing the total number of items ahead of time.
  3. Inter-item time is (hypothesis) less useful on average than total duration.

The API could be extended from:

yourView.spruce.animate([.fadeIn, .expand(.slightly)], sortFunction: sortFunction)

to something like

yourView.spruce.animate([.fadeIn, .expand(.slightly)], sortBy: sortFunction, duration: .total(3.0))

or

yourView.spruce.animate([.fadeIn, .expand(.slightly)], sortBy: sortFunction, duration: .withInterItemTime(0.1))

Reproducing the middle animation on the README

First off, this library is awesome and I want to thank everybody who has had a part in making it open source.

I'm trying to reproduce the middle animation on the README file and I can't seem to line up the right attributes to get it to do exactly what it is doing. Any help would be appreciated!

screen shot 2017-03-16 at 11 55 45 am

Can't create custom `SortFunction`s

The current setup of SortFunction.swift makes it impossible to create custom instances of SortFunctions because:

  1. SortFunction is public, implying that the intention is that developers should be able to conform to it and create custom sorts.
  2. SortFunction requires two functions, each of which are expected to return [TimedView].
  3. However, TimedView has two properties, each of which have no access modifiers (and so default to internal), and which has no init method (and so one is autogenerated and defaults to internal).
  4. Because TimedView's init and properties are all internal, they can't be viewed from outside of the compiled target and, thus, it is impossible to construct a new instance of a TimedView. This means it's impossible to make one to return from the required functions and so it is impossible to actually subclass SortFunction.

I recommend we create a public init method for this struct to fix this issue. I'll submit a PR shortly to do that.

Add Extensibility tests

  • Consider different scenarios where Spruce would be used
  • Add examples of:
    • Misaligned content
    • Loading from an API
    • UITableView
    • UICollectionView

Future of project

Is this project still maintained (e.g support for Swift 5) or should developers look for alternatives?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.