Flo*_*lle 36 ios swift swiftui hstack
是否有可能完全显示蓝色标签(当前被截断)然后自动换行?
NavigationLink(destination: GameListView()) {
VStack(alignment: .leading, spacing: 5){
// Name der Sammlung:
Text(collection.name)
.font(.headline)
// Optional: Für welche Konsolen bzw. Plattformen:
HStack(alignment: .top, spacing: 10){
ForEach(collection.platforms, id: \.self) { platform in
Text(platform)
.padding(.all, 5)
.font(.caption)
.background(Color.blue)
.foregroundColor(Color.white)
.cornerRadius(5)
.lineLimit(1)
}
}
}
.padding(.vertical, 10)
}
Run Code Online (Sandbox Code Playgroud)
此外,蓝色标签中不应有换行符:
这就是它最终的样子:
Asp*_*eri 38
Here is some approach of how this could be done using alignmentGuide(s). It is simplified to avoid many code post, but hope it is useful.
Update: There is also updated & improved variant of below solution in my answer for SwiftUI HStack with wrap and dynamic height
This is the result:
And here is full demo code (orientation is supported automatically):
import SwiftUI
struct TestWrappedLayout: View {
@State var platforms = ["Ninetendo", "XBox", "PlayStation", "PlayStation 2", "PlayStation 3", "PlayStation 4"]
var body: some View {
GeometryReader { geometry in
self.generateContent(in: geometry)
}
}
private func generateContent(in g: GeometryProxy) -> some View {
var width = CGFloat.zero
var height = CGFloat.zero
return ZStack(alignment: .topLeading) {
ForEach(self.platforms, id: \.self) { platform in
self.item(for: platform)
.padding([.horizontal, .vertical], 4)
.alignmentGuide(.leading, computeValue: { d in
if (abs(width - d.width) > g.size.width)
{
width = 0
height -= d.height
}
let result = width
if platform == self.platforms.last! {
width = 0 //last item
} else {
width -= d.width
}
return result
})
.alignmentGuide(.top, computeValue: {d in
let result = height
if platform == self.platforms.last! {
height = 0 // last item
}
return result
})
}
}
}
func item(for text: String) -> some View {
Text(text)
.padding(.all, 5)
.font(.body)
.background(Color.blue)
.foregroundColor(Color.white)
.cornerRadius(5)
}
}
struct TestWrappedLayout_Previews: PreviewProvider {
static var previews: some View {
TestWrappedLayout()
}
}
Run Code Online (Sandbox Code Playgroud)
Dan*_*iel 15
对我来说,所有答案都不起作用。要么是因为我有不同类型的元素,要么是因为周围的元素没有正确定位。因此,我最终实现了自己的 WrappingHStack,它的使用方式与 HStack 非常相似。您可以在GitHub 上找到它:WrappingHStack。
这是一个例子:
代码:
WrappingHStack {
Text("WrappingHStack")
.padding()
.font(.title)
.border(Color.black)
Text("can handle different element types")
Image(systemName: "scribble")
.font(.title)
.frame(width: 200, height: 20)
.background(Color.purple)
Text("and loop")
.bold()
WrappingHStack(1...20, id:\.self) {
Text("Item: \($0)")
.padding(3)
.background(Rectangle().stroke())
}.frame(minWidth: 250)
}
.padding()
.border(Color.black)
Run Code Online (Sandbox Code Playgroud)
小智 10
这是对蒂米答案的改进。它保留了所有可用的水平空间,修复了一个错误,即如果包装容器的元素少于一整行,它将保留大量空间,从而导致 UI 未对齐,并且还修复了导致内容溢出的错误一些边缘情况:
private struct WrappingHStack: Layout {
// inspired by: https://stackoverflow.com/a/75672314
private var horizontalSpacing: CGFloat
private var verticalSpacing: CGFloat
public init(horizontalSpacing: CGFloat, verticalSpacing: CGFloat? = nil) {
self.horizontalSpacing = horizontalSpacing
self.verticalSpacing = verticalSpacing ?? horizontalSpacing
}
public func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache _: inout ()) -> CGSize {
guard !subviews.isEmpty else { return .zero }
let height = subviews.map { $0.sizeThatFits(proposal).height }.max() ?? 0
var rowWidths = [CGFloat]()
var currentRowWidth: CGFloat = 0
subviews.forEach { subview in
if currentRowWidth + horizontalSpacing + subview.sizeThatFits(proposal).width >= proposal.width ?? 0 {
rowWidths.append(currentRowWidth)
currentRowWidth = subview.sizeThatFits(proposal).width
} else {
currentRowWidth += horizontalSpacing + subview.sizeThatFits(proposal).width
}
}
rowWidths.append(currentRowWidth)
let rowCount = CGFloat(rowWidths.count)
return CGSize(width: max(rowWidths.max() ?? 0, proposal.width ?? 0), height: rowCount * height + (rowCount - 1) * verticalSpacing)
}
public func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
let height = subviews.map { $0.dimensions(in: proposal).height }.max() ?? 0
guard !subviews.isEmpty else { return }
var x = bounds.minX
var y = height / 2 + bounds.minY
subviews.forEach { subview in
x += subview.dimensions(in: proposal).width / 2
if x + subview.dimensions(in: proposal).width / 2 > bounds.maxX {
x = bounds.minX + subview.dimensions(in: proposal).width / 2
y += height + verticalSpacing
}
subview.place(
at: CGPoint(x: x, y: y),
anchor: .center,
proposal: ProposedViewSize(
width: subview.dimensions(in: proposal).width,
height: subview.dimensions(in: proposal).height
)
)
x += subview.dimensions(in: proposal).width / 2 + horizontalSpacing
}
}
}
Run Code Online (Sandbox Code Playgroud)
你可以这样使用它:
struct MyView: View {
var body: some View {
WrappingHStack(horizontalSpacing: 5) {
Text("Hello")
Text("Hello again")
Text("Hello")
Text("Hello again")
Text("Hello")
Text("Hello again")
Text("Hello")
Text("Hello again")
Text("Hello")
Text("Hello again")
Text("Hello")
Text("Hello again")
}
}
}
Run Code Online (Sandbox Code Playgroud)
效果如下:
小智 5
我以前曾尝试过创造你所需要的东西。
我HStack
在 a 中使用过VStack
。
您传入一个geometryProxy
用于确定最大行宽度的。我将其传递进去,以便它可以在滚动视图中使用
我将 SwiftUI 视图包装在 UIHostingController 中以获取每个子项的大小。
然后,我循环遍历视图,将它们添加到行中,直到达到最大宽度,在这种情况下,我开始添加到新行。
这只是组合和输出 VStack 中的行的初始阶段和最后阶段
struct WrappedHStack<Content: View>: View {
private let content: [Content]
private let spacing: CGFloat = 8
private let geometry: GeometryProxy
init(geometry: GeometryProxy, content: [Content]) {
self.content = content
self.geometry = geometry
}
var body: some View {
let rowBuilder = RowBuilder(spacing: spacing,
containerWidth: geometry.size.width)
let rowViews = rowBuilder.generateRows(views: content)
let finalView = ForEach(rowViews.indices) { rowViews[$0] }
VStack(alignment: .center, spacing: 8) {
finalView
}.frame(width: geometry.size.width)
}
}
extension WrappedHStack {
init<Data, ID: Hashable>(geometry: GeometryProxy, @ViewBuilder content: () -> ForEach<Data, ID, Content>) {
let views = content()
self.geometry = geometry
self.content = views.data.map(views.content)
}
init(geometry: GeometryProxy, content: () -> [Content]) {
self.geometry = geometry
self.content = content()
}
}
Run Code Online (Sandbox Code Playgroud)
魔法就在这里发生
extension WrappedHStack {
struct RowBuilder {
private var spacing: CGFloat
private var containerWidth: CGFloat
init(spacing: CGFloat, containerWidth: CGFloat) {
self.spacing = spacing
self.containerWidth = containerWidth
}
func generateRows<Content: View>(views: [Content]) -> [AnyView] {
var rows = [AnyView]()
var currentRowViews = [AnyView]()
var currentRowWidth: CGFloat = 0
for (view) in views {
let viewWidth = view.getSize().width
if currentRowWidth + viewWidth > containerWidth {
rows.append(createRow(for: currentRowViews))
currentRowViews = []
currentRowWidth = 0
}
currentRowViews.append(view.erasedToAnyView())
currentRowWidth += viewWidth + spacing
}
rows.append(createRow(for: currentRowViews))
return rows
}
private func createRow(for views: [AnyView]) -> AnyView {
HStack(alignment: .center, spacing: spacing) {
ForEach(views.indices) { views[$0] }
}
.erasedToAnyView()
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是我使用的扩展
extension View {
func erasedToAnyView() -> AnyView {
AnyView(self)
}
func getSize() -> CGSize {
UIHostingController(rootView: self).view.intrinsicContentSize
}
}
Run Code Online (Sandbox Code Playgroud)
您可以在此处查看完整代码和一些示例: https://gist.github.com/kanesbetas/63e719cb96e644d31bf027194bf4ccdb
E.C*_*oms -4
您需要在文本视图之后立即处理线路配置。如果需要多行,请勿使用 lineLimit(1)。
HStack(alignment: .top, spacing: 10){
ForEach(collection.platforms, id: \.self) { platform in
Text(platform)
.fixedSize(horizontal: false, vertical: true)
.lineLimit(10)
.multilineTextAlignment(.leading)
.padding(.all, 5)
.font(.caption)
.background(Color.blue)
.foregroundColor(Color.white)
.cornerRadius(5)
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
13517 次 |
最近记录: |