我希望在迭代数组的索引时进行转换。我想显示多个通知,7.5 秒后我想通过过渡显示新通知。我尝试了 withAnimation、.transition,但什么也没发生。我该如何解决它或者我做错了什么?
struct SoleInAppNotifications3: View {
@State private var currentIndex: Int = 0
@State private var isPaused: Bool = false
@State var hasImage: Bool? = true
@State var isActive: Bool = false
@State var isPressed: Bool = false
@State var isDismissed: Bool = false
private var notifications: [NotificationItem] = [NotificationItem(title: "Notification 1", subtitle: "Notification 1 subtitle"), NotificationItem(title: "Notification 2", subtitle: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, se"), NotificationItem(title: "Notification 3", subtitle: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, se") ]
let timer = Timer.publish(every: 1.5, on: .main, in: .common).autoconnect()
var body: some View {
HStack(spacing: .zero) {
if hasImage ?? false {
Image("raiff")
.padding(.trailing, 17)
}
VStack(alignment: .leading, spacing: .zero) {
Text(notifications[currentIndex].title)
.foregroundColor(.white)
.lineLimit(1)
.padding(.bottom, 1)
Text(notifications[currentIndex].subtitle)
.foregroundColor(.white)
.lineLimit(2)
}
Spacer()
VStack(spacing: .zero) {
ForEach(0..<notifications.count, id: \.self) { index in
bubbleView(isActive: index == currentIndex)
}
}
}
.frame(maxWidth: .infinity)
.padding(17)
.background(isPressed ? Color.red : Color.black)
.onReceive(timer) { timer in
if !isPaused, currentIndex < notifications.count - 1 {
withAnimation {
currentIndex = (currentIndex + 1)
}
} else {
currentIndex = 0
}
}
.transition(.scale)
}
private func bubbleView(isActive: Bool) -> some View {
ZStack {
if isActive {
RoundedRectangle(cornerRadius: 3)
.fill(Color.gray)
.frame(width: 6, height: 24)
.overlay(
RoundedRectangle(cornerRadius: 3).trim(from:0, to: progress())
.foregroundColor(Color.white)
.animation(.easeInOut, value: currentIndex)
)
} else {
Circle()
.fill(Color.gray)
.frame(width: 6, height: 6)
.padding(4)
.animation(.easeInOut, value: currentIndex)
}
}
}
func progress() -> Double {
return (CGFloat(currentIndex) / (CGFloat(notifications.count) - 1))
}
}
不会发生任何转换,因为即使视图的内容正在更改,相同的视图始终可见。
currentIndex
设置为带有通知文本的 id
上的 VStack
,以便当通知更改时它也会更改。transition
添加 VStack
修饰符。currentIndex
重置为 0 时的转换,请执行重置 withAnimation
:VStack(alignment: .leading, spacing: .zero) {
Text(notifications[currentIndex].title)
.foregroundColor(.white)
.lineLimit(1)
.padding(.bottom, 1)
Text(notifications[currentIndex].subtitle)
.foregroundColor(.white)
.lineLimit(2)
}
.id(currentIndex)
.transition(.push(from: .trailing))
.onReceive(timer) { timer in
if !isPaused, currentIndex < notifications.count - 1 {
withAnimation {
currentIndex = (currentIndex + 1)
}
} else {
withAnimation { // <- ADDED
currentIndex = 0
}
}
}
顺便说一句,
PhaseAnimator
可能是实现此类动画的另一种方式。