【问题标题】:SwiftUI Center Content alignment without supporting frames没有支撑框架的 SwiftUI 中心内容对齐
【发布时间】:2021-10-10 21:46:45
【问题描述】:

我在尝试将单个元素居中以模拟带有关闭按钮的导航模式时遇到了麻烦。 我想在不使用 supporting Rectangle 的侧面或垫片的情况下将内容居中。

我想要实现的是每当文本增长时,如果它到达有关闭 xmark 按钮的左侧,它应该尝试将自身推到有可用空间的右侧,直到它到达右边框和如果两边都没有可用空间,则在 wrap 之后。

这里有一些图片:

预期结果 1

预期结果 2

当前解决方案短文本

当前解决方案长文本

我尝试使用长文本和短文本来测试内容行为

目前这是代码的开始,基本上我想避免添加蓝色矩形(通常很清楚)

   struct TestAlignmentSwiftUIView: View {
        var body: some View {
            HStack(spacing: 0) {
                Rectangle().fill(Color.blue).frame(width: 44, height: 44)
    
                Text("aaa eee aaa")
                    .background(Color.red)
                    .padding(5)
    
                Button(action: {}, label: {
                    Image(systemName: "xmark")
                        .padding(15)
                        .frame(width: 44, height: 44)
                        .background(Color.yellow)
                })
            }
            .background(Color.green)
        }
    }

到目前为止我已经尝试过,但如果文本组件内的代码增长,则无法解决问题:

  1. 使用 zstack 我放置文本和关闭按钮之一 彼此顶部,但使用垫片将按钮推到侧面。它适用于小文本或内容,但如果文本增长则不可扩展
    var body: some View {
        ZStack {
            HStack {
                Spacer()
                Button(action: {}, label: {
                    Image(systemName: "xmark")
                        .padding(15)
                        .frame(width: 44, height: 44)
                        .background(Color.yellow)
                })
            }
            Text("aaa eee aaa random long very long text that should wrap without overlapping. long text")
                .background(Color.red)
                .frame(maxWidth: .infinity, alignment: .center)
                .padding(5)
                .opacity(0.7)
        }
        .background(Color.green)
    }
  1. 使用对齐指南: 我会创建自己的中心对齐指南,然后在我放置我的内容的 vstack 上使用此自定义对齐方式,加上一个假填充矩形,该矩形应该使元素在内容侧居中。

问题在于,据我所知,使用 swiftui 只能对齐一个后代元素,并且不支持在元素堆栈上进行多个自定义对齐。所以我只会让文本居中或侧边按钮对齐,而不是一个对齐到中心,另一个对齐到后缘。如果我在它们之间放置一个垫片,它只会弄乱创建的对齐方式。如果我尝试使用小文本,它们都会被附加。 这是代码。尝试评论按钮,您会看到它会自行居中或在它们之间添加间隔。

extension HorizontalAlignment {
    private enum MyAlignment: AlignmentID {
        static func defaultValue(in d: ViewDimensions) -> CGFloat {
            d[HorizontalAlignment.center]
        }
    }
    static let myAlignment = HorizontalAlignment(MyAlignment.self)
}
var body: some View {
        VStack(alignment: .myAlignment, spacing: 0) {
            HStack {
                Text("aaa eee aaa random  ")
                    .background(Color.red)
                    .frame(maxWidth: .infinity, alignment: .center)
                    .padding(5)
                    .alignmentGuide(.myhAlignment, computeValue: { dimension in
                        dimension[HorizontalAlignment.center]
                    })

                Button(action: {}, label: {
                    Image(systemName: "xmark")
                        .padding(15)
                        .frame(width: 44, height: 44)
                        .background(Color.yellow)
                })
            }
            .background(Color.green)
            Rectangle()
                .fill(Color.purple)
                .frame(width: 10, height: 10, alignment: .center)

                .alignmentGuide(.myhAlignment, computeValue: { dimension in
                    dimension[HorizontalAlignment.center]
                })
        }
    }

  1. 尝试使用几何阅读器和/或锚首选项的组合来阅读文本内容的大小和侧按钮宽度并手动应用适当的中心偏移,但它似乎太老套了,如果没有好的结果,它永远不会按预期工作

如果您熟悉 uikit,则可以使用 centerX 在具有次要布局优先级和从中心到右约束的容器上 关闭按钮,然后收工。但是在swiftui上似乎很难 处理这种简单的情况。

到目前为止,我还没有找到一个解决方案,而不使用侧面的支持固定框架,它可以同时处理长文本和短文本。如果您尝试使用长文本,该空间显然是可见的。它会让用户想知道为什么不使用。

¯\ (ツ)

编辑:在答案中添加了可能的解决方案

【问题讨论】:

  • 我只是希望我能像您在这里所做的那样作为新贡献者提出我的问题。没有任何其他用户试图纠正您的问题!我认为这需要很多运气。
  • 你试过玩.overlay
  • 是的,这取决于.overlay 是否意味着在侧面使用垫片,然后就像Spacer() Text() Spacer().overlay(Button) .overlay 的问题是我丢失了关于按钮大小。如果文本增长,它将重叠。不知道您是否打算使用 '.overlay(Geometryreader { ... ) ' 来获取 Button 的大小,但无论如何都会出现相同的重叠问题。
  • 您可以将 .hidden() 添加到蓝色矩形或 .opacity(0) 然后它会在保持布局不变的同时消失
  • 我认为您使用左侧的填充视图走在正确的轨道上。而且我认为当文本准备好包装时缩小蓝色方块会做你想做的事。 @Asperi 有 a solution to a similar problem 关于 TextFields 可能会给你你需要的东西。本质上,您使用另一个 Text() 并跟踪其大小,然后使用它来设置显示的 Text() 的大小。

标签: swift swiftui vertical-alignment


【解决方案1】:

根据 cmets 中的@Yrb 建议,这是我想出的缩小蓝色尺寸的方法,因此它将以可用空间为中心。 我在下面添加了一个假文本并跟踪了大小。如果它超出了可用空间,我将采取差异并缩小蓝色矩形。 需要记住的一件事是,如果隐藏的内容包含一些文本,则其 linelimit 应为 1,否则它会因自动换行而变得更小。

我只是假设我知道用于中心对齐的关闭按钮(或至少一侧)的大小,即使我在编译时不知道,我也可以使用首选项键来获取运行时的大小,并使其动态化。

但目前我认为我得到的结果很好。 但老实说,我希望将来能找到更容易的东西。

@State var text: String = "aaa eee aaa"
@State private var fillerWidth: CGFloat = 44
// i assume i know the max size of the close button or at least one side
private let kCloseButtonWidth: CGFloat = 44 

private struct FakeSizeTitlteContentKey: PreferenceKey {
    static var defaultValue: CGFloat { .zero }
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = nextValue()
    }
}

var body: some View {
    ZStack(alignment: Alignment(horizontal: .center, vertical: .top)) {
        GeometryReader { parentGeometry in
            titleContent
                .lineLimit(1) // hidden text must not wrap
                .overlay(GeometryReader { proxyFake in
                    Color.clear.border(Color.black, width: 0.3)
                        .preference(key: FakeSizeTitlteContentKey.self, value: proxyFake.frame(in: .local).width
                        .onPreferenceChange(FakeSizeTitlteContentKey.self) { value in
                            let availableW = parentGeometry.frame(in: .local).width
                            let fillSpace = availableW - value - kCloseButtonWidth * 2
                            fillerWidth = min(kCloseButtonWidth, max(0, fillSpace))
                        }
                })
        }
        .hidden()
        VStack {
            HStack(spacing: 0) {
                Rectangle()
                    .fill(Color.blue)
                    .frame(width: fillerWidth, height: 44)
                titleContent
                    .background(Color.green)
                    .multilineTextAlignment(.center)
                    .frame(maxWidth: .infinity, alignment: .center)
                Button(action: {}, label: {
                    Image(systemName: "xmark")
                        .padding(15)
                        .frame(width: kCloseButtonWidth, height: kCloseButtonWidth)
                        .background(Color.yellow)
                })
            }
            .coordinateSpace(name: "fullCont")
            .background(Color.green)
            
            TextEditor(text: $text)
                .frame(maxHeight: 150, alignment: .center)
                .border(Color.black, width: 1)
                .padding(15)
            Spacer()
        }
    }
}

@ViewBuilder var titleContent: some View {
    HStack(spacing: 0) {
        Text(text)
            .background(Color.red)
            .padding(.horizontal, 5)
    }
}

【讨论】:

    猜你喜欢
    • 2012-03-12
    • 2018-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-07
    • 1970-01-01
    • 1970-01-01
    • 2021-01-14
    相关资源
    最近更新 更多