DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Build an AI Chatroom With ChatGPT and ZK by Asking It How!
  • Serverless Patterns: Web
  • Router4j: A Free Alternative to Google Maps for Route and Distance Calculation
  • AI-Powered Flashcard Application With Next.js, Clerk, Firebase, Material UI, and LLaMA 3.1

Trending

  • Unlocking the Benefits of a Private API in AWS API Gateway
  • Unlocking the Potential of Apache Iceberg: A Comprehensive Analysis
  • Unlocking AI Coding Assistants Part 3: Generating Diagrams, Open API Specs, And Test Data
  • Beyond ChatGPT, AI Reasoning 2.0: Engineering AI Models With Human-Like Reasoning
  1. DZone
  2. Software Design and Architecture
  3. Integration
  4. Rolling Numbers Animation Using Only CALayers

Rolling Numbers Animation Using Only CALayers

RollingNumbers is a lightweight UIView for getting smooth rolling animation between numbers implemented using only CALayer.

By 
Max Kalik user avatar
Max Kalik
·
Apr. 11, 23 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
3.5K Views

Join the DZone community and get the full member experience.

Join For Free

I’m an iOS Engineer at Triumph Labs, where I develop TriumphSDK for game devs. Not so long ago, I got a task to completely reimplement UI and animations. One of the most interesting parts was implementing of balance view with rolling animation of the number in it.

At the first sight, it looks pretty simple, but we needed a well-configured custom solution. Of course, I checked existing libraries that I could reuse and adjust this kind of animation for our needs. After the first try, I figured out that the libraries are not so adjustable, and we cannot control the animation of each number. I won’t go into details too much. However, I decided to make my own solution from scratch.

Rolling Numbers is an open-source project that is available as Cocoapod and Swift packages.

CALayers

I had a list of requirements — how should look animation but also I added another one — the component should be implemented only using CALayers. Why?

Most of the libraries that I checked were implemented using only UIViews. The typical approach is making UIScrollViews or UIStackVeiws inside of a UIView where arranged views are UILabels. The rolling animation is implemented by using UIView.animate changing constraints. That’s absolutely ok, but I wanted as much as possible to reduce the workload of performance.

CALayers represent the visual content of UIView. Layers provide low-level API of an efficient and more detailed configuration of rendering and animation using a low-cost Core Animation framework. Another important thing about Layers is that they are rendered using only GPU resources. This means the CPU will be free for another calculation task while the animation is running — that’s what I wanted to mention about performance.

Solution

If to check open-source solutions, the approach of how to construct the view is everywhere almost the same: a stack of numbers but visible ones only based on the state. My wireframe looks the same but different :) As you can see in the picture, the stacks of numbers are doubled; this means the number of elements in one stack is 20.

Wireframe of the stacks of numbers

Wireframe of the stacks of numbers

This particular number of elements needs a smooth transition from an initial state to the next one, and in the meantime, the direction of rolling I wanted to be fully configurable. Let’s say a stack would have only 10 elements (and it would be pretty reasonable at the first sight), then the rolling direction from one digit to another would be always different. I needed to obey the direction rolling, for example, all digits from up to down.

Each stack in the view is CALayer where sublayers are numbers. The numbers are arranged by the same height, which means it’s easy to calculate the next position of the future number.

Characters

All characters are  CATextLayers, and they have their own width. If to use UIStackView it’s not needed to care about width; all will be arranged by view configuration, but in my case — CALayers don’t have a such default implementation, so it means it should be calculated.

To get the width of a character is a pretty straight-forward task to use NSAttributedString and font:

Swift
 
private func prepareWidthOfChar(_ char: Character) -> Double {
    let fontAttributes = [NSAttributedString.Key.font: font]
    let size = String(char).size(withAttributes: fontAttributes as [NSAttributedString.Key : Any])
    return size.width * characterSpacing
}


This function also helps to calculate an actual with of all columns in the Rolling Numbers view. To get this information, a developer can use the public property width.

Animation Configuration

To achieve the spring animation effect, I used CASpringAnimation changing y position of theCALayer — a column where sublayers are numbers CATextLayers.

Swift
 
private func animate(config: RollingNumbersView.AnimationConfiguration,
                 completion: (() -> Void)? = nil) {
        
  CATransaction.begin()
        
  let animation: CASpringAnimation = CASpringAnimation(keyPath: "position.y")
  let fromValue: CGFloat = position.y
  let toValue: CGFloat = moveToDigit()

  animation.fromValue = fromValue
  animation.toValue = toValue
  animation.duration = config.duration
  animation.speed = config.speed
  animation.damping = config.damping
  animation.initialVelocity = config.initialVelocity
  animation.isRemovedOnCompletion = false
  animation.fillMode = .forwards
        
  CATransaction.setCompletionBlock {
    completion?()
  }
        
  add(animation, forKey: nil)
        
  CATransaction.commit()
}


Additionally, I used CATransaction to catch the animation completion moment, which reference I exposed in the Rolling Numbers view API as trailing completion. It happens only once after setting a new number with animation.

Swift
 
rollingNumbersView.setNumberWithAnimation(245699) {
    // completion
}


For the default animation config, I prepared a separate struct called: AnimationConfiguration . There are four initial configurations of the spring animation that are publically accessible.

Swift
 
duration: CFTimeInterval = 1,
speed: Float = 0.3,
damping: CGFloat = 17,
initialVelocity: CGFloat = 1


Animation Type

There are four animation types prepared for usage:

Swift
 
public enum AnimationType {
    case allNumbers
    case onlyChangedNumbers
    case allAfterFirstChangedNumber
    case noAnimation
}


AnimationType is accessible using public property animationType .

Let’s consider an example formatted as US currency. The initial value is $4.588.77. The future value is $4.576.67 (the changed numbers in the price I highlighted in bold). DZone does not allow GIF images, so use this link to the file where you can see the animation types.

By default, the animation is set up with .allAfterFirstChangedNumber . This means if a future number is in the middle of the horizontal string, then all others digits after this number will be animated. If to use .onlyChangedNumbers — this means literally: if a future number is different, then only this number column will be scrolled. But somebody will need to roll all digits, so for this, you can use just .allNumbers.

Rolling Direction

The numbers rolling direction can be configured using specific public properties rollingDirection: RollingDirection where obviously only two directions: .up and .down. For example, if to set up this property as .upthen the numbers in the view will always move in the up direction. However, initially, this property is nil which means, by default, the direction depends on a future number. If the future number will be less than the old one, then the numbers column will move down and vice versa.

Formatting

There are several options to format the Rolling Numbers: alignment, character spacing, font, NumberFormatterconfigurator, and text color.

Swift
 
var rollingNumbersView = {
    // Initialize Rolling Numbers view with initial number value
    let view = RollingNumbersView(number: 1234.56)
    
    // Spacing between numbers
    view.characterSpacing = 1
    
    // Text color
    view.textColor = .black
    
    // Alignment within UIView
    view.alignment = .left
    
    // UIFont
    view.font = .systemFont(ofSize: 48, weight: .medium)
    
    let formatter = NumberFormatter()
    formatter.numberStyle = .currency
    view.formatter = formatter
    
    return view
}()


Changing the size of the font, keep in mind that the actual height of the view can be smaller, which means the digits can be invisible.

Besides general public property textColor, there is another interesting public method:

Swift
 
rollingNumbersView.setTextColor(.blue, withAnimationDuration: 3)


Under the hood, the animation was implemented using CABasicAnimation of forgroundColor. Changing the text color with animation. The public setTextColor method helps to change the color of the text (numbers) while moving animation is happening! As an option changing color can be also an animation with duration.

Wrapping Up

I enjoy building UI for mobile apps and solving performance problems. If it’s needed to use more than just UIView then Apple Documentation and tons of examples can help to make a more efficient solution.

When I finished building the Rolling Numbers component, I decided to share my solution with everyone — making a public library. Full documentation of the usage of Rolling Number you can get from the GitHub repository. Welcome to everyone who can contribute and suggest improvements to this project via PR!

Thanks for reading.

API Core Animation GIF Public library UI Data Types

Published at DZone with permission of Max Kalik. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Build an AI Chatroom With ChatGPT and ZK by Asking It How!
  • Serverless Patterns: Web
  • Router4j: A Free Alternative to Google Maps for Route and Distance Calculation
  • AI-Powered Flashcard Application With Next.js, Clerk, Firebase, Material UI, and LLaMA 3.1

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!