在我的应用程序中,我有一个显示事件列表的屏幕。有一个View
从底部向上滑动,在列表上,所以用户可以快速添加额外的事件与DatePicker
,同时仍然看到列表。
的数据
我遇到过奇怪的行为:在没有底部安全区的设备上,如iPhone SE或iPhone 7 Plus,包含DatePicker
的View
的向上滑动动画是平滑的。
然而,在有底部安全区的设备上,如iPhone 14或iPhone 12,动画是跳跃的。虽然它是动画向上,它似乎有一个打嗝沿着。
我首先注意到这个问题在真实的设备上是完全可重现的,但在模拟器中似乎也是可见的。
当我删除DatePicker
时,动画是平滑的。
如何在具有底部安全区的设备上使此动画平滑?
下面是我的代码:
import SwiftUI
struct ContentView: View {
@State var panelPresented: Bool = false
var body: some View {
NavigationView {
ZStack {
List {
ForEach(0..<10, id: \.self) { index in
Text("Dummy Data \(index)")
}
}
.navigationBarTitle("List View")
.navigationBarItems(trailing: Button(action: {
withAnimation(.easeInOut(duration: 0.3)) {
panelPresented.toggle()
}
}) {
Image(systemName: "ellipsis.circle")
.imageScale(.large)
})
if panelPresented {
PanelView(isPresented: $panelPresented)
.transition(AnyTransition
.asymmetric(insertion: .move(edge: .bottom),
removal: .move(edge: .bottom)))
.animation(.easeInOut(duration: 0.3))
.zIndex(1)
}
}
}
}
}
struct PanelView: View {
@Binding var isPresented: Bool
@GestureState private var dragOffset: CGFloat = 0
@State private var panelVerticalOffset: CGFloat = 0
@State private var panelHeight: CGFloat = 0
private let dismissVelocityThreshold: CGFloat = 100
@State private var selectedDate: Date = Date()
var body: some View {
VStack(alignment: .center) {
Spacer()
VStack(spacing: 0) {
Toolbar(isPresented: $isPresented)
VStack {
Text("This is a panel where the user can quickly add events with a DatePicker.")
.padding()
Button {
//
} label: {
Text("Button")
.padding()
}
.buttonStyle(BorderedButtonStyle())
DatePicker("Pick date",
selection: $selectedDate)
.datePickerStyle(.wheel)
.labelsHidden()
}
}
.background(Color(UIColor.systemBackground))
.background(Color.white
.shadow(color: Color.secondary.opacity(0.5),
radius: 8,
x: 0,
y: 0)
)
}
.background(GeometryReader { geometry in
Color.clear
.preference(key: PanelHeightPreferenceKey.self, value: geometry.size.height)
})
.onPreferenceChange(PanelHeightPreferenceKey.self) { newValue in
panelHeight = newValue
}
.offset(y: isPresented ? (panelVerticalOffset + dragOffset) : panelHeight)
.animation(.easeInOut, value: panelVerticalOffset)
.gesture(
DragGesture(minimumDistance: 10, coordinateSpace: .local)
.updating($dragOffset) { value, state, _ in
if value.translation.height >= 0 {
state = value.translation.height
} else {
state = 0
}
}
.onEnded { value in
let predictedTranslationHeight = value.predictedEndLocation.y - value.location.y
if predictedTranslationHeight > dismissVelocityThreshold {
withAnimation {
isPresented = false
}
} else {
withAnimation(.easeInOut) {
panelVerticalOffset = 0
}
}
}
)
.onChange(of: isPresented) { newValue in
if newValue {
panelVerticalOffset = 0
}
}
}
struct Toolbar: View {
@Binding var isPresented: Bool
var body: some View {
ZStack {
Text("Data Entry Panel")
.font(.headline)
.padding()
HStack {
Spacer()
Button {
// Hide the panel
isPresented = false
} label: {
Text("Done")
.font(.headline)
}
.padding(.trailing)
}
}
.frame(maxWidth: .infinity)
.background(Color(UIColor.secondarySystemBackground))
}
}
struct PanelHeightPreferenceKey: PreferenceKey {
typealias Value = CGFloat
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
}
字符串
**更新:**根据Yrb在评论中的建议,我更新了代码,这样PanelView
就不会每次都被创建和销毁,而是使用.offset
修饰符而不是条件。
struct ContentView: View {
@State var panelPresented: Bool = false
var body: some View {
NavigationView {
ZStack {
List {
ForEach(0..<10, id: \.self) { index in
Text("Dummy Data \(index)")
}
}
.navigationBarTitle("List View")
.navigationBarItems(trailing: Button(action: {
withAnimation(.easeInOut(duration: 0.3)) {
panelPresented.toggle()
}
}) {
Image(systemName: "ellipsis.circle")
.imageScale(.large)
})
PanelView(isPresented: $panelPresented)
.transition(AnyTransition
.asymmetric(insertion: .move(edge: .bottom),
removal: .move(edge: .bottom)))
.animation(.easeInOut(duration: 0.3))
.zIndex(1)
.offset(y: panelPresented ? 0 : UIScreen.main.bounds.height)
}
}
}
}
型
然而,即使在此更改之后,PanelView
的视图生命周期与ContentView
相同,当面板内有DatePicker
时,面板上下滑动时动画中的跳跃仍然存在。
1条答案
按热度按时间uemypmqf1#
我注意到使用相同ZStack机制的自定义底部工作表也有同样的问题,我通过向
DatePicker
添加.compositingGroup()
修改器来修复动画过程中的“跳转”。我不确定技术上的含义,但我没有注意到任何奇怪的事情,选择器功能齐全。