[英]Button blink animation with SwiftUI
How to make border color changing animation in SwiftUI.如何在 SwiftUI 中制作边框颜色变化动画。 Here is the code with UIKit
这是 UIKit 的代码
extension UIButton{
func blink(setColor: UIColor, repeatCount: Float, duration: Double) {
self.layer.borderWidth = 1.0
let animation: CABasicAnimation = CABasicAnimation(keyPath: "borderColor")
animation.fromValue = UIColor.clear.cgColor
animation.toValue = setColor.cgColor
animation.duration = duration
animation.autoreverses = true
animation.repeatCount = repeatCount
self.layer.borderColor = UIColor.clear.cgColor
self.layer.add(animation, forKey: "")
}
}
I had a similar problem to implement a repeating text with my SwiftUI project.我在 SwiftUI 项目中实现重复文本时遇到了类似的问题。 And the answer looks too advanced for me to implement.
答案看起来太先进了,我无法实施。 After some search and research.
经过一番搜索和研究。 I managed to repeatedly blink my text.
我设法反复闪烁我的文字。 For someone who sees this post later, you may try this approach using
withAnimation{}
and .animation()
.对于稍后看到这篇文章的人,您可以使用
withAnimation{}
和.animation()
尝试这种方法。
Swift 5斯威夫特 5
@State private var myRed = 0.2
@State private var myGreen = 0.2
@State private var myBlue = 0.2
var body:some View{
Button(action:{
//
}){
Text("blahblahblah")
}
.border(Color(red: myRed,green: myGreen,blue: myBlue))
.onAppear{
withAnimation{
myRed = 0.5
myGreen = 0.5
myBlue = 0
}
}
.animation(Animation.easeInOut(duration:2).repeatForever(autoreverses:true))
}
A proposed solution still works with some minimal tuning.建议的解决方案仍然可以通过一些最小的调整来工作。
Updated code and demo is here 更新的代码和演示在这里
Hope the following approach would be helpful.希望以下方法会有所帮助。 It is based on ViewModifier and can be controlled by binding.
它基于 ViewModifier,可以通过绑定来控制。 Speed of animation as well as animation kind itself can be easily changed by needs.
动画速度以及动画种类本身可以根据需要轻松更改。
Note: Although there are some observed drawbacks: due to no didFinish callback provided by API for Animation it is used some trick to workaround it;注意:虽然有一些观察到的缺点:由于动画 API 没有提供 didFinish 回调,因此使用了一些技巧来解决它; also it is observed some strange handling of Animation.repeatCount, but this looks like a SwiftUI issue.
还观察到对 Animation.repeatCount 的一些奇怪处理,但这看起来像是一个 SwiftUI 问题。
Anyway, here is a demo (screen flash at start is launch of Preview): a) activating blink in onAppear b) force activating by some action, in this case by button无论如何,这是一个演示(开始时的屏幕闪烁是预览的启动):a)在 onAppear 中激活闪烁 b)通过某些操作强制激活,在这种情况下通过按钮
struct BlinkingBorderModifier: ViewModifier {
let state: Binding<Bool>
let color: Color
let repeatCount: Int
let duration: Double
// internal wrapper is needed because there is no didFinish of Animation now
private var blinking: Binding<Bool> {
Binding<Bool>(get: {
DispatchQueue.main.asyncAfter(deadline: .now() + self.duration) {
self.state.wrappedValue = false
}
return self.state.wrappedValue }, set: {
self.state.wrappedValue = $0
})
}
func body(content: Content) -> some View
{
content
.border(self.blinking.wrappedValue ? self.color : Color.clear, width: 1.0)
.animation( // Kind of animation can be changed per needs
Animation.linear(duration:self.duration).repeatCount(self.repeatCount)
)
}
}
extension View {
func blinkBorder(on state: Binding<Bool>, color: Color,
repeatCount: Int = 1, duration: Double = 0.5) -> some View {
self.modifier(BlinkingBorderModifier(state: state, color: color,
repeatCount: repeatCount, duration: duration))
}
}
struct TestBlinkingBorder: View {
@State var blink = false
var body: some View {
VStack {
Button(action: { self.blink = true }) {
Text("Force Blinking")
}
Divider()
Text("Hello, World!").padding()
.blinkBorder(on: $blink, color: Color.red, repeatCount: 5, duration: 0.5)
}
.onAppear {
self.blink = true
}
}
}
This is so much easy.这很容易。 First create a
ViewModifier
, so that we can use it easily anywhere.首先创建一个
ViewModifier
,以便我们可以在任何地方轻松使用它。
import SwiftUI
struct BlinkViewModifier: ViewModifier {
let duration: Double
@State private var blinking: Bool = false
func body(content: Content) -> some View {
content
.opacity(blinking ? 0 : 1)
.animation(.easeOut(duration: duration).repeatForever())
.onAppear {
withAnimation {
blinking = true
}
}
}
}
extension View {
func blinking(duration: Double = 0.75) -> some View {
modifier(BlinkViewModifier(duration: duration))
}
}
Then use this like,然后像这样使用,
// with duration
Text("Hello, World!")
.foregroundColor(.white)
.padding()
.background(Color.blue)
.blinking(duration: 0.75) // here duration is optional. This is blinking time
// or (default is 0.75)
Text("Hello, World!")
.foregroundColor(.white)
.padding()
.background(Color.blue)
.blinking()
After a lot of research on this topic, I found two ways to solve this thing.在对这个话题进行了大量研究之后,我找到了两种解决这个问题的方法。 Each has its advantages and disadvantages.
每个都有其优点和缺点。
There is a direct answer to your question.你的问题有一个直接的答案。 It's not elegant as it relies on you putting in the timing in a redundant way.
它并不优雅,因为它依赖于您以冗余方式输入时间。
Add a reverse
function to Animation
like this:像这样向
Animation
添加reverse
函数:
extension Animation {
func reverse(on: Binding<Bool>, delay: Double) -> Self {
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
on.wrappedValue = false /// Switch off after `delay` time
}
return self
}
}
With this extension, you can create a text, that scales up and back again after a button was pressed like this:使用此扩展程序,您可以创建一个文本,在按下按钮后按比例放大和返回,如下所示:
struct BlinkingText: View {
@State private var isBlinking: Bool = false
var body: some View {
VStack {
Button {
isBlinking = true
} label: {
Text("Let it blink")
}
.padding()
Text("Blink!")
.font(.largeTitle)
.foregroundColor(.red)
.scaleEffect(isBlinking ? 2.0 : 1.0)
.animation(Animation.easeInOut(duration: 0.5).reverse(on: $isBlinking, delay: 0.5))
}
}
}
It's not perfect so I did more research.它并不完美,所以我做了更多的研究。
Actually, SwiftUI provides two ways to get from one look (visual representation, ... you name it) to another smoothly.实际上,SwiftUI 提供了两种方式来平滑地从一种外观(视觉表示,......你的名字)到另一种外观。
So, here's another code snippet using transitions.所以,这是另一个使用转换的代码片段。 The hacky part is the
if-else
which ensures, that one View disappears and another one appears.骇人听闻的部分是
if-else
确保一个视图消失而另一个视图出现。
struct LetItBlink: View {
@State var count: Int
var body: some View {
VStack {
Button {
count += 1
} label: {
Text("Let it blink: \(count)")
}
.padding()
if count % 2 == 0 {
BlinkingText(text: "Blink Blink 1!")
} else {
BlinkingText(text: "Blink Blink 2!")
}
}
.animation(.default)
}
}
private struct BlinkingText: View {
let text: String
var body: some View {
Text(text)
.foregroundColor(.red)
.font(.largeTitle)
.padding()
.transition(AnyTransition.scale(scale: 1.5).combined(with: .opacity))
}
}
You can create nice and interesting "animations" by combining transitions.您可以通过组合过渡来创建漂亮而有趣的“动画”。
if-else
.if-else
。 Adding the possibility to SwiftUI to chain Animations would help.This is some code I came up with for a blinking button in SwiftUI 2, it might help someone.这是我为 SwiftUI 2 中的闪烁按钮提出的一些代码,它可能对某人有所帮助。 It's a toggle button that blinks a capsule shaped overlay around the button.
这是一个切换按钮,可在按钮周围闪烁胶囊形覆盖层。 It works but personally, I don't like my function blink() that calls itself.
它有效,但就我个人而言,我不喜欢调用自身的函数 blink()。
struct BlinkingButton:View{
@Binding var val:Bool
var label:String
@State private var blinkState:Bool = false
var body: some View{
Button(label){
val.toggle()
if val{
blink()
}
}
.padding(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16))
.foregroundColor(.white)
.background(val ? Color.blue:Color.gray)
.clipShape(Capsule())
.padding(.all,8)
.overlay(Capsule().stroke( blinkState && val ? Color.red:Color.clear,lineWidth: 3))
}
func blink(){
blinkState.toggle()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5){
if val{
blink()
}
}
}
}
In use it looks like this:在使用中它看起来像这样:
struct ContentView: View {
@State private var togVal:Bool = false
var body: some View {
VStack{
Text("\(togVal ? "ON":"OFF")")
BlinkingButton(val: $togVal, label: "tap me")
}
}
}
Please use below SwiftUI code to implement border blinking animation. 请使用下面的SwiftUI代码实现边框闪烁动画。
extension AnyTransition {
static func repeating<T: ViewModifier>(from: T, to: T, duration: Double = 1) -> AnyTransition {
.asymmetric(
insertion: AnyTransition
.modifier(active: from, identity: to)
.animation(Animation.easeInOut(duration: duration).repeatForever())
.combined(with: .opacity),
removal: .opacity
)
}
}
struct BorderColor: ViewModifier {
private let borderColor: Color
init(_ borderColor: Color) {
self.borderColor = borderColor
}
func body(content: Content) -> some View {
content.border(borderColor, width: 2.0)
}
}
struct ContentView: View {
@State var showBlinkingView: Bool = false
var body: some View {
VStack {
if showBlinkingView {
Text("I am blinking")
.transition(.repeating(from: BorderColor(.red), to: BorderColor(.green)))
}
Spacer()
Button(action: {
self.showBlinkingView.toggle()
}, label: {
Text("Toggle blinking view")
})
}.padding(.vertical, 50)
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.