How to Create a Smooth Colour Change Animation Using Swiftui? (Example in Question)

How can I create a smooth colour change animation using swiftUI? (Example in question)

Here is a demo of possible approach - just use some shape (circle in this case) between text and opaque background and move it depending on state.

Prepared with Xcode 12.4 / iOS 14.4

Note: you can use just tap gesture instead of button and tune all parameters and colors, but in general the idea remains the same

demo

struct DemoView: View {
@State private var playing = false
var body: some View {
Button(action: { playing.toggle() }) {
HStack{
Image(systemName: playing ? "pause" : "play")
Text(playing ? "STOP" : "PLAY")
.bold()
}
.background(
Circle().fill(Color.purple)
.frame(width: 160, height: 160, alignment: .leading)
.blur(radius: 3)
.offset(x: playing ? 0 : -160, y: -40)
.animation(.easeInOut(duration: 1), value: playing)
)
.padding()
.foregroundColor(.white)
.background(Color.green)
.cornerRadius(25)
.clipped()
.shadow(radius: 20)
}
}
}

How can I create an animation color change from center of a view in SwiftUI?

You can use two Circles in a ZStack and animate the scale effect value of the top most circle.

struct ContentView: View {

private let colors: [Color] = [.red, .yellow, .blue, .pink, .orange]
private let maxScaleEffect: CGFloat = 4.0
private let minScaleEffect: CGFloat = 0
private let animationDuration = 0.6
private let animationDelay = 0.1

@State private var shouldTransition = true
@State private var colorIndex = 0

var body: some View {
ZStack {
Circle()
.fill(previousColor)
.scaleEffect(maxScaleEffect)

Circle()
.fill(transitioningColor)
.scaleEffect(shouldTransition ? maxScaleEffect : minScaleEffect)

Button("Change color") {
shouldTransition = false
colorIndex += 1

// Removing DispatchQueue here will cause the first transition not to work
DispatchQueue.main.asyncAfter(deadline: .now() + animationDelay) {
withAnimation(.easeInOut(duration: animationDuration)) {
shouldTransition = true
}
}
}
.foregroundColor(.primary)
}
}

private var previousColor: Color {
colors[colorIndex%colors.count]
}

private var transitioningColor: Color {
colors[(colorIndex+1)%colors.count]
}
}

SwiftUI - Animate resize of a view frame

I don't know exactly what you need but here is a very basic example with a Rectangle that gets scaled when you tap the Button:

struct ContentView: View {

@State var animate = false

var body: some View {
VStack {
Button(action: {
withAnimation {
self.animate.toggle()
}
}, label: {
Text("Animate")
})
Rectangle()
.foregroundColor(.blue)
.frame(width: self.animate ? 100 : 150, height: self.animate ? 60 : 90)
}
}
}

Please add some code to your next question or edit the question so people can provide a more specific answer.

Make the soft gradient between colors of LinearGradient removed

You can use LinearGradient(stops: ) init. It will not give you a full hard edge, but quite close. You can then change the stop values or the array for animation.

Sample Image

struct ContentView: View {

let stops = [Gradient.Stop(color: Color.green, location: 0.0),
Gradient.Stop(color: Color.green, location: 0.33),
Gradient.Stop(color: Color.blue, location: 0.33),
Gradient.Stop(color: Color.blue, location: 0.66),
Gradient.Stop(color: Color.red, location: 0.66),
Gradient.Stop(color: Color.red, location: 1.0)]

var body: some View {
Rectangle()
.fill(
LinearGradient(stops: stops, startPoint: .trailing, endPoint: .leading)
)
.frame(height: 200)
.padding()
}
}

How to animate/transition text value change in SwiftUI

So it turns out this is really easy

Text(textValue)
.font(.largeTitle)
.frame(width: 200, height: 200)
.transition(.opacity)
.id("MyTitleComponent" + textValue)

Note the additional id at the end. SwiftUI uses this to decide if it's dealing with the same view or not when doing a redraw. If the id is different then it assumes the previous view was removed and this one has been added. Because it's adding a new view it applies the specified transition as expected.

NB: It's quite possible that this id should be unique for the entire view tree so you probably want to take care to namespace it accordingly (hence the MyTitleComponent prefix in the example).

Animation transition of one gradient to another SwiftUI

We need to animate container to make transition work, so here is a solution:

Text("random button")
.background(
VStack {
if self.animCheck {
LinearGradient(gradient: Gradient(colors: [.white, .green]), startPoint: .leading, endPoint: .trailing)
.transition(.slide)
} else {
LinearGradient(gradient: Gradient(colors: [.black, .orange]), startPoint: .leading, endPoint: .trailing)
.transition(.slide)
}
}.animation(.spring()))

iOS - Smooth Color Change Transition/Animation

One possible way of doing it is apply background color animation on view's layer.

Now to pass through entire spectrum you have to work on combinations of three colors.

The best way to achieve this is to use 'Hue'.

[[UIColor alloc] initWithHue:135/360.0f saturation:1
brightness:1 alpha:1]

Now you have to iterate for all Hue values and you will get the smooth transition that goes across all the spectrum.

Sample code:

  1. make a local variable

int _currentColorHue = 0;


  1. recursive call for change background color.

-(void)animateMyView
{

[UIView animateWithDuration:0.01 animations:^{
self.view.layer.backgroundColor = [[UIColor alloc] initWithHue:_currentColorHue/360.0f saturation:1 brightness:1 alpha:1].CGColor;
} completion:^(BOOL finished)
{
_currentColorHue++;
if (_currentColorHue > 360)
{
_currentColorHue = 0;
}
[self animateMyView];
}];
}

You can stop the animation according to your use.

How to animate transition between views in SwiftUI?

With the following approach you can modify your appMode as you wish (onAppear, onTapGesture, etc.). Animation duration is, of course, can be set any you wish (as well as kind of transition, actually, however some transitions behaves bad in Preview, and should be tested on Simulator or real device).

Demo: (first blink is just Preview launch, then two transitions for onAppear, then for onTap)

demo

Tested with Xcode 11.4 / iOS 13.4 - works and in Preview and in Simulator.

Code: Important parts marked with comments inline.

var body: some View {
ZStack {
// !! to keep background deepest, `cause it affects removing transition
Color.blue.zIndex(-1)

// !! keep any view in explicit own `if` (don't use `else`)
if .game == self.appStore.appMode {
GameView()
.transition(AnyTransition.scale.animation(.easeInOut(duration: 1)))
}

if .options == self.appStore.appMode {
OptionsView()
.transition(AnyTransition.scale.animation(.easeInOut(duration: 1)))
}

if .menu == self.appStore.appMode {
MenuView()
.transition(AnyTransition.scale.animation(.easeInOut(duration: 1)))
}
}
}

Update: for .slide (and probably other moving transitions) the change of state should be wrapped into explicit withAnimation, like below

withAnimation {
self.appStore.appMode = new_mode_here
}

Note: these is one of transitions that is not supported by Preview - test in Simulator.

Example for transition like

    ...
if .menu == self.appStore.appMode {
Text("MenuView").frame(width: 300, height: 100).background(Color.red)
.transition(AnyTransition.move(edge: .bottom).combined(with: .opacity).animation(.easeInOut(duration: 1)))
}
}
.onTapGesture {
withAnimation {
let next = self.appStore.appMode.rawValue + 1
self.appStore.appMode = next > 2 ? .game : AppStore.AppMode(rawValue: next)!
}
}

demo2



Related Topics



Leave a reply



Submit