为什么我的视图没有更新,即使使用了@State?

huangapple go评论113阅读模式
英文:

Why my view isn't updating, event with @State?

问题

我有这个EventDetailsView,其中包含许多TextFields、Pickers等。以前它运行得非常好,但现在,我不知道为什么,我的视图对任何更改都没有响应。我只能删除它,TextFields和Pickers都不起作用。

如果你想看其他文件的代码,请写下来。

以下是EventDetailsView的代码:

  1. import SwiftUI
  2. struct EventDetailsView: View {
  3. @EnvironmentObject var calendarViewModel: CalendarViewModel
  4. @Environment(\.dismiss) var dismiss
  5. @State private var event: Event // 我传递给这个视图的事件
  6. var body: some View {
  7. NavigationStack {
  8. GeometryReader { geo in
  9. VStack {
  10. // 所有的Pickers和TextFields都在这里,它们与事件绑定,所以我不显示它们,因为它们肯定不是问题所在。
  11. }
  12. Button("Delete Event", role: .destructive) {
  13. calendarViewModel.showingDeleteEventDialog = true
  14. }
  15. }
  16. .navigationTitle("Event Details")
  17. .navigationBarTitleDisplayMode(.inline)
  18. .toolbar {
  19. ToolbarItem(placement: .navigationBarLeading) {
  20. Button {
  21. dismiss()
  22. } label: {
  23. Label("Back", systemImage: "chevron.left")
  24. .labelStyle(.titleAndIcon)
  25. }
  26. }
  27. ToolbarItem(placement: .navigationBarTrailing) {
  28. Button("Save") {
  29. calendarViewModel.changeEvent(event)
  30. dismiss()
  31. }
  32. }
  33. }
  34. .alert("Failed to set a Notification.", isPresented: $calendarViewModel.showingAlertErrorAlert) {
  35. Button("Cancel", role: .cancel) {
  36. event.alert = .noAlerts
  37. calendarViewModel.setNoAlerts()
  38. }
  39. Button("Settings") {
  40. event.alert = .noAlerts
  41. calendarViewModel.setNoAlerts()
  42. calendarViewModel.openSettings()
  43. }
  44. } message: {
  45. Text("Please, enable notifications for this app in settings.")
  46. }
  47. .confirmationDialog("", isPresented: $calendarViewModel.showingDeleteEventDialog) {
  48. Button("Delete", role: .destructive) {
  49. calendarViewModel.deleteEvent(event)
  50. }
  51. Button("Cancel", role: .cancel) { }
  52. } message: {
  53. Text("Are you sure you want to delete this event?")
  54. }
  55. }
  56. }
  57. init(event: Event) {
  58. _event = State(wrappedValue: event) // 在这里传递事件并创建新的State对象
  59. }
  60. }
  61. struct EventDetailsView_Previews: PreviewProvider {
  62. static var previews: some View {
  63. EventDetailsView(event: Event.example)
  64. .environmentObject(CalendarViewModel())
  65. }
  66. }

还有Event结构体:

  1. struct Event: Identifiable, Codable, Hashable, Comparable {
  2. var id = UUID()
  3. var title: String
  4. var description: String
  5. var tag: EventTags
  6. var startDate: Date
  7. var endDate: Date
  8. var repeatEvent: RepeatEvents
  9. var alert: Alerts
  10. var isCritical: Bool
  11. var sound: String
  12. var soundVolume: Double
  13. var color: EventColors.RawValue
  14. var background: Backgrounds
  15. var backgroundLocations: BackgroundLocation
  16. static let example = Event(title: "Workout", description: "We need to get strong", tag: .workout, startDate: Date(), endDate: Date().addingTimeInterval(86400), repeatEvent: .everyDay, alert: .noAlerts, isCritical: false, sound: "", soundVolume: 0.5, color: "darkGray", background: .color, backgroundLocations: BackgroundLocation.example)
  17. var stringStartDate: String {
  18. let formatter = DateFormatter()
  19. formatter.dateFormat = "MMM d, HH:mm a"
  20. return formatter.string(from: startDate)
  21. }
  22. var stringEndDate: String {
  23. let formatter = DateFormatter()
  24. formatter.dateFormat = "MMM d, HH:mm a"
  25. return formatter.string(from: endDate)
  26. }
  27. var stringNotificationTime: String {
  28. var formattedStart = ""
  29. var formattedEnd = ""
  30. if Calendar.current.isDate(startDate, inSameDayAs: endDate) {
  31. formattedStart = startDate.formatted(date: .omitted, time: .shortened)
  32. formattedEnd = endDate.formatted(date: .omitted, time: .shortened)
  33. } else {
  34. let formatter = DateFormatter()
  35. formatter.dateFormat = "MMM d, h:mm a"
  36. formattedStart = formatter.string(from: startDate)
  37. formattedEnd = formatter.string(from: endDate)
  38. }
  39. return "\(formattedStart) - \(formattedEnd)"
  40. }
  41. var currentTime: Double {
  42. let hour = Double(Calendar.current.component(.hour, from: startDate))
  43. let time = hour / 24
  44. return time
  45. }
  46. // Sorting
  47. static func <(lhs: Event, rhs: Event) -> Bool {
  48. lhs.startDate < rhs.startDate
  49. }
  50. static func == (lhs: Event, rhs: Event) -> Bool {
  51. lhs.id == rhs.id
  52. }
  53. func hash(into hasher: inout Hasher) {
  54. hasher.combine(id)
  55. }
  56. // 这里只是更多的枚举
  57. }

当viewModel中的selectedEvent不为nil时,Event会从viewModel传递过来。

  1. .fullScreenCover(item: $calendarViewModel.selectedEvent) { event in
  2. EventDetailsView(event: event)
  3. }
英文:

I have this EventDetailsView, with many TextFields, Pickers, etc. Previously it was working fantastically, but now, I don't know why, my view isn't responding to any changes. I can only delete it, TextFields, and Pickers are not working.

If you want to see some other file code, right down)

Here is EventDetailsView:

  1. import SwiftUI
  2. struct EventDetailsView: View {
  3. @EnvironmentObject var calendarViewModel: CalendarViewModel
  4. @Environment(\.dismiss) var dismiss
  5. @State private var event: Event // My event that I am passing to this view
  6. var body: some View {
  7. NavigationStack {
  8. GeometryReader { geo in
  9. VStack {
  10. // All the Pickers and TextFields Are here, they are bound to the event, so I don&#39;t display them, because they are definitely not the problem.
  11. }
  12. Button(&quot;Delete Event&quot;, role: .destructive) {
  13. calendarViewModel.showingDeleteEventDialog = true
  14. }
  15. }
  16. .navigationTitle(&quot;Event Details&quot;)
  17. .navigationBarTitleDisplayMode(.inline)
  18. .toolbar {
  19. ToolbarItem(placement: .navigationBarLeading) {
  20. Button {
  21. dismiss()
  22. } label: {
  23. Label(&quot;Back&quot;, systemImage: &quot;chevron.left&quot;)
  24. .labelStyle(.titleAndIcon)
  25. }
  26. }
  27. ToolbarItem(placement: .navigationBarTrailing) {
  28. Button(&quot;Save&quot;) {
  29. calendarViewModel.changeEvent(event)
  30. dismiss()
  31. }
  32. }
  33. }
  34. .alert(&quot;Failed to set a Notification.&quot;, isPresented: $calendarViewModel.showingAlertErrorAlert) {
  35. Button(&quot;Cancel&quot;, role: .cancel) {
  36. event.alert = .noAlerts
  37. calendarViewModel.setNoAlerts()
  38. }
  39. Button(&quot;Settings&quot;) {
  40. event.alert = .noAlerts
  41. calendarViewModel.setNoAlerts()
  42. calendarViewModel.openSettings()
  43. }
  44. } message: {
  45. Text(&quot;Please, enable notifications for this app in settings.&quot;)
  46. }
  47. .confirmationDialog(&quot;&quot;, isPresented: $calendarViewModel.showingDeleteEventDialog) {
  48. Button(&quot;Delete&quot;, role: .destructive) {
  49. calendarViewModel.deleteEvent(event)
  50. }
  51. Button(&quot;Cancel&quot;, role: .cancel) { }
  52. } message: {
  53. Text(&quot;Are you sure you want to delete this event?&quot;)
  54. }
  55. }
  56. }
  57. }
  58. init(event: Event) {
  59. _event = State(wrappedValue: event) // Here I passing the event and creating the new State object
  60. }
  61. }
  62. struct EventDetailsView_Previews: PreviewProvider {
  63. static var previews: some View {
  64. EventDetailsView(event: Event.example)
  65. .environmentObject(CalendarViewModel())
  66. }
  67. }

Also Here is Event struct:

  1. struct Event: Identifiable, Codable, Hashable, Comparable {
  2. var id = UUID()
  3. var title: String
  4. var description: String
  5. var tag: EventTags
  6. var startDate: Date
  7. var endDate: Date
  8. var repeatEvent: RepeatEvents
  9. var alert: Alerts
  10. var isCritical: Bool
  11. var sound: String
  12. var soundVolume: Double
  13. var color: EventColors.RawValue
  14. var background: Backgrounds
  15. var backgroundLocations: BackgroundLocation
  16. static let example = Event(title: &quot;Workout&quot;, description: &quot;We need to get strong&quot;, tag: .workout, startDate: Date(), endDate: Date().addingTimeInterval(86400), repeatEvent: .everyDay, alert: .noAlerts, isCritical: false, sound: &quot;&quot;, soundVolume: 0.5, color: &quot;darkGray&quot;, background: .color, backgroundLocations: BackgroundLocation.example)
  17. var stringStartDate: String {
  18. let formatter = DateFormatter()
  19. formatter.dateFormat = &quot;MMM d, HH:mm a&quot;
  20. return formatter.string(from: startDate)
  21. }
  22. var stringEndDate: String {
  23. let formatter = DateFormatter()
  24. formatter.dateFormat = &quot;MMM d, HH:mm a&quot;
  25. return formatter.string(from: endDate)
  26. }
  27. var stringNotificationTime: String {
  28. var formattedStart = &quot;&quot;
  29. var formattedEnd = &quot;&quot;
  30. if Calendar.current.isDate(startDate, inSameDayAs: endDate) {
  31. formattedStart = startDate.formatted(date: .omitted, time: .shortened)
  32. formattedEnd = endDate.formatted(date: .omitted, time: .shortened)
  33. } else {
  34. let formatter = DateFormatter()
  35. formatter.dateFormat = &quot;MMM d, h:mm a&quot;
  36. formattedStart = formatter.string(from: startDate)
  37. formattedEnd = formatter.string(from: endDate)
  38. }
  39. return &quot;\(formattedStart) - \(formattedEnd)&quot;
  40. }
  41. var currentTime: Double {
  42. let hour = Double(Calendar.current.component(.hour, from: startDate))
  43. let time = hour / 24
  44. return time
  45. }
  46. // Sorting
  47. static func &lt;(lhs: Event, rhs: Event) -&gt; Bool {
  48. lhs.startDate &lt; rhs.startDate
  49. }
  50. static func == (lhs: Event, rhs: Event) -&gt; Bool {
  51. lhs.id == rhs.id
  52. }
  53. func hash(into hasher: inout Hasher) {
  54. hasher.combine(id)
  55. }
  56. // Here is just more enums
  57. }

Event got passed from my viewModel when it isn't nil.

  1. .fullScreenCover(item: $calendarViewModel.selectedEvent) { event in
  2. EventDetailsView(event: event)
  3. }

答案1

得分: 2

当将数据传递给子视图结构体时,你有两个选项。使用let进行只读访问,或者使用@Binding var进行读写访问,例如:

  1. struct EventDetailsView: View {
  2. @Binding var event: Event // 读写访问
  3. // let event: Event // 只读访问
  4. }

然而,在设计视图结构体时,你应该问自己:这个视图需要哪些数据来完成它的工作?因此,与其传递整个Event对象,最好只传递实际使用的数据。例如,在你的情况下,详细视图并不需要Event结构体的所有参数。这将提高变化检测的性能(因为只有当实际使用的Event属性发生变化时,才会调用body,而不是任何属性发生变化时),并且更容易创建预览(因为你不需要创建一个完整的测试Event对象)。代码示例如下:

  1. struct MyDetailView: View {
  2. let title: String
  3. @Binding var startDate: Date
  4. }

使用方式如下:

  1. MyDetailView(title: event.title, startDate: $event.startDate)
英文:

When passing data down to a child View struct you have 2 options. let for read access or @Binding var for write access, e.g.

  1. struct EventDetailsView: View {
  2. @Binding var event: Event // for read/write access
  3. // let event: Event // if only need read access

However, when designing your View structs, you should ask yourself - what data does this View need to do its job? Thus instead of passing in the whole Event it would be better to only pass in the data it actually uses. I.e. in your case the detail view doesn't need all those params of the Event struct. This will improve performance of the change detection (since body will only be called when the properties of the Event actually being used change rather than any property changing) and make it easier to create Previews (since you don't need to create a whole test Event). This could look like this:

  1. struct MyDetailView: View {
  2. let title: String
  3. @Binding var startDate: Date

And use it like:

  1. MyDetailView(title: event.title, startDate: $event.startDate)

huangapple
  • 本文由 发表于 2023年8月8日 23:56:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/76861263.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定