BackgroundModifier.swift 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. extension View {
  2. /// Sets the background of this view to another view.
  3. ///
  4. /// - Parameter background: The view to place behind this view.
  5. public func background<Background: View>(_ background: Background) -> some View {
  6. BackgroundModifier(background: background, foreground: self)
  7. }
  8. /// Layers views that you specify behind this view.
  9. ///
  10. /// - Parameter alignment: The alignment used to align the implicit ``ZStack``
  11. /// the stacks the background views.
  12. /// - Parameter content: A builder which declares views to display behind this
  13. /// view. The builder is implicitly the body of a ``ZStack``, leading to the
  14. /// views in the builder stacking in the Z direction.
  15. public func background<V: View>(
  16. alignment: Alignment = .center,
  17. @ViewBuilder content: () -> V
  18. ) -> some View {
  19. let zstack = ZStack(alignment: alignment, content: content)
  20. return BackgroundModifier(background: zstack, foreground: self)
  21. }
  22. }
  23. struct BackgroundModifier<Background: View, Foreground: View>: TypeSafeView {
  24. typealias Children = TupleView2<Background, Foreground>.Children
  25. var body: TupleView2<Background, Foreground>
  26. init(background: Background, foreground: Foreground) {
  27. body = TupleView2(background, foreground)
  28. }
  29. func children<Backend: BaseAppBackend>(
  30. backend: Backend,
  31. snapshots: [ViewGraphSnapshotter.NodeSnapshot]?,
  32. environment: EnvironmentValues
  33. ) -> TupleView2<Background, Foreground>.Children {
  34. body.children(backend: backend, snapshots: snapshots, environment: environment)
  35. }
  36. func layoutableChildren<Backend: BaseAppBackend>(
  37. backend: Backend,
  38. children: TupleView2<Background, Foreground>.Children
  39. ) -> [LayoutSystem.LayoutableChild] {
  40. []
  41. }
  42. func asWidget<Backend: BaseAppBackend>(
  43. _ children: TupleView2<Background, Foreground>.Children,
  44. backend: Backend
  45. ) -> Backend.Widget {
  46. body.asWidget(children, backend: backend)
  47. }
  48. func computeLayout<Backend: BaseAppBackend>(
  49. _ widget: Backend.Widget,
  50. children: TupleView2<Background, Foreground>.Children,
  51. proposedSize: ProposedViewSize,
  52. environment: EnvironmentValues,
  53. backend: Backend
  54. ) -> ViewLayoutResult {
  55. let foregroundResult = children.child1.computeLayout(
  56. with: body.view1,
  57. proposedSize: proposedSize,
  58. environment: environment
  59. )
  60. let foregroundSize = foregroundResult.size
  61. let backgroundResult = children.child0.computeLayout(
  62. with: body.view0,
  63. proposedSize: ProposedViewSize(foregroundSize),
  64. environment: environment
  65. )
  66. let backgroundSize = backgroundResult.size
  67. let frameSize = ViewSize(
  68. max(backgroundSize.width, foregroundSize.width),
  69. max(backgroundSize.height, foregroundSize.height)
  70. )
  71. // TODO: Investigate the ordering of SwiftUI's preference merging for
  72. // the background modifier.
  73. return ViewLayoutResult(
  74. size: frameSize,
  75. childResults: [backgroundResult, foregroundResult]
  76. )
  77. }
  78. public func commit<Backend: BaseAppBackend>(
  79. _ widget: Backend.Widget,
  80. children: TupleView2<Background, Foreground>.Children,
  81. layout: ViewLayoutResult,
  82. environment: EnvironmentValues,
  83. backend: Backend
  84. ) {
  85. let frameSize = layout.size
  86. let backgroundSize = children.child0.commit().size
  87. let foregroundSize = children.child1.commit().size
  88. let backgroundPosition = Alignment.center.position(
  89. ofChild: backgroundSize.vector,
  90. in: frameSize.vector
  91. )
  92. let foregroundPosition = Alignment.center.position(
  93. ofChild: foregroundSize.vector,
  94. in: frameSize.vector
  95. )
  96. backend.setPosition(ofChildAt: 0, in: widget, to: backgroundPosition)
  97. backend.setPosition(ofChildAt: 1, in: widget, to: foregroundPosition)
  98. backend.setSize(of: widget, to: frameSize.vector)
  99. }
  100. }