当子视图处于活动状态时,动画会在父视图上停止

问题描述 投票:0回答:1

我有一个父视图,其中包含一些带有动画的按钮,使它们以无限滚动的方式水平流动。单击时还有另一个按钮会打开工作表视图,但是当工作表视图处于活动状态时,即使我关闭工作表视图,动画也会停止并保持停止状态。

代码结构:HomeView包含显示表单的SearchButtonView和包含动画的HomeBodyView(QueryChipsView) 代码:

首页查看

struct HomeView: View {
    var body: some View {
        VStack{
            // Header
            HeaderView()
            Spacer() // Push everything to the top
            Spacer()
            
            //Query chips and sign in button
            HomeBodyView()
            Spacer()
            Spacer()
            
            
            SearchButtonView()
        }
        
    }
}

搜索按钮视图

struct SearchButtonView: View {
    @StateObject var viewModel = SearchButtonViewVModel()
    var body: some View {
        Button(action: {
            viewModel.showSearchSheet.toggle()
        }) {
            Text("Ask anything...")
                .font(.system(size: 16))
                .fontWeight(.semibold)
                .foregroundColor(Color("FontColor"))
                .padding() // Adds internal padding to the text
                .frame(maxWidth: .infinity) // Make the button take full width
                .background(Color(.systemGray6))
                .cornerRadius(30)
                .shadow(radius: 1)
        }
        .padding()// Horizontal padding around the button
        .sheet(isPresented: $viewModel.showSearchSheet) {
            // Content of the sheet
          
            SearchView(showSearchSheet: $viewModel.showSearchSheet)
        }
        
    }
}

首页BodyView

struct HomeBodyView: View {
    var body: some View {
        VStack{
            Image("butler_logo_light") // Replace with your image asset name
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(height: 110) // Adjust the height as needed
                            .padding(.bottom, 10)
            Text("Search for your restaurant")
                .font(.system(size: 20))
                .fontWeight(.regular)
                .foregroundStyle(Color("FontColor"))

            // View that contains animation
            QueryChipsView()
        }.frame(width: UIScreen.main.bounds.width, height: 100)
            
    }
}

查询芯片视图

import SwiftUI

struct ButtonData: Identifiable, Hashable {
    let id = UUID()
    var text: String
}

struct QueryChipsView: View {
    let buttonData = [
        ["🕯️ Did Edison invent the lightbulb?", "🚴‍♂️ How do you ride a bike?"],
        ["⛷️ The best ski resorts in the Alps", "🏟️ The greatest Super Bowl moments", "🎬 Best movies of all time"],
        ["🥊 The best boxing style", "🐩 The best dog breed for apartments","🏖️ Top beach destinations"],
    ]
    

    var body: some View {
        
        // Apply the scrolling content as an overlay to a placeholder,
        // so that it is leading-aligned instead of center-aligned
        Color.clear
            .overlay(alignment: .leading) {
                VStack(alignment: .leading) {
                    ForEach(Array(DummyData().restaurantButtonData.enumerated()), id: \.offset) { rowIndex, buttonItems in
                        ScrollingButtonRow(buttonItems: buttonItems)
                        
                    }
                }
            }
            .frame(height: 200) // Constrain the height of the button area
    }
    
}

struct ScrollingButtonRow: View {
    let buttonItems: [String]
    let spacing: CGFloat = 14
    let baseSpeed: CGFloat = 50 // Speed of the scroll (points per second)
    @State private var offsetX: CGFloat = 0 // To move the buttons horizontally
    @Environment(\.colorScheme) var colorScheme
    var body: some View {
        HStack(spacing: spacing) {
            buttons
                .background {
                    
                    // Measure the width of one set of buttons and
                    // launch scrolling when the view appears
                    GeometryReader { proxy in
                        Color.clear
                            .onAppear {
                                startScrolling(scrolledWidth: proxy.size.width + spacing)
                            }
                            
                    }
                }
            
            // Repeat the buttons, so that the follow-on is seamless
            buttons
        }
        .offset(x: offsetX)
    }
    
    private var buttons: some View {
        HStack(spacing: spacing) {
            ForEach(Array(buttonItems.enumerated()), id: \.offset) { offset, item in
                Button {
                    // Button action
                } label: {
                    Text(item)
                        .padding(.vertical, 8)
                        .padding(.horizontal,15)
                        .fixedSize()
                        .foregroundStyle(Color("FontColor"))
                        .font(.system(size: 12))
                        .fontWeight(.light)
                        .background {
                            RoundedRectangle(cornerRadius: 5)
                                .fill(Color.gray.opacity(colorScheme == .dark ? 0.15 : 0.05))
                        }
                }
            }
        }
    }
    
    private func startScrolling(scrolledWidth: CGFloat) {
        withAnimation(
            .linear(duration: scrolledWidth / baseSpeed)
            .repeatForever(autoreverses: false)
        ) {
            offsetX = -scrolledWidth
        }
    }
}

#Preview {
    QueryChipsView()
}

我尝试将 showSearchSheet 变量绑定到 QueryChipsView(animation) 但不成功,还将 onChange 添加到 QueryChipsView 组件仍然不成功。

预期结果:从 SearchButtonView 打开工作表不应停止 QueryChipsView 的动画

实际结果:从 SearchButtonView 打开工作表会使 QueryChipsView 中的动画停止,并且即使关闭工作表也永远不会开始。

问题:是否有解决方法或者应该在工作表关闭时重建视图?

ios swift swiftui
1个回答
0
投票

所以这个问题是您的上一个问题的后续问题,我建议您使用

repeatForever 
而不是使用
Timer
来运行动画。

在装有 iOS 18.0 的 iPhone 16 模拟器上运行(并使用骨架代码来填补缺失代码的空白),我可以看到显示工作表时动画会跳跃。它有助于更改页面的布局,使动画位于上半部分,然后将

.presentationDetents([.medium])
应用于工作表内容,使其不超过半高。然而,使用临时代码,一旦工作表显示,动画似乎会自行纠正,并且在工作表关闭时也保持运行。

不过,我至少看到显示工作表时动画受到了影响。

我还建议您将

.geometryGroup()
应用于具有连续动画的视图部分,但是当我尝试此操作时,它似乎没有帮助。

所以我想知道,像您以前那样使用

Timer
是否会更好?要尝试这个,你只需要更改函数
startScrolling

private func startScrolling(scrolledWidth: CGFloat) {
    let step = baseSpeed * 0.05
    Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { timer in
        if abs(offsetX + scrolledWidth) < (step / 2) {
            offsetX += scrolledWidth // without animation
        }
        withAnimation(.linear(duration: 0.1)) {
            offsetX -= step
        }
    }
}

使用这种方法,我发现动画不如连续重复动画那么平滑,但它不会受到显示的工作表的影响。

如果这有帮助,那么使动画更平滑的一种方法可能是计算经过时间的偏移量,而不是每次计时器触发时总是从偏移量中扣除相同的步骤。另外,您可以考虑将计时器移到视图模型中,以便所有滚动的行都可以使用相同的计时器。请参阅此答案作为执行此操作的一种方法。

© www.soinside.com 2019 - 2024. All rights reserved.