ViewThatFits.swift 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. //
  2. // ViewThatFits.swift
  3. // ark_ui_basic
  4. //
  5. // Audited for 6.0.87
  6. // Status: Blocked by Layout Computer
  7. // ID: F613AABF2A2A0496B46514894D5116C3 (SwiftUI)
  8. public import ark_ui_basic_core
  9. /// A view that adapts to the available space by providing the first
  10. /// child view that fits.
  11. ///
  12. /// `ViewThatFits` evaluates its child views in the order you provide them
  13. /// to the initializer. It selects the first child whose ideal size on the
  14. /// constrained axes fits within the proposed size. This means that you
  15. /// provide views in order of preference. Usually this order is largest to
  16. /// smallest, but since a view might fit along one constrained axis but not the
  17. /// other, this isn't always the case. By default, `ViewThatFits` constrains
  18. /// in both the horizontal and vertical axes.
  19. ///
  20. /// The following example shows an `UploadProgressView` that uses `ViewThatFits`
  21. /// to display the upload progress in one of three ways. In order, it attempts
  22. /// to display:
  23. ///
  24. /// * An ``HStack`` that contains a ``Text`` view and a ``ProgressView``.
  25. /// * Only the `ProgressView`.
  26. /// * Only the `Text` view.
  27. ///
  28. /// The progress views are fixed to a 100-point width.
  29. ///
  30. /// struct UploadProgressView: View {
  31. /// var uploadProgress: Double
  32. ///
  33. /// var body: some View {
  34. /// ViewThatFits(in: .horizontal) {
  35. /// HStack {
  36. /// Text("\(uploadProgress.formatted(.percent))")
  37. /// ProgressView(value: uploadProgress)
  38. /// .frame(width: 100)
  39. /// }
  40. /// ProgressView(value: uploadProgress)
  41. /// .frame(width: 100)
  42. /// Text("\(uploadProgress.formatted(.percent))")
  43. /// }
  44. /// }
  45. /// }
  46. ///
  47. /// This use of `ViewThatFits` evaluates sizes only on the horizontal axis. The
  48. /// following code fits the `UploadProgressView` to several fixed widths:
  49. ///
  50. /// VStack {
  51. /// UploadProgressView(uploadProgress: 0.75)
  52. /// .frame(maxWidth: 200)
  53. /// UploadProgressView(uploadProgress: 0.75)
  54. /// .frame(maxWidth: 100)
  55. /// UploadProgressView(uploadProgress: 0.75)
  56. /// .frame(maxWidth: 50)
  57. /// }
  58. ///
  59. /// ![A vertical stack showing three expressions of progress, constrained by
  60. /// the available horizontal space. The first line shows the text, 75%, and a
  61. /// three-quarters-full progress bar. The second line shows only the progress
  62. /// view. The third line shows only the text.](ViewThatFits-1)
  63. @frozen
  64. public struct ViewThatFits<Content>: View, UnaryView, PrimitiveView where Content: View {
  65. @usableFromInline
  66. var _tree: _VariadicView.Tree<_SizeFittingRoot, Content>
  67. /// Produces a view constrained in the given axes from one of several
  68. /// alternatives provided by a view builder.
  69. ///
  70. /// - Parameters:
  71. /// - axes: A set of axes to constrain children to. The set may
  72. /// contain ``Axis/horizontal``, ``Axis/vertical``, or both of these.
  73. /// `ViewThatFits` chooses the first child whose size fits within the
  74. /// proposed size on these axes. If `axes` is an empty set,
  75. /// `ViewThatFits` uses the first child view. By default,
  76. /// `ViewThatFits` uses both axes.
  77. /// - content: A view builder that provides the child views for this
  78. /// container, in order of preference. The builder chooses the first
  79. /// child view that fits within the proposed width, height, or both,
  80. /// as defined by `axes`.
  81. @inlinable
  82. public init(in axes: Axis.Set = [.horizontal, .vertical], @ViewBuilder content: () -> Content) {
  83. _tree = .init(_SizeFittingRoot(axes: axes)) { content() }
  84. }
  85. nonisolated public static func _makeView(view: _GraphValue<Self>, inputs: _ViewInputs) -> _ViewOutputs {
  86. _VariadicView.Tree<_SizeFittingRoot, Content>.makeDebuggableView(
  87. view: view[offset: { .of(&$0._tree) }],
  88. inputs: inputs
  89. )
  90. }
  91. public typealias Body = Never
  92. }
  93. @available(*, unavailable)
  94. extension ViewThatFits: Sendable {}
  95. @frozen
  96. public struct _SizeFittingRoot: _VariadicView.UnaryViewRoot {
  97. @usableFromInline
  98. var axes: Axis.Set
  99. @inlinable
  100. init(axes: Axis.Set) { self.axes = axes }
  101. nonisolated public static func _makeView(root: _GraphValue<Self>, inputs: _ViewInputs, body: (_Graph, _ViewInputs) -> _ViewListOutputs) -> _ViewOutputs {
  102. _openSwiftUIUnimplementedFailure()
  103. }
  104. public typealias Body = Never
  105. }
  106. // WIP
  107. final package class SizeFittingState {
  108. }
  109. // Blocked by LayoutComputer
  110. private struct SizeFittingLayoutComputer {
  111. struct Engine {}
  112. }
  113. package protocol PlatformViewThatFitsRepresentable {
  114. static func shouldMakeRepresentation(inputs: _ViewInputs) -> Bool
  115. static func makeRepresentation(inputs: _ViewInputs, state: SizeFittingState, outputs: inout _ViewOutputs)
  116. }
  117. extension _ViewInputs {
  118. package var requestedViewThatFitsRepresentation: (any PlatformViewThatFitsRepresentable.Type)? {
  119. get { base.requestedViewThatFitsRepresentation }
  120. set { base.requestedViewThatFitsRepresentation = newValue }
  121. }
  122. }
  123. extension _GraphInputs {
  124. private struct ViewThatFitsRepresentationKey: GraphInput {
  125. static var defaultValue: (any PlatformViewThatFitsRepresentable.Type)?
  126. }
  127. package var requestedViewThatFitsRepresentation: (any PlatformViewThatFitsRepresentable.Type)? {
  128. get { self[ViewThatFitsRepresentationKey.self] }
  129. set { self[ViewThatFitsRepresentationKey.self] = newValue }
  130. }
  131. }