VStack.swift 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. /// A view that arranges its subviews vertically.
  2. public struct VStack<Content: View>: View {
  3. static var defaultSpacing: Int { 10 }
  4. public var body: Content
  5. /// The amount of spacing to apply between children.
  6. private var spacing: Int
  7. /// The alignment of the stack's children in the horizontal direction.
  8. private var alignment: HorizontalAlignment
  9. /// Creates a vertical stack with the given spacing and alignment.
  10. ///
  11. /// - Parameters:
  12. /// - alignment: The alignment of the stack's children in the horizontal
  13. /// direction.
  14. /// - spacing: The amount of spacing to apply between children.
  15. /// - content: The content of the stack.
  16. public init(
  17. alignment: HorizontalAlignment = .center,
  18. spacing: Int? = nil,
  19. @ViewBuilder content: () -> Content
  20. ) {
  21. self.init(alignment: alignment, spacing: spacing, content: content())
  22. }
  23. /// Creates a vertical stack with the given spacing and alignment.
  24. ///
  25. /// - Parameters:
  26. /// - alignment: The alignment of the stack's children in the horizontal
  27. /// direction.
  28. /// - spacing: The amount of spacing to apply between children.
  29. /// - content: The content of the stack.
  30. init(
  31. alignment: HorizontalAlignment = .center,
  32. spacing: Int? = nil,
  33. content: Content
  34. ) {
  35. body = content
  36. self.spacing = spacing ?? Self.defaultSpacing
  37. self.alignment = alignment
  38. }
  39. public func asWidget<Backend: BaseAppBackend>(
  40. _ children: any ViewGraphNodeChildren,
  41. backend: Backend
  42. ) -> Backend.Widget {
  43. let vStack = backend.createContainer()
  44. for (index, child) in children.widgets(for: backend).enumerated() {
  45. backend.insert(child, into: vStack, at: index)
  46. }
  47. return vStack
  48. }
  49. public func computeLayout<Backend: BaseAppBackend>(
  50. _ widget: Backend.Widget,
  51. children: any ViewGraphNodeChildren,
  52. proposedSize: ProposedViewSize,
  53. environment: EnvironmentValues,
  54. backend: Backend
  55. ) -> ViewLayoutResult {
  56. if !(children is TupleViewChildren || children is EmptyViewChildren) {
  57. // TODO: Make layout caching a ViewGraphNode feature so that we can handle
  58. // these edge cases without a second thought. Would also make introducing
  59. // a port of SwiftUI's Layout protocol much easier.
  60. logger.warning(
  61. "VStack will not function correctly with non-TupleView content",
  62. metadata: [
  63. "childrenType": "\(type(of: children))",
  64. "contentType": "\(Content.self)",
  65. ]
  66. )
  67. }
  68. var cache = (children as? TupleViewChildren)?.stackLayoutCache ?? StackLayoutCache.initial
  69. let result = LayoutSystem.computeStackLayout(
  70. container: widget,
  71. children: layoutableChildren(backend: backend, children: children),
  72. cache: &cache,
  73. proposedSize: proposedSize,
  74. environment: environment
  75. .with(\.layoutOrientation, .vertical)
  76. .with(\.layoutAlignment, alignment.asStackAlignment)
  77. .with(\.layoutSpacing, spacing),
  78. backend: backend
  79. )
  80. (children as? TupleViewChildren)?.stackLayoutCache = cache
  81. return result
  82. }
  83. public func commit<Backend: BaseAppBackend>(
  84. _ widget: Backend.Widget,
  85. children: any ViewGraphNodeChildren,
  86. layout: ViewLayoutResult,
  87. environment: EnvironmentValues,
  88. backend: Backend
  89. ) {
  90. var cache = (children as? TupleViewChildren)?.stackLayoutCache ?? StackLayoutCache.initial
  91. LayoutSystem.commitStackLayout(
  92. container: widget,
  93. children: layoutableChildren(backend: backend, children: children),
  94. cache: &cache,
  95. layout: layout,
  96. environment: environment
  97. .with(\.layoutOrientation, .vertical)
  98. .with(\.layoutAlignment, alignment.asStackAlignment)
  99. .with(\.layoutSpacing, spacing),
  100. backend: backend
  101. )
  102. (children as? TupleViewChildren)?.stackLayoutCache = cache
  103. }
  104. }