Skip to content

Commit aaf51f1

Browse files
committed
[iOS] - Fix layout of WrappingHStack when text becomes multi-line (#22608)
* Fix layout of WrappingHStack when text becomes multi-line. * Also fix spacing between items in the wrapping HStack on iOS 15 when line becomes multi-line. Signed-off-by: Brandon T <[email protected]>
1 parent 9a0cf9f commit aaf51f1

File tree

1 file changed

+78
-51
lines changed

1 file changed

+78
-51
lines changed

ios/brave-ios/Sources/AIChat/Components/Messages/AIChatSuggestionsView.swift

Lines changed: 78 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
// License, v. 2.0. If a copy of the MPL was not distributed with this
44
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
55

6-
import SwiftUI
76
import DesignSystem
7+
import SwiftUI
88

99
@available(iOS 16, *)
1010
struct WrappingHStack: Layout {
@@ -15,52 +15,62 @@ struct WrappingHStack: Layout {
1515
guard !subviews.isEmpty else {
1616
return .zero
1717
}
18-
18+
1919
let height = subviews.map { $0.sizeThatFits(proposal).height }.max() ?? 0.0
20-
20+
2121
var rowWidths = [CGFloat]()
2222
var currentRowWidth: CGFloat = 0
2323
subviews.forEach { subview in
24-
if currentRowWidth + hSpacing + subview.sizeThatFits(proposal).width >= proposal.width ?? 0.0 {
24+
if currentRowWidth + hSpacing + subview.sizeThatFits(proposal).width >= proposal.width ?? 0.0
25+
{
2526
rowWidths.append(currentRowWidth)
2627
currentRowWidth = subview.sizeThatFits(proposal).width
2728
} else {
2829
currentRowWidth += hSpacing + subview.sizeThatFits(proposal).width
2930
}
3031
}
31-
32+
3233
rowWidths.append(currentRowWidth)
33-
34+
3435
let rowCount = CGFloat(rowWidths.count)
35-
return CGSize(width: max(rowWidths.max() ?? 0.0, proposal.width ?? 0.0), height: rowCount * height + (rowCount - 1) * vSpacing)
36+
return CGSize(
37+
width: max(rowWidths.max() ?? 0.0, proposal.width ?? 0.0),
38+
height: rowCount * height + (rowCount - 1) * vSpacing
39+
)
3640
}
3741

38-
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
42+
func placeSubviews(
43+
in bounds: CGRect,
44+
proposal: ProposedViewSize,
45+
subviews: Subviews,
46+
cache: inout ()
47+
) {
3948
let height = subviews.map { $0.dimensions(in: proposal).height }.max() ?? 0.0
40-
49+
4150
guard !subviews.isEmpty else {
4251
return
4352
}
44-
53+
4554
var x = bounds.minX
46-
var y = height / 2.0 + bounds.minY
47-
55+
var y = height + bounds.minY
56+
4857
subviews.forEach { subview in
4958
x += subview.dimensions(in: proposal).width / 2.0
50-
59+
5160
if x + subview.dimensions(in: proposal).width / 2.0 > bounds.maxX {
5261
x = bounds.minX + subview.dimensions(in: proposal).width / 2.0
53-
y += height + vSpacing
62+
y += subview.dimensions(in: proposal).height + vSpacing
5463
}
55-
56-
subview.place(at: CGPoint(x: x, y: y),
57-
anchor: .center,
58-
proposal: ProposedViewSize(
59-
width: subview.dimensions(in: proposal).width,
60-
height: subview.dimensions(in: proposal).height
61-
)
64+
65+
subview.place(
66+
at: CGPoint(x: x, y: y),
67+
anchor: .init(x: 0.5, y: 1.0),
68+
proposal: ProposedViewSize(
69+
width: subview.dimensions(in: proposal).width,
70+
height: subview.dimensions(in: proposal).height
71+
)
6272
)
63-
73+
6474
x += subview.dimensions(in: proposal).width / 2.0 + hSpacing
6575
}
6676
}
@@ -69,24 +79,30 @@ struct WrappingHStack: Layout {
6979
@available(iOS, introduced: 15.0, obsoleted: 16.0)
7080
struct WrappingHStackOld<Model, V>: View where Model: Hashable, V: View {
7181
typealias ViewGenerator = (Model) -> V
72-
82+
7383
var models: [Model]
7484
var hSpacing: CGFloat
7585
var vSpacing: CGFloat
7686
var viewGenerator: ViewGenerator
7787
var proxy: GeometryProxy
7888

7989
@State private var viewHeight = CGFloat.infinity
80-
90+
8191
init(geometry: GeometryProxy, models: [Model], viewGenerator: @escaping ViewGenerator) {
8292
self.models = models
8393
self.viewGenerator = viewGenerator
8494
self.hSpacing = 2.0
8595
self.vSpacing = 2.0
8696
self.proxy = geometry
8797
}
88-
89-
init(geometry: GeometryProxy, models: [Model], hSpacing: Float, vSpacing: Float, viewGenerator: @escaping ViewGenerator) {
98+
99+
init(
100+
geometry: GeometryProxy,
101+
models: [Model],
102+
hSpacing: Float,
103+
vSpacing: Float,
104+
viewGenerator: @escaping ViewGenerator
105+
) {
90106
self.models = models
91107
self.viewGenerator = viewGenerator
92108
self.hSpacing = CGFloat(hSpacing)
@@ -108,25 +124,29 @@ struct WrappingHStackOld<Model, V>: View where Model: Hashable, V: View {
108124
ZStack(alignment: .topLeading) {
109125
ForEach(models, id: \.self) { model in
110126
viewGenerator(model)
111-
.padding(.horizontal, hSpacing)
112-
.padding(.vertical, vSpacing)
113-
.alignmentGuide(.leading, computeValue: { dimension in
114-
if abs(width - dimension.width) > geometry.size.width {
115-
width = 0.0
116-
height -= dimension.height
127+
.alignmentGuide(
128+
.leading,
129+
computeValue: { dimension in
130+
if abs(width - dimension.width) > geometry.size.width {
131+
width = 0.0
132+
height -= dimension.height + vSpacing
133+
}
134+
135+
let result = width
136+
width = model == models.last! ? 0.0 : width - dimension.width - hSpacing
137+
return result
117138
}
118-
119-
let result = width
120-
width = model == models.last! ? 0.0 : width - dimension.width
121-
return result
122-
})
123-
.alignmentGuide(.top, computeValue: { dimension in
124-
let result = height
125-
if model == models.last! {
126-
height = 0.0
139+
)
140+
.alignmentGuide(
141+
.top,
142+
computeValue: { dimension in
143+
let result = height + dimension.height
144+
if model == models.last! {
145+
height = 0.0
146+
}
147+
return result
127148
}
128-
return result
129-
})
149+
)
130150
}
131151
}
132152
}
@@ -136,7 +156,7 @@ struct AIChatSuggestionsButton: View {
136156
var title: String
137157
var isLoading: Bool
138158
var onSuggestionPressed: (() -> Void)?
139-
159+
140160
var body: some View {
141161
Button {
142162
onSuggestionPressed?()
@@ -147,7 +167,7 @@ struct AIChatSuggestionsButton: View {
147167
.foregroundColor(Color(braveSystemName: .textInteractive))
148168
.multilineTextAlignment(.leading)
149169
.padding(12.0)
150-
170+
151171
if isLoading {
152172
ProgressView()
153173
.tint(Color(braveSystemName: .textInteractive))
@@ -168,8 +188,12 @@ struct AIChatSuggestionsView: View {
168188
var suggestions: [String]
169189
var onSuggestionPressed: ((String) -> Void)?
170190
private var proxy: GeometryProxy
171-
172-
init(geometry: GeometryProxy, suggestions: [String], onSuggestionPressed: ((String) -> Void)? = nil) {
191+
192+
init(
193+
geometry: GeometryProxy,
194+
suggestions: [String],
195+
onSuggestionPressed: ((String) -> Void)? = nil
196+
) {
173197
self.suggestions = suggestions
174198
self.proxy = geometry
175199
self.onSuggestionPressed = onSuggestionPressed
@@ -201,10 +225,13 @@ struct AIChatSuggestionsView_Preview: PreviewProvider {
201225
GeometryReader { geometry in
202226
AIChatSuggestionsView(
203227
geometry: geometry,
204-
suggestions: ["What Bluetooth version does it use?",
205-
"Summarize this page?",
206-
"What is Leo?",
207-
"What can the Leo assistant do for me?"])
228+
suggestions: [
229+
"What Bluetooth version does it use?",
230+
"Summarize this page?",
231+
"What is Leo?",
232+
"What can the Leo assistant do for me?",
233+
]
234+
)
208235
.previewLayout(.sizeThatFits)
209236
}
210237
}

0 commit comments

Comments
 (0)