改变引用类型不适用于 .animation(_:value:)

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

当要变异的值是引用类型时,如何对视图进行动画更改?
在下面的示例中,更改提醒时间会更新顺序,但没有动画。
我认为数组是值类型,但也许 event.reminders 是一个引用,因此不会触发更新?如果是这样,我猜 .animation(_:value:) 不是正确的方法?
有哪些替代方案?

import SwiftUI

struct MissingAnimationWhenChangingArrayOrder: View {
    let event: Event
    var body: some View {
        ForEach(event.reminders.sorted { $0.dateTime < $1.dateTime }) { reminder in
            ReminderView(reminder: reminder)
        }
        .animation(.spring, value: event.reminders)
    }
}

#Preview {
    var event = Event(
        reminders: [
            Reminder(title: "Meeting 1", dateTime: .now),
            Reminder(title: "Meeting 2", dateTime: .now)
        ]
    )
    return MissingAnimationWhenChangingArrayOrder(event: event)
}

struct ReminderView: View {
    @Bindable var reminder: Reminder
    var body: some View {
        DatePicker("", selection: $reminder.dateTime)
        .frame(width: 200)
    }
}

@Observable
class Event {
    var reminders: [Reminder]
    init(reminders: [Reminder]) {
        self.reminders = reminders
    }
}

@Observable
class Reminder: Identifiable, Equatable {
    let id = UUID()
    var title: String
    var dateTime: Date
    init(title: String, dateTime: Date) {
        self.title = title
        self.dateTime = dateTime
    }

    static func == (lhs: Reminder, rhs: Reminder) -> Bool {
        lhs.id == rhs.id
    }
}
swiftui swiftui-animation
1个回答
0
投票

这确实是因为引用类型语义。

.animation
修改器的工作方式是在视图更新后检查其
value:
参数是否已更改(由
==
确定)。 SwiftUI 在视图更新之前存储
value:
的“旧版本”,然后通过调用
body
运行视图更新,然后将
value:
调用期间收到的新
body
与它的“旧值”进行比较有。如果它们不相等,则启动动画。

这对于值类型来说效果很好,但对于引用类型来说,

value:
的“旧版本”只是同一对象的reference的另一个副本!当您更改属性时,SwiftUI 存储的旧版本也会“更改”为相同的值,因为它是同一个实例。所以 SwiftUI 总是发现它们是相等的,除非你以非常错误的方式实现了
==
:)

请注意,即使

Reminder
是值类型,您的
==
实现也会阻止动画,因为它只比较 ID。更改提醒的日期不会导致提醒不相等。

为了解决这个问题,我将

map
将提醒数组设置为日期数组:

.animation(.spring, value: event.reminders.map(\.dateTime))
© www.soinside.com 2019 - 2024. All rights reserved.