SwiftUI 动画在嵌入另一个视图时不起作用 [英] SwiftUI animation not working when embedded inside another view

查看:24
本文介绍了SwiftUI 动画在嵌入另一个视图时不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我制作了一个可重复使用的圆形进度条.我应用了 repeatForever 动画来保持它旋转,但它仅在直接与 @State 或 @Published 变量一起使用时才有效,并且在嵌入另一个视图时无效.

<块引用>

可重复使用的 RingView.这是一个圆形的进度条

struct RingView:查看{private let percent: CGFloat = 80//保持固定为 80 用于测试目的var color = Color.primaryLightColor//一些随机颜色@Binding var 显示:布尔var主体:一些视图{ZStack {GeometryReader { 边界圆圈().trim(来自:self.show?self.progress:1,到:1).中风(LinearGradient(gradient: Gradient(colors: [self.color]), startPoint: .topTrailing, endPoint: .bottomLeading),样式:StrokeStyle(lineWidth: self.lineWidth(width: bounds.size.width), lineCap: .round, lineJoin: .round, miterLimit: .infinity, dash: [20, 0], dashPhase: 0)).rotationEffect(Angle(degrees: 90)).animation(.none).frame(宽度:bounds.size.width,高度:bounds.size.height)}}.rotationEffect(.degrees(show ? 360.0 : 0.0)).animation(show ? Animation.linear(duration: 1.0).repeatForever(autoreverses: false) : .none)}func multiplier(width: CGFloat) ->CGFloat {宽度/44}func lineWidth(width: CGFloat) ->CGFloat {5 * self.multiplier(宽度:宽度)}var 进度:CGFloat {1 - (百分比/100)}}

<块引用>

进度按钮.它使用上面的 RingView.一般用例是在执行长时间后台任务时显示动画.

RingView 动画在此 ProgressButton 中不起作用

struct ProgressButton: 查看{var 动作:() ->空白var 图像:图像?= 零var 文本:字符串 = ""var backgroundColor: Color = Color.bluevar textColor: 颜色 = .white@Binding var showProgress: Boolvar主体:一些视图{按钮(动作:动作){堆栈{如果显示进度{RingView(颜色:textColor,显示:self.$showProgress).frame(宽:25,高:25).transition(.scale).animation(.Spring())} 别的 {图片?.renderingMode(.original).resizable().frame(宽:25,高:25)}文字(文字).font(.headline).foregroundColor(textColor)}.填充().frame(minWidth: 0, maxWidth: .infinity, minHeight: 50, 对齐: .center).background(背景颜色).clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous))}.buttonStyle(PlainButtonStyle())}}

<块引用>

当 RingView 动画起作用时.当我直接使用它时,如下所示.

struct LoginView: View {@State var showProgress = truevar主体:一些视图{虚拟堆栈{//在职的RingView(显示:self.$showProgress).frame(宽:25,高:25)//不工作进度按钮(动作:{self.showProgress = true}, image: Image("password-lock"), text: "Login", backgroundColor: .blue, showProgress: self.showProgress)}}}

<小时><块引用>

我想我在理解@Binding 和@State 的工作方式时犯了一些错误.

解决方案

动画在状态更改时被激活.在提供的代码中没有任何变化,因此根本没有动画.

以下是主要更改,所以我让它工作了.使用 Xcode 11.4/iOS 13.4 进行测试(对缺少的自定义依赖项进行了一些复制)

1) 关闭初始动画

struct LoginView: View {@State var showProgress = false//... 其他代码//没有提供模型,所以使用相同的状态进行进度进度按钮(动作:{self.showProgress.toggle()//<<激活改变!!!}, image: Image("password-lock"), text: L.Login.LoginSecurely, backgroundColor: self.isLoginButtonEnabled ?Color.primaryLightColor : Color.gray, showProgress: $showProgress)

2) 在进度按钮内添加内环激活状态;用于隐藏/取消隐藏和激活的相同状态再次不起作用,因为当环出现时没有变化(它已经是真的)所以环没有被激活

@State private var actiavteRing = false//<<这里 !!var主体:一些视图{按钮(动作:动作){堆栈{如果显示进度{RingView(color: textColor, show: self.$actiavteRing)//!!.frame(宽:25,高:25).transition(.opacity).animation(.spring()).onAppear { self.actiavteRing = true }//<<.onDisappear { self.actiavteRing = false }//<<

3) 在 Ring 固定动画停用以避免累积效应(.none 在这里不起作用),所以

.rotationEffect(.degrees(show ? 360.0 : 0.0)).animation(显示?Animation.linear(持续时间:1.0).repeatForever(autoreverses: false) : .default)//<<这里 !!

I made a reusable circular progress bar. I applied repeatForever animation to keep it rotating but it only works when used directly with a @State or @Published variable and doesn't work when it is embedded in another view.

Reusable RingView. Which is a circular progress bar

struct RingView: View {
    private let percent: CGFloat = 80   // Keeping it fixed 80 for test purpose
    var color = Color.primaryLightColor // Some random color
    @Binding var show: Bool

    var body: some View {
        ZStack {
            GeometryReader { bounds in

                Circle()
                    .trim(from: self.show ? self.progress : 1, to: 1)
                    .stroke(
                        LinearGradient(gradient: Gradient(colors: [self.color]), startPoint: .topTrailing, endPoint: .bottomLeading),
                        style: StrokeStyle(lineWidth: self.lineWidth(width: bounds.size.width), lineCap: .round, lineJoin: .round, miterLimit: .infinity, dash: [20, 0], dashPhase: 0)
                )
                    .rotationEffect(Angle(degrees: 90))
                    .animation(.none)
                    .frame(width: bounds.size.width, height: bounds.size.height)
            }
        }
        .rotationEffect(.degrees(show ? 360.0 : 0.0))
        .animation(show ? Animation.linear(duration: 1.0).repeatForever(autoreverses: false) : .none)
    }

    func multiplier(width: CGFloat) -> CGFloat {
        width / 44
    }

    func lineWidth(width: CGFloat) -> CGFloat {
        5 * self.multiplier(width: width)
    }

    var progress: CGFloat {
        1 - (percent / 100)
    }
}

Progress Button. It uses the RingView above. A general use case is to show animation when doing a long background task.

The RingView animation doesn't work in this ProgressButton

struct ProgressButton: View {
        var action: () -> Void
        var image: Image? = nil
        var text: String = ""
        var backgroundColor: Color = Color.blue
        var textColor: Color = .white
        @Binding var showProgress: Bool

        var body: some View {
            Button(action: action) {
                HStack {
                    if showProgress {
                        RingView(color: textColor, show: self.$showProgress)
                            .frame(width: 25, height: 25)
                        .transition(.scale)
                            .animation(.Spring())
                    } else {
                        image?
                            .renderingMode(.original)
                            .resizable()
                            .frame(width: 25, height: 25)
                    }
                    Text(text)
                        .font(.headline)
                        .foregroundColor(textColor)
                }
                .padding()
                .frame(minWidth: 0, maxWidth: .infinity, minHeight: 50, alignment: .center)
                .background(backgroundColor)
                .clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous))
            }.buttonStyle(PlainButtonStyle())
        }
    }

When RingView animation works. When I use it directly as below.

struct LoginView: View {
    @State var showProgress = true
    var body: some View {
        VStack {

            // WORKING
            RingView(show: self.$showProgress)
                .frame(width: 25, height: 25)

            // NOT WORKING
            ProgressButton(action: {
                self.showProgress = true
            }, image: Image("password-lock"), text: "Login", backgroundColor: .blue, showProgress: self.showProgress)
        }
    }
}


I think I'm doing some mistake understanding how @Binding and @State are working.

解决方案

Animation is activate on state change. In provided code there is no change so no animation at all.

Below are main changes so I made it work. Tested with Xcode 11.4 / iOS 13.4 (with some replication of absent custom dependencies)

1) Made initial animation off

struct LoginView: View {
    @State var showProgress = false

// ... other code

    // no model provided so used same state for progress
    ProgressButton(action: {
        self.showProgress.toggle()     // << activate change !!!
    }, image: Image("password-lock"), text: L.Login.LoginSecurely, backgroundColor: self.isLoginButtonEnabled ? Color.primaryLightColor : Color.gray, showProgress: $showProgress) 

2) Add internal ring activating state inside progress button; same state used for hide/unhide and activating does not work, again, because when ring appeared there is no change (it is already true) so ring is not activated

@State private var actiavteRing = false     // << here !!
var body: some View {
    Button(action: action) {
        HStack {

            if showProgress {
                RingView(color: textColor, show: self.$actiavteRing) // !!
                    .frame(width: 25, height: 25)
                    .transition(.opacity)
                    .animation(.spring())
                    .onAppear { self.actiavteRing = true } // << 
                    .onDisappear { self.actiavteRing = false } // <<

3) in Ring fixed animation deactivation to avoid cumulative effect (.none does not work here), so

.rotationEffect(.degrees(show ? 360.0 : 0.0))
.animation(show ? Animation.linear(duration: 
    1.0).repeatForever(autoreverses: false) : .default)   // << here !!

这篇关于SwiftUI 动画在嵌入另一个视图时不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆