我有一个父视图,其中包含一些带有动画的按钮,使它们以无限滚动的方式水平流动。单击时还有另一个按钮会打开工作表视图,但是当工作表视图处于活动状态时,即使我关闭工作表视图,动画也会停止并保持停止状态。
代码结构: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 中的动画停止,并且即使关闭工作表也永远不会开始。
问题:是否有解决方法或者应该在工作表关闭时重建视图?
所以这个问题是您的上一个问题的后续问题,我建议您使用
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
}
}
}
使用这种方法,我发现动画不如连续重复动画那么平滑,但它不会受到显示的工作表的影响。
如果这有帮助,那么使动画更平滑的一种方法可能是计算经过时间的偏移量,而不是每次计时器触发时总是从偏移量中扣除相同的步骤。另外,您可以考虑将计时器移到视图模型中,以便所有滚动的行都可以使用相同的计时器。请参阅此答案作为执行此操作的一种方法。