AlertModifier.swift 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. extension View {
  2. public func alert(
  3. _ title: String,
  4. isPresented: Binding<Bool>,
  5. @AlertActionsBuilder actions: () -> [AlertAction]
  6. ) -> some View {
  7. AlertModifierView(
  8. child: self,
  9. title: title,
  10. isPresented: isPresented,
  11. actions: actions()
  12. )
  13. }
  14. public func alert(
  15. _ title: Binding<String?>,
  16. @AlertActionsBuilder actions: () -> [AlertAction]
  17. ) -> some View {
  18. AlertModifierView(
  19. child: self,
  20. title: title.wrappedValue ?? "",
  21. isPresented: Binding {
  22. title.wrappedValue != nil
  23. } set: { newValue in
  24. if !newValue {
  25. title.wrappedValue = nil
  26. }
  27. },
  28. actions: actions()
  29. )
  30. }
  31. }
  32. struct AlertModifierView<Child: View>: TypeSafeView {
  33. typealias Children = AlertModifierViewChildren<Child>
  34. var body = EmptyView()
  35. var child: Child
  36. var title: String
  37. var isPresented: Binding<Bool>
  38. var actions: [AlertAction]
  39. func children<Backend: BaseAppBackend>(
  40. backend: Backend,
  41. snapshots: [ViewGraphSnapshotter.NodeSnapshot]?,
  42. environment: EnvironmentValues
  43. ) -> Children {
  44. AlertModifierViewChildren(
  45. childNode: AnyViewGraphNode(
  46. ViewGraphNode(
  47. for: child,
  48. backend: backend,
  49. environment: environment
  50. )
  51. ),
  52. alert: nil
  53. )
  54. }
  55. func asWidget<Backend: BaseAppBackend>(
  56. _ children: Children,
  57. backend: Backend
  58. ) -> Backend.Widget {
  59. children.childNode.widget.into()
  60. }
  61. func computeLayout<Backend: BaseAppBackend>(
  62. _ widget: Backend.Widget,
  63. children: Children,
  64. proposedSize: ProposedViewSize,
  65. environment: EnvironmentValues,
  66. backend: Backend
  67. ) -> ViewLayoutResult {
  68. children.childNode.computeLayout(
  69. with: child,
  70. proposedSize: proposedSize,
  71. environment: environment
  72. )
  73. }
  74. func commit<Backend: BaseAppBackend>(
  75. _: Backend.Widget,
  76. children: AlertModifierViewChildren<Child>,
  77. layout: ViewLayoutResult,
  78. environment: EnvironmentValues,
  79. backend: Backend
  80. ) {
  81. func castBackend_inner<NewBackend: BaseAppBackend & BackendFeatures.Alerts>(_ backend: NewBackend) {
  82. _ = children.childNode.commit()
  83. if isPresented.wrappedValue && children.alert == nil {
  84. let alert = backend.createAlert()
  85. backend.updateAlert(
  86. alert,
  87. title: title,
  88. actionLabels: actions.map(\.label),
  89. environment: environment
  90. )
  91. backend.showAlert(
  92. alert,
  93. window: .some(environment.window! as! NewBackend.Window)
  94. ) { responseId in
  95. children.alert = nil
  96. isPresented.wrappedValue = false
  97. actions[responseId].action()
  98. }
  99. children.alert = alert
  100. } else if isPresented.wrappedValue == false && children.alert != nil {
  101. backend.dismissAlert(
  102. children.alert as! NewBackend.Alert,
  103. window: .some(environment.window! as! NewBackend.Window)
  104. )
  105. children.alert = nil
  106. }
  107. }
  108. guard let castedBackend = backend as? any (BaseAppBackend & BackendFeatures.Alerts) else {
  109. fatalError("Backend does not implement BackendFeatures.Alerts")
  110. }
  111. castBackend_inner(castedBackend)
  112. }
  113. }
  114. class AlertModifierViewChildren<Child: View>: ViewGraphNodeChildren {
  115. var childNode: AnyViewGraphNode<Child>
  116. var alert: Any?
  117. var widgets: [AnyWidget] {
  118. [childNode.widget]
  119. }
  120. var erasedNodes: [ErasedViewGraphNode] {
  121. [ErasedViewGraphNode(wrapping: childNode)]
  122. }
  123. init(
  124. childNode: AnyViewGraphNode<Child>,
  125. alert: Any?
  126. ) {
  127. self.childNode = childNode
  128. self.alert = alert
  129. }
  130. }