当视图出现在 SwiftUI 中时禁用动画
Posted
技术标签:
【中文标题】当视图出现在 SwiftUI 中时禁用动画【英文标题】:Disable animation when a view appears in SwiftUI 【发布时间】:2020-12-16 02:25:01 【问题描述】:尝试在 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)
指示器本身是正确旋转的,问题是当它出现时,它显示为从屏幕底部到中间的平移动画。这是我的带有服务器调用和 isLoading 属性的 viewModel:
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) 来提供有线转换。 你的意思是 OrangeActivityIndicator() .frame(width: 40, height: 40) .transition(.identity)。 ?它不起作用 【参考方案1】:使用关联状态的动画
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()
【讨论】:
它没有解决问题。这是问题发生的视频:vimeo.com/491538782【参考方案2】:SwiftUI 3 (ios 15)
使用新的 API
.animation(.default, value: isAppeared)
SwiftUI 1、2
如下所示,当我第一次加载视图时,按钮是动画的。
所以,我使用了 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)
结果
【讨论】:
以上是关于当视图出现在 SwiftUI 中时禁用动画的主要内容,如果未能解决你的问题,请参考以下文章