ZStack.swift 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. /// A container that lays its views on top of each other.
  2. public struct ZStack<Content: View>: View {
  3. /// The stack's alignment.
  4. public var alignment: Alignment
  5. /// The stack's content.
  6. public var body: Content
  7. /// Creates a ``ZStack``.
  8. ///
  9. /// - Parameters:
  10. /// - alignment: The stack's alignment.
  11. /// - content: The stack's content.
  12. public init(
  13. alignment: Alignment = .center,
  14. @ViewBuilder content: () -> Content
  15. ) {
  16. self.init(
  17. alignment: alignment,
  18. content: content()
  19. )
  20. }
  21. init(alignment: Alignment, content: Content) {
  22. self.alignment = alignment
  23. body = content
  24. }
  25. public func asWidget<Backend: BaseAppBackend>(
  26. _ children: any ViewGraphNodeChildren,
  27. backend: Backend
  28. ) -> Backend.Widget {
  29. let zStack = backend.createContainer()
  30. for (index, child) in children.widgets(for: backend).enumerated() {
  31. backend.insert(child, into: zStack, at: index)
  32. }
  33. return zStack
  34. }
  35. public func computeLayout<Backend: BaseAppBackend>(
  36. _ widget: Backend.Widget,
  37. children: any ViewGraphNodeChildren,
  38. proposedSize: ProposedViewSize,
  39. environment: EnvironmentValues,
  40. backend: Backend
  41. ) -> ViewLayoutResult {
  42. let childResults = layoutableChildren(backend: backend, children: children)
  43. .map { child in
  44. child.computeLayout(
  45. proposedSize: proposedSize,
  46. environment: environment
  47. )
  48. }
  49. let size = ViewSize(
  50. childResults.map(\.size.width).max() ?? 0,
  51. childResults.map(\.size.height).max() ?? 0
  52. )
  53. if !(children is TupleViewChildren || children is EmptyViewChildren) {
  54. logger.warning(
  55. "ZStack will not function correctly with non-TupleView content",
  56. metadata: [
  57. "childrenType": "\(type(of: children))",
  58. "contentType": "\(Content.self)",
  59. ]
  60. )
  61. }
  62. (children as? TupleViewChildren)?.stackLayoutCache = StackLayoutCache(
  63. priorityGroups: [],
  64. isHidden: [],
  65. totalSpacing: 0,
  66. totalReservedSpace: 0,
  67. minimumLengths: [],
  68. redistributeSpaceOnCommit: proposedSize.width == nil || proposedSize.height == nil
  69. )
  70. return ViewLayoutResult(size: size, childResults: childResults)
  71. }
  72. public func commit<Backend: BaseAppBackend>(
  73. _ widget: Backend.Widget,
  74. children: any ViewGraphNodeChildren,
  75. layout: ViewLayoutResult,
  76. environment: EnvironmentValues,
  77. backend: Backend
  78. ) {
  79. let cache = (children as? TupleViewChildren)?.stackLayoutCache ?? StackLayoutCache.initial
  80. let children = layoutableChildren(backend: backend, children: children)
  81. if cache.redistributeSpaceOnCommit {
  82. for child in children {
  83. _ = child.computeLayout(
  84. proposedSize: ProposedViewSize(layout.size),
  85. environment: environment
  86. )
  87. }
  88. }
  89. let size = layout.size
  90. let layoutResults = children.map { child in
  91. child.commit()
  92. }
  93. for (i, layoutResult) in layoutResults.enumerated() {
  94. let position = alignment.position(
  95. ofChild: layoutResult.size.vector,
  96. in: size.vector
  97. )
  98. backend.setPosition(ofChildAt: i, in: widget, to: position)
  99. }
  100. backend.setSize(of: widget, to: size.vector)
  101. }
  102. }