在SwiftUI中,当一个视图出现时禁用动画

6

在尝试在SwiftUI中显示自定义加载视图时,我遇到了一个问题。 我创建了一个自定义结构视图OrangeActivityIndicator:

struct OrangeActivityIndicator: View {
    var style = StrokeStyle(lineWidth: 6, lineCap: .round)
    @State var animate = false
    let orangeColor = Color.orOrangeColor
    let orangeColorOpaque = Color.orOrangeColor.opacity(0.5)
    
    init(lineWidth: CGFloat = 6) {
        style.lineWidth = lineWidth
    }
    
    var body: some View {
        ZStack {
            Circle()
                .trim(from: 0, to: 0.7)
                .stroke(
                    AngularGradient(gradient: .init(colors: [orangeColor, orangeColorOpaque]), center: .center), style: style
                )
                .rotationEffect(Angle(degrees: animate ? 360 : 0))
                .animation(Animation.linear(duration: 0.7).repeatForever(autoreverses: false))
        }.onAppear() {
            self.animate.toggle()
        }
    }
}

我在不同的屏幕或视图中使用它,但问题是它看起来很奇怪,例如,在应用程序的CampaignsView中,当服务器调用正在进行时,我会显示它。

struct CampaignsView: View {
    
    @ObservedObject var viewModel: CampaignsViewModel
    
    var body: some View {
        NavigationView {
            ZStack {
                VStack(spacing: 0) {
                    CustomNavigationBar(campaignsNumber: viewModel.cardCampaigns.count)
                        .padding([.leading, .trailing], 24)
                        .frame(height: 25)
                    CarouselView(x: $viewModel.x, screen: viewModel.screen, op: $viewModel.op, count: $viewModel.index, cardCampaigns: $viewModel.cardCampaigns).frame(height: 240)
                    CampaignDescriptionView(idx: viewModel.index, cardCampaigns: viewModel.cardCampaigns)
                        .padding([.leading, .trailing], 24)
                    Spacer()
                }
                .onAppear {
                    self.viewModel.getCombineCampaigns()
                }
                if viewModel.isLoading {
                    OrangeActivityIndicator()
                        .frame(width: 40, height: 40)
                }
            }
            .padding(.top, 34)
            .background(Color.orBackgroundGrayColor.edgesIgnoringSafeArea(.all))
            .navigationBarHidden(true)
        }
    }
}

指示器本身正在正确旋转,问题出现在它出现时,它会作为从屏幕底部到中间的翻译动画出现。这是我的viewModel与服务器调用和isLoading属性:

class CampaignsViewModel: ObservableObject {
    
    @Published var index: Int = 0
    @Published var cardCampaigns: [CardCampaign] =  [CardCampaign]()
    @Published var isLoading: Bool = false

    var cancellable: AnyCancellable?


    
    func getCombineCampaigns() {
        self.isLoading = true
        let campaignLoader = CampaignLoader()
        cancellable = campaignLoader.getCampaigns()
            .receive(on: DispatchQueue.main)
            //Handle Events operator is used for debugging.
            .handleEvents(receiveSubscription: { print("Receive subscription: \($0)") },
                          receiveOutput: { print("Receive output: \($0)") },
                          receiveCompletion: { print("Receive completion: \($0)") },
                          receiveCancel: { print("Receive cancel") },
                          receiveRequest: { print("Receive request: \($0)") })
            .sink { completion in
                switch completion {
                case .finished:
                    break
                case .failure(let error):
                    print(error)
                }
            } receiveValue: { campaignResult in
                self.isLoading = false
                guard let campaignsList = campaignResult.content else {
                    return
                }
                self.cardCampaigns = campaignsList.map { campaign in
                    return CardCampaign(campaign: campaign)
                }
                self.moveToFirstCard()
            }
    }
}

你有尝试在视图中使用.transition(.identity)进行连线转换吗?这会让你的转换更加流畅。 - YodagamaHeshan
你是指 OrangeActivityIndicator() .frame(width: 40, height: 40) .transition(.identity). ? 它不能工作 - Adrian Macarenco
2个回答

4

使用关联状态与动画

var body: some View {
    ZStack {
        Circle()
            .trim(from: 0, to: 0.7)
            .stroke(
                AngularGradient(gradient: .init(colors: [orangeColor, orangeColorOpaque]), center: .center), style: style
            )
            .rotationEffect(Angle(degrees: animate ? 360 : 0))
            .animation(Animation.linear(duration: 0.7)
                                .repeatForever(autoreverses: false), 
                       value: animate)                              // << here !!
    }.onAppear() {
        self.animate.toggle()
    }
}

1
它没有解决问题。这是问题发生的视频:https://vimeo.com/491538782 - Adrian Macarenco

1

SwiftUI 3 (iOS 15)

使用新的API

.animation(.default, value: isAppeared)

SwiftUI 1, 2

就像下面这样,当我第一次加载视图时,按钮会有动画效果。

Problem




所以,我使用了GeometryReader。

当虚拟物体的坐标改变时,动画被设置为nil。


RecordView.swift

struct RecordView: View {
    var body: some View {
        Button(action: { }) {
            Color(.red)
                .frame(width: 38, height: 38)
                .cornerRadius(6)
                .padding(34)
                .overlay(Circle().stroke(Color(.red), lineWidth: 8))
        }
        .buttonStyle(ButtonStyle1()) //  customize the button.
    }
}


ButtonStyle.swift

import SwiftUI

struct ButtonStyle1: ButtonStyle {

    // In my case, the @State isn't worked, so I used class.
    @ObservedObject private var data = ButtonStyle1Data()


    func makeBody(configuration: Self.Configuration) -> some View {
        ZStack {
            // Dummy for tracking the view status
            GeometryReader {
                Color.clear
                    .preference(key: FramePreferenceKey.self, value: $0.frame(in: .global))
            }
            .frame(width: 0, height: 0)
            .onPreferenceChange(FramePreferenceKey.self) { frame in
                guard !data.isAppeared else { return }

                // ⬇️ This is the key
                data.isLoaded = 0 <= frame.origin.x
            }
        
            // Content View
            configuration.label
                .opacity(configuration.isPressed ? 0.5 : 1)
                .scaleEffect(configuration.isPressed ? 0.92 : 1)
                .animation(data.isAppeared ? .easeInOut(duration: 0.18) : nil)
        }
    }
}

ButtonStyleData.swift

final class ButtonStyle1Data: ObservableObject {

    @Published var isAppeared = false

}

FramePreferenceKey.swift

struct FramePreferenceKey: PreferenceKey {

    static var defaultValue: CGRect = .zero

    static func reduce(value: inout CGRect, nextValue: () -> CGRect) {}
}

Result

Result


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接