Static is boring nowadays, especially in Mobile designs. Designers are looking for more and more creative ways to attract users’ attention.
At the same time, platforms are providing really good support for the easier implementation of crazy animations!
Last week, we also received one such requirement from our designer team. The app we were working on is Nolonely — Build healthy habits, available on both android and iOS platforms.
Here are our previous and new designs.
As you can see, the right is much more engaging and eye catchy. AppStore app also has similar animation.
Today we will explore how we can implement infinite scrolling animations that look seamless.
We will not implement exact same UI here as it will be very time-consuming and also unrelated to the animation we are implementing.
Instead, we will create a simple app with 4 colored blocks that keep moving.
Later, we will also explore how we can create a common component InfiniteScroller
that can be used to apply infinite scrolling animation to any view.
I have divided this post into 3 parts, feel free to jump around!
InfiniteScroller
componentIf you are in hurry and just need to apply the animation in your project, feel free to just copy InfiniteScroller
in your app and you are ready to go.
Alright, let’s begin!
We are what we repeatedly do. Excellence, then, is not an act, but a habit. Try out Justly and start building your habits today!
Let’s start by adding a ColorAnimationView
that has 4 colored blocks with a size of screenWidth/4
.
struct ColorAnimationView: View {
var body: some View {
GeometryReader { geometry in
let size = geometry.size.width / 4
HStack(spacing: 0) {
ColorView(size: size, color: Color.green)
ColorView(size: size, color: Color.white)
ColorView(size: size, color: Color.pink)
ColorView(size: size, color: Color.cyan)
}
.padding(.vertical, 100)
}
}
}
struct ColorView: View {
var size: CGFloat
var color: Color
var body: some View {
VStack {
VStack{}.frame(width: size, height: size, alignment: .center)
}
.background(color)
}
}
Nothing fancy here, we have just added a ColorView
that accepts size and color and then we have used 4 instances of that view in ColorAnimationView
.
Run the app and you will see the following UI.
Alright, now it’s time to animate those color blocks.
The trick to applying infinite scrolling animation is to add a duplicate color block at the end and then later when an exact 1 page is scrolled, that is equal to the total width of 4 colored blocks, set scroll offset to 0 again.
It’s easier to explain with code rather than words, let’s add a code to animate those blocks!
struct ColorAnimationView: View {
@State
var xOffset: CGFloat = 0
var body: some View {
GeometryReader { geometry in
let size = geometry.size.width / 4
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 0) {
ColorView(size: size, color: Color.green)
ColorView(size: size, color: Color.white)
ColorView(size: size, color: Color.pink)
ColorView(size: size, color: Color.cyan)
// Add Dupliate color blocks to mimic infinie animation
ColorView(size: size, color: Color.green)
ColorView(size: size, color: Color.white)
ColorView(size: size, color: Color.pink)
ColorView(size: size, color: Color.cyan)
}
.padding(.vertical, 100)
.offset(x: xOffset, y: 0)
}.disabled(true)
.onAppear {
withAnimation(.linear(duration: 3).repeatForever(autoreverses: false)) {
xOffset = -size * 4
}
}
}
}
}
Let’s explore changes
xOffset
variable for horizontal animation. We will animate its value from 0 to -size * 4
and will repeat the animation forever. That means when 4 blocks are scrolled off the screen, the animation will start from 0 offset again but users will not notice it because the exact frame of duplicate views will be visible at that time.ScrollView
and set it to disabled
to make sure it can not be scrolled manually with gestures.That’s it, run the app and you will see infinite animation.
InfiniteScroller
componentLet’s move everything into a separate view called InfiniteScroller
that can be reused.
struct InfiniteScroller<Content: View>: View {
var contentWidth: CGFloat
var content: (() -> Content)
@State
var xOffset: CGFloat = 0
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 0) {
content()
content()
}
.offset(x: xOffset, y: 0)
}
.disabled(true)
.onAppear {
withAnimation(.linear(duration: 5).repeatForever(autoreverses: false)) {
xOffset = -contentWidth
}
}
}
}
And here’s its usage example.
struct ColorAnimationView: View {
var body: some View {
GeometryReader { geometry in
let size = geometry.size.width / 4
InfiniteScroller(contentWidth: size * 4) {
HStack(spacing: 0) {
ColorView(size: size, color: Color.green)
ColorView(size: size, color: Color.white)
ColorView(size: size, color: Color.pink)
ColorView(size: size, color: Color.cyan)
}
}
}
}
}
Well, that’s pretty easy, right? We just need to wrap the views inside InfiniteScroller
and we are done!
Well, that’s it for today, hope you learned something!
As you might have observed, creating animations with SwiftUI is very easy. Today, we have just explored the tip of the iceberg. There are so many creative animations you can implement with SwiftUI. I have previously explored more animations in 2 part series, feel free to check them out if you want to learn more.
Keep animating!!
Whether you need...