ErasedViewGraphNode.swift 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import Foundation
  2. @MainActor
  3. public struct ErasedViewGraphNode {
  4. public var node: AnyObject
  5. /// If the new view doesn't have the same type as the old view then the returned
  6. /// value will have `viewTypeMatched` set to `false`, allowing views such as `AnyView`
  7. /// to choose how to react to a mismatch. In `AnyView`'s case this means throwing away
  8. /// the current view graph node and creating a new one for the new view type.
  9. public var computeLayoutWithNewView:
  10. (
  11. _ newView: Any?,
  12. _ proposedSize: ProposedViewSize,
  13. _ environment: EnvironmentValues
  14. ) -> (viewTypeMatched: Bool, size: ViewLayoutResult)
  15. /// The underlying view graph node's commit method.
  16. public var commit: () -> ViewLayoutResult
  17. public var getWidget: () -> AnyWidget
  18. public var viewType: any View.Type
  19. public var backendType: any BaseAppBackend.Type
  20. public init<V: View, Backend: BaseAppBackend>(
  21. for view: V,
  22. backend: Backend,
  23. snapshot: ViewGraphSnapshotter.NodeSnapshot? = nil,
  24. environment: EnvironmentValues
  25. ) {
  26. self.init(
  27. wrapping: ViewGraphNode(
  28. for: view,
  29. backend: backend,
  30. snapshot: snapshot,
  31. environment: environment
  32. )
  33. )
  34. }
  35. public init<V: View, Backend: BaseAppBackend>(
  36. wrapping node: ViewGraphNode<V, Backend>
  37. ) {
  38. self.node = node
  39. backendType = Backend.self
  40. viewType = V.self
  41. computeLayoutWithNewView = { view, proposedSize, environment in
  42. if let view {
  43. guard let view = view as? V else {
  44. return (false, ViewLayoutResult.leafView(size: .zero))
  45. }
  46. let size = node.computeLayout(
  47. with: view,
  48. proposedSize: proposedSize,
  49. environment: environment
  50. )
  51. return (true, size)
  52. } else {
  53. let size = node.computeLayout(
  54. with: nil,
  55. proposedSize: proposedSize,
  56. environment: environment
  57. )
  58. return (true, size)
  59. }
  60. }
  61. commit = node.commit
  62. getWidget = {
  63. return AnyWidget(node.widget)
  64. }
  65. }
  66. public init<V: View>(wrapping node: AnyViewGraphNode<V>) {
  67. self.init(wrapping: node, backend: node.getBackend())
  68. }
  69. private init<V: View, Backend: BaseAppBackend>(
  70. wrapping node: AnyViewGraphNode<V>,
  71. backend: Backend
  72. ) {
  73. self.init(wrapping: node.node as! ViewGraphNode<V, Backend>)
  74. }
  75. public func transform<R>(with transformer: any ErasedViewGraphNodeTransformer<R>) -> R {
  76. func helper<V: View, Backend: BaseAppBackend>(
  77. viewType: V.Type,
  78. backendType: Backend.Type
  79. ) -> R {
  80. transformer.transform(node: node as! ViewGraphNode<V, Backend>)
  81. }
  82. return helper(viewType: viewType, backendType: backendType)
  83. }
  84. }
  85. @MainActor
  86. public protocol ErasedViewGraphNodeTransformer<Return> {
  87. associatedtype Return
  88. func transform<V: View, Backend: BaseAppBackend>(node: ViewGraphNode<V, Backend>) -> Return
  89. }