Coder Social home page Coder Social logo

calimarkus / jdflipnumberview Goto Github PK

View Code? Open in Web Editor NEW
777.0 64.0 138.0 21.49 MB

[iOS] Animated analog flip numbers like airport/train-station displays (Swift/SwiftUI ready)

License: MIT License

Objective-C 85.43% Ruby 2.43% Swift 12.01% C 0.13%
ios animation cocoapod customizable views cocoapods swiftui

jdflipnumberview's Introduction

JDFlipNumberView & JDFlipImageView (SwiftUI ready)

Target Value Animation Animated Time Display
Target Animation Time Display

The FlipNumberView is simulating an analog flip display (e.g. like those at the airport / train station). It's well abstracted and easy to use. The FlipImageView let's you run a similar animation on any view, e.g. images. See the example project for working examples (You can run pod try JDFlipNumberView to open the example project quickly.). Please open a Github issue, if you think anything is missing or wrong.

Screenshot

Installation using CocoaPods

  • pod 'JDFlipNumberView' all flip views, SwiftUI views & default imageset

To further limit what you depend on, use the following subpods. They don't include the default image bundle, thus they are much more lightweight. See Customization below for infos, on how to use your own images.

  • pod 'JDFlipNumberView/NoImageBundle', everything except the default image bundle
  • pod 'JDFlipNumberView/NoImageBundle-NoSwiftUI', everything except the default image bundle & the SwiftUI views

Even more targeted:

  • pod 'JDFlipNumberView/Core', only the JDFlipNumberView
  • pod 'JDFlipNumberView/FlipImageView', only the JDFlipImageView
  • pod 'JDFlipNumberView/FlipClockView', /Core + JDFlipClockView
  • pod 'JDFlipNumberView/DateCountdownFlipView', /Core + JDDateCountdownFlipView
  • pod 'JDFlipNumberView/DefaultImageBundle', the default image bundle, as it's not included automatically in the other subpods

(For infos on cocoapods, have a look the cocoapods website)

Manual Installation

  1. Add all files (or only the ones you want to) from JDFlipNumberView/JDFlipNumberView/*.{h,m} to your project
  2. Add the JDFlipNumberView.bundle, if you want to use the default images

Contents

The main classes are

  • JDFlipNumberView (SwiftUI: FlipNumberView)

    • The Standard FlipNumberView. It shows an integer value as FlipView. It has a choosable amount of digits. Can be animated in any way described in this document.
  • JDFlipImageView (SwiftUI: FlipImageView)

    • An Image View with flip animations. Use it like a regular UIImageView, but set new images animated via setImageAnimated:duration:completion:
  • JDDateCountdownFlipView (SwiftUI: DateCountdownFlipView)

    • A date countdown. Create it with a target date and it will display an animated flipping countdown showing the remaining days, hours, minutes and seconds until that date.
  • JDFlipClockView (SwiftUI: FlipClockView)

    • A digital clock. Displays the current hour and minutes as animated flip views. Seconds can also be enabled. Always shows the current time.
  • UIView+JDFlipImageView

    • A UIView category that makes it possible to transition between any two views using a flip animation.

Usage Example

After installing you only need to follow some simple steps to get started. Here is a working example: A 4-digit FlipNumberView animating down once every second.

Objective-C + UIKit:

// create a new FlipNumberView, set a value, start an animation
JDFlipNumberView *flipNumberView = [[JDFlipNumberView alloc] initWithInitialValue: 1337];
[flipNumberView animateDownWithTimeInterval: 1.0];

// add to view hierarchy and resize
[self.view addSubview: flipNumberView];
[flipNumberView sizeToFit];
flipNumberView.center = self.view.center;

In SwiftUI it's even simpler:

struct SwiftExample: View {
    @State var value = 1337

    var body: some View {
        FlipNumberView(value: $value, animationStyle: .interval(interval: 1.0, direction: .down))
          .frame(height: 80)
    }
}

That's it. This will display a working, flipping, animating countdown view!
See the example project for other examples.

Available animations

  • Simple (a single flip):
- (void)setValue:(NSInteger)newValue animated:(BOOL)animated;
- (void)animateToNextNumber;
- (void)animateToPreviousNumber;
  • Continuous (Flipping through all numbers, until target is reached):
- (void)animateToValue:(NSInteger)newValue duration:(CGFloat)duration;
  • Interval (A timed animation without a target value, flipping once per timeInterval):
- (void)animateUpWithTimeInterval:(NSTimeInterval)timeInterval;
- (void)animateDownWithTimeInterval:(NSTimeInterval)timeInterval;

In SwiftUI, see these:

public enum FlipNumberViewAnimationStyle {
    case none
    case simple
    case continuous(duration: Double)
    case interval(interval: Double, direction: FlipNumberViewIntervalAnimationDirection)
}

Customization

A) Replace original images
Replace the images within the JDFlipNumberView.bundle. (In the Finder just Rightclick > Show Contents to see the images.)

When using Pods, make sure to use pod 'JDFlipNumberView/Core', so the default bundle won't be copied. Just create a bundle named JDFlipNumberView.bundle in your project yourself.

B) Use multiple bundles
Add another graphics bundle to your project. A bundle is nothing else, than a regular folder, but named with .bundle as extension. You need one image per digit. 0.png, 1.png, 2.png, etc. See the next section on how to use multiple bundles.

Implementing a custom bundle

To use your own bundles, use the imageBundle: initializers:

[[JDFlipNumberView alloc] initWithDigitCount:<#count#> 
                                 imageBundle:[JDFlipNumberViewImageBundle imageBundleNamed:@"<#imageBundleName#>"]];
[[JDDateCountdownFlipView alloc] initWithDayDigitCount:<#count#> 
                                           imageBundle:[JDFlipNumberViewImageBundle imageBundleNamed:@"<#imageBundleName#>"]];

SwiftUI:

FlipNumberView(value: <#valueBinding#>, imageBundle: JDFlipNumberViewImageBundle(named: "<#imageBundleName#>"))

Feel free to use the original .psd file from the gfx folder to create custom numbers.

digits

Twitter

I'm @calimarkus on Twitter. Maybe tweet, if you like JDFlipNumberView!

tweetbutton

Shoutout

I found "FlipClock-SwiftUI" by @elpassion - a fully native SwiftUI flip-clock. It could be a good starting point for a native swift rewrite. I'm playing around with it in a fork a bit: FlipNumberView-Swift.

jdflipnumberview's People

Contributors

calimarkus avatar dchohfi avatar dlinsin avatar filmhomage avatar johnboiles 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

jdflipnumberview's Issues

Trainstation animation style [JDClassicFlipNumberView]

When changing a number, e.g. 3520 to 9250 every digit should animate on its own to the new number iterating over every digit inbetween.

Means:
3 to 9 (in 6 flips)
5 upwards to 2 (in 7 flips)
2 to 5 (in 3 flips)
0 stays like it is (0 flips)

Working Title: JDClassicFlipNumberView

flipView infinite loop bug ?

i will like the value from 99999 to 1, but the flipview have infinite loop.

- (void)viewDidLoad
{
 [self performSelector:@selector(changeCreditValue:) withObject:@"1" afterDelay:3];
}

- (void)setupCreditBar
{
    //Credit
    self.flipView = [[JDFlipNumberView alloc] initWithDigitCount:5];
    self.flipView.value = 99999;
    self.flipView.maximumValue = 99999;
    // add to view hierarchy and resize
    [self.view addSubview: self.flipView];
    self.flipView.frame = CGRectMake(190,7,90,50);    
}

- (void)changeCreditValue:(NSString *)newCreditValue
{
    NSDate *startDate = [NSDate date];
    NSLog(@"new Credit %@",newCreditValue);
    [self.flipView animateToValue:[newCreditValue intValue] duration:3.5];
}

No pod-2.0.6 tag

I tried updating to the latest version with "pod update" and I'm getting the following error:

[!] Pod::Executable fetch origin tags/pod-2.0.6 2>&1

fatal: Couldn't find remote ref tags/pod-2.0.6

fatal: The remote end hung up unexpectedly

Freeze in some occassion

When I leave a scene during the animation and switch back, the numbers are frozen in a middle state until next one, how to avoid this?

Thank you in advance.
04a0a88f-e503-4442-959d-7c028462458c

CALayerInvalidGeometry crash in JDFlipNumberDigitView.m

I'm seeing an intermittent crash. Here's what it's looks like:

Fatal Exception: CALayerInvalidGeometry
CALayer position contains NaN: [32.5 nan]

Thread : Fatal Exception: CALayerInvalidGeometry
0  CoreFoundation                 6495280712 __exceptionPreprocess
1  libobjc.A.dylib                6770999524 objc_exception_throw
2  CoreFoundation                 6495280520 -[NSException initWithCoder:]
3  QuartzCore                     6563416432 CA::Layer::set_position(CA::Vec2<double> const&, bool)
4  QuartzCore                     6563416264 -[CALayer setPosition:]
5  QuartzCore                     6563416152 -[CALayer setFrame:]
6  UIKit                          6570418116 -[UIView(Geometry) setFrame:]
7  MyApp                          4297676208 -[JDFlipNumberDigitView setFrame:] (JDFlipNumberDigitView.m:159)
8  MyApp                          4297690220 -[JDFlipNumberView layoutSubviews] (JDFlipNumberView.m:495)
9  UIKit                          6570433068 -[UIView(CALayerDelegate) layoutSublayersOfLayer:]
10 QuartzCore                     6563453332 -[CALayer layoutSublayers]
11 QuartzCore                     6563431780 CA::Layer::layout_if_needed(CA::Transaction*)
12 QuartzCore                     6563431432 CA::Layer::layout_and_display_if_needed(CA::Transaction*)
13 QuartzCore                     6563429384 CA::Context::commit_transaction(CA::Transaction*)
14 QuartzCore                     6563428748 CA::Transaction::commit()
15 UIKit                          6570441032 _UIApplicationHandleEventQueue
16 CoreFoundation                 6494986472 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
17 CoreFoundation                 6494983052 __CFRunLoopDoSources0
18 CoreFoundation                 6494975036 __CFRunLoopRun

I've gotten this a handful of times on several devices (iPhone 5s, 6, 6s) on both iOS8 and 9.1. It's super intermittent so I haven't been able to reproduce in the debugger.

Seems like the things that affect CALayer's position are bounds, anchorPoint, and possibly transform. Though I haven't been able to figure out where a division by zero could have happened for any of those. I'll post here if I make any progress in diagnosing this.

Allow JDFlipViewRelativeMargin to be configured

This value should be configurable someplace in the component to enhance the rendering.

For instance with my custom design, the views are too close one from each other, I would like to set JDFlipViewRelativeMargin to something like 30%.

Set the animation direction manually.

  • Do all changes (to higher & lower numbers) in the same direction, as set.
  • (Currently the animation direction is falling down for higher numbers and reverse for lower numbers)

animateToValue should use the given speed

- (void) animateToValue: (NSInteger) value withDuration: (CGFloat) duration is limited to the minimum timer speed of 0.001s currently set within JDFlipNumberView.m.

Use ARC

Currently the classes do not use ARC.

Support autoresizing

Change setFrame: logic: Place the view in the center of any given frame. Not at the top left.

Use setFrame:allowScaling: if desired.

Add shadow when flipping view

Hi
thanks for this great library.
Is there any way to add shadow when flipping views? it makes flipping more realistic.

change animation direction when the number get down or up

I try to change animation direction to up when the number decreased. i hope its useful
in "- (void) animateIntoCurrentDirectionWithDuration: (CGFloat) duration
" changed
if (mCurrentDirection == eFlipDirectionDown) {
// exchange images & setup animation
if (mCurrentState == eFlipStateFirstHalf)
{
// setup first animation half
mImageViewFlip.frame = CGRectMake(0, self.frame.size.height/2.0, self.frame.size.width, self.frame.size.height/2.0);
mImageViewFlip.image = [mBottomImages objectAtIndex: mCurrentValue];
mImageViewBottom.image = [mBottomImages objectAtIndex: nextIndex];
mImageViewTop.image = [mTopImages objectAtIndex: mCurrentValue];

        // inform delegate
        if ([delegate respondsToSelector: @selector(flipNumberView:willChangeToValue:)]) {
            [delegate flipNumberView: self willChangeToValue: nextIndex];
        }

        animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(0.0, 1, 0, 0)];
        animation.toValue   = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI_2, 1, 0, 0)];
        animation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut];
    }
    else
    {
        // setup second animation half
        mImageViewFlip.image = [mTopImages objectAtIndex: nextIndex];
        animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(-M_PI_2, 1, 0, 0)];
        animation.toValue   = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(0.0, 1, 0, 0)];
        animation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseIn];

    }
}else{
    // exchange images & setup animation
    if (mCurrentState == eFlipStateFirstHalf)
    {
        // setup first animation half
        mImageViewFlip.frame   = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height/2.0);
        mImageViewFlip.image   = [mTopImages    objectAtIndex: mCurrentValue];
        mImageViewBottom.image = [mBottomImages objectAtIndex: mCurrentValue];
        mImageViewTop.image    = [mTopImages    objectAtIndex: nextIndex];

        // inform delegate
        if ([delegate respondsToSelector: @selector(flipNumberView:willChangeToValue:)]) {
            [delegate flipNumberView: self willChangeToValue: nextIndex];
        }

        animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(0.0, 1, 0, 0)];
        animation.toValue   = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(-M_PI_2, 1, 0, 0)];
        animation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseIn];
    }
    else
    {
        // setup second animation half
        mImageViewFlip.image = [mBottomImages objectAtIndex: nextIndex];

        animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI_2, 1, 0, 0)];
        animation.toValue   = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(0.0, 1, 0, 0)];
        animation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut];
    }
}

in "- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
" changed
// update images
if (mCurrentDirection == eFlipDirectionDown) {
mImageViewTop.image = [mTopImages objectAtIndex: mCurrentValue];
}else{
mImageViewBottom.image = [mBottomImages objectAtIndex: mCurrentValue];
}

in "- (void) updateFlipViewFrame
" changed
if (mCurrentDirection == eFlipDirectionDown) {
if (mCurrentState == eFlipStateFirstHalf)
{
mImageViewFlip.layer.anchorPoint = CGPointMake(0.5, 0.0);
mImageViewFlip.frame = mImageViewBottom.frame;
}
else
{
mImageViewFlip.layer.anchorPoint = CGPointMake(0.5, 1.0);
mImageViewFlip.frame = mImageViewTop.frame;
}
}else{
if (mCurrentState == eFlipStateFirstHalf)
{
mImageViewFlip.layer.anchorPoint = CGPointMake(0.5, 1.0);
mImageViewFlip.frame = mImageViewTop.frame;
}
else
{
mImageViewFlip.layer.anchorPoint = CGPointMake(0.5, 0.0);
mImageViewFlip.frame = mImageViewBottom.frame;
}
}

Add a [JDFlipClockView]

Just displays the current time..

Optionally also support custom date/time formats:

  • (void)initWithDateFormat:(NSString*)dateFormat;

NSBundle for `imageBundleName` should be configurable

JDFlipNumberViewImageFactory.m lines 53 and 96 use [NSBundle mainBundle] as the bundle from which to load the digit images. This causes trouble when trying to use JDFlipNumberView as a framework, with Carthage, or in an IB_DESIGNABLE since the bundle for JDFlipNumberView is not necessarily the same as [NSBundle mainBundle].

Unfortunately, the fix isn't as simple as just replacing [NSBundle mainBundle] with [NSBundle bundleForClass:[self class]] since the default images JDFlipNumberView.bundle will be in the JDFlipNumberView bundle, but any custom images may be in another bundle.

Probably the right solution is to allow the option of passing an NSBundle object along with the image bundle name. For example, here are a few methods that would get renamed.

- (void)generateImagesFromBundleNamed:(NSString*)bundleName;

becomes

- (void)generateImagesFromImageBundleName:(NSString*)imageBundleName inBundle:(NSBundle*)bundle;

And

- (void)setImageBundleName:(NSString*)imageBundleName;

becomes

- (void)setImageBundleName:(NSString*)imageBundleName inBundle:(NSBundle*)bundle;

If necessary, the old methods could stick around for backwards compatibility. They would just call the new methods with [NSBundle mainBundle] for the bundle parameter

Full support for NSNumberFormatter

Hi!

It would be great if your library will support NSNumberFormatter (for example, in our project we're using groupSeparator for NSNumberFormatter, and our numbers looks like "1 000 000", not "1000000")

Thanks!

Weird behavior with AutoLayout

The internal usage of Autoresizing masks leed to a a weird behavior of JDFlipNumberView when using AutoLayout in the iOS Project

Nested completion blocks aren't working

Discussion with MattFoley@dcdbd01

I'm sorry, this is a bit in progress on my fork and didn't have time to fully fix it. The starting issue, an infinite loop, was caused calling something like:

[self.countdownView animateToValue:someValue duration:someDuration  completion:^(BOOL finished) {
    [self.countdownView animateToValue:someValue duration:someDuration completion:^(BOOL finished) {
        if (finished) {
            [self doStuff];
        }
    }];
}];

The first line of animateToValue called [self stopAnimation] would try to call the previous completion block, which executed the second animateToValue, which would hit the [self stopAnimation] again, on and on forever.

You can see in my fork I started to try to fix it but wasn't able to fully fix it before I ran out of time.

My current workaround is this:

[self.countdownView animateToValue:someValue duration:someDuration  completion:^(BOOL finished) {
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.flipNumberView animateToValue:someValue duration:someDuration completion:^(BOOL finished) {
            if (finished) {
                [self doStuff];
            }
        }];
    });
}];

I believe the timer issue was just a nested block/threading issue as a result of some of the other changes I've made.

CC: @MattFoley

Add shadows

Darken views while flipping to fake shadow.

imageSnapshotAfterScreenUpdates Problem when flipping different Views on different Screens

I'm using the JDFlipImageView flipToView Method to animate 2
Views concurrently. One on an iOS 8 Device and one on a connected
Beamer (simply an UIWindow with UIView where an UIImageView is
displayed and flipped, same constellation on the iOS Device).
The animation is invoked first for the Beamer's View and then for the
iOS Device View.

It's quite simple something like:

  ...
  if(self->secondScreen) {
    UIImageView* beamerLastImageView = self->newBeamerImageView;
    self->newBeamerImageView = [[UIImageView alloc]
initWithImage:self->beamerImage];
  }
  UIImageView* lastImageView = self->newImageView;
  self->newImageView = [[UIImageView alloc] initWithImage:self->newImage];
  ...
  if(self->secondScreen) {
    [beamerLastImageView flipToView:self->newBeamerImageView
duration:dur direction:dir completion:^(BOOL finished){ ... }];
  }
  [lastImageView flipToView:self->newImageView duration:dur
direction:dir completion:^(BOOL finished){ ... }];

The Problem is that shortly before the flip animation an Image appears
on the iOS Device View, but not on the Beamer. It is half the Size
(may be due to the Beamers windows frame being 1/2 of that of the
Devices View) and rotated by 180 degree.

After few hours of reading my code (can't exactly debug the APP while
using a connected display) which is quite short for the animations, I
started reading through the code path invoked by flipToView and it
seems that imageSnapshotAfterScreenUpdates either makes a wrong Image
or somehow mixes things between the 2 screens.

For now I worked around it by exposing the
addFlipViewWithAnimationFromImage and using my own Images directly
from the UIImageViews. This works without Problems.

I couldn't find any Information as to why this Problem occurs.

GraphicsDidChangeNotification

Send notification when graphics are replaced. And listen to that notification in each flipview to replace the current graphics.

JDDateCountdownFlipView: Weird behavior when countdown reaches "0"

There seems to be a problem with the countdown:
When it gets reached (0 days, 0 hours, 0 minutes, 0 seconds) it restarts to count down with 1 minute left. After that it restarts with 1 hour left.

The correct behavior would be IMHO to freeze the countdown when it reaches "0".

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.