The*_*Fox 13 xcode swift swiftui
我想在屏幕上有一个“徽章”,当条件满足时,它会从正常大小反弹到更大,然后反复恢复正常,直到不再满足条件。不过,我似乎无法让徽章停止“弹跳”。一旦开始,势不可挡。
我尝试过的: 我尝试过使用一些动画,但它们可以分为使用“repeatForever”来达到预期效果的动画和不使用的动画。例如:
Animation.default.repeatForever(autoreverses: true)
和
Animation.spring(response: 1, dampingFraction: 0, blendDuration: 1)(将阻尼设置为 0 使其永远消失)
然后用 .animation(nil) 把它换掉。似乎不起作用。有没有人有任何想法?非常感谢您提前!这是重现它的代码:
struct theProblem: View {
@State var active: Bool = false
var body: some View {
Circle()
.scaleEffect( active ? 1.08: 1)
.animation( active ? Animation.default.repeatForever(autoreverses: true): nil )
.frame(width: 100, height: 100)
.onTapGesture {
self.active = !self.active
}
}
}
Run Code Online (Sandbox Code Playgroud)
The*_*Fox 41
我想到了!
.repeatForever() 如果您将动画替换为 ,则 使用的动画不会停止nil。如果您用相同的动画替换它但没有 .repeatForever(). (或者使用任何其他停止的动画,因此您可以使用持续时间为 0 的线性动画来立即停止)
换句话说,这行不通: .animation(active ? Animation.default.repeatForever() : nil)
但这确实有效: .animation(active ? Animation.default.repeatForever() : Animation.default)
为了使其更具可读性和易于使用,我将其放入一个扩展中,您可以像这样使用: .animation(Animation.default.repeat(while: active))
这是一个使用我的扩展程序的交互式示例,您可以使用实时预览来测试它:
import SwiftUI
extension Animation {
func `repeat`(while expression: Bool, autoreverses: Bool = true) -> Animation {
if expression {
return self.repeatForever(autoreverses: autoreverses)
} else {
return self
}
}
}
struct TheSolution: View {
@State var active: Bool = false
var body: some View {
Circle()
.scaleEffect( active ? 1.08: 1)
.animation(Animation.default.repeat(while: active))
.frame(width: 100, height: 100)
.onTapGesture {
self.active.toggle()
}
}
}
struct TheSolution_Previews: PreviewProvider {
static var previews: some View {
TheSolution()
}
}
Run Code Online (Sandbox Code Playgroud)
据我所知,一旦您分配动画,它永远不会消失,直到您的视图完全停止。因此,如果您将 .default 动画设置为永远重复并自动反转,然后您分配了一个持续时间为 4 的线性动画,您会注意到默认的重复动画仍在进行,但它的运动变得越来越慢,直到它在我们的 4 秒结束时完全停止。因此,我们通过线性动画将默认动画设置为停止动画。
小智 6
在经历了很多事情之后,我发现了一些对我有用的东西。至少暂时如此,直到我有时间找出更好的方法为止。
struct WiggleAnimation<Content: View>: View {
var content: Content
@Binding var animate: Bool
@State private var wave = true
var body: some View {
ZStack {
content
if animate {
Image(systemName: "minus.circle.fill")
.foregroundColor(Color(.systemGray))
.offset(x: -25, y: -25)
}
}
.id(animate) //THIS IS THE MAGIC
.onChange(of: animate) { newValue in
if newValue {
let baseAnimation = Animation.linear(duration: 0.15)
withAnimation(baseAnimation.repeatForever(autoreverses: true)) {
wave.toggle()
}
}
}
.rotationEffect(.degrees(animate ? (wave ? 2.5 : -2.5) : 0.0),
anchor: .center)
}
init(animate: Binding<Bool>,
@ViewBuilder content: @escaping () -> Content) {
self.content = content()
self._animate = animate
}
}
Run Code Online (Sandbox Code Playgroud)
使用
@State private var editMode = false
WiggleAnimation(animate: $editMode) {
VStack {
Image(systemName: image)
.resizable()
.frame(width: UIScreen.screenWidth * 0.1,
height: UIScreen.screenWidth * 0.1)
.padding()
.foregroundColor(.white)
.background(.gray)
Text(text)
.multilineTextAlignment(.center)
.font(KMFont.tiny)
.foregroundColor(.black)
}
}
Run Code Online (Sandbox Code Playgroud)
它是如何工作的?这里的 .id(animate) 修饰符不会刷新视图,只是将其替换为新视图,因此它回到了原始状态。
同样,这可能不是最好的解决方案,但它适用于我的情况。
使用交易怎么样
在下面的代码中,我根据动画的状态关闭或打开动画active
警告:一定要使用withAnimation,否则什么都不起作用
@State var active: Bool = false
var body: some View {
Circle()
.scaleEffect(active ? 1.08: 1)
.animation(Animation.default.repeatForever(autoreverses: true), value: active)
.frame(width: 100, height: 100)
.onTapGesture {
useTransaction()
}
}
func useTransaction() {
var transaction = Transaction()
transaction.disablesAnimations = active ? true : false
withTransaction(transaction) {
withAnimation {
active.toggle()
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3886 次 |
| 最近记录: |