ios - 在 HStack 中以正确的方式对齐两个 SwiftUI TextView

标签 ios swiftui

我有一个包含两行的简单 ListView 。

每行包含两个 TextView 。查看一和查看二。

我想对齐每行中的最后一个标签(查看两个),以便名称标签前导对齐,并且无论字体大小如何都保持对齐。

第一个标签(查看一个)也需要前导对齐。

我尝试在第一个标签(View One)上设置最小框架宽度,但它不起作用。设置最小宽度以及使 TextView 在 View 一中前导对齐似乎也是不可能的。

有什么想法吗?这在 UIKit 中相当简单。

List View

最佳答案

我找到了一种解决此问题的方法,该方法支持动态类型并且不麻烦。答案是使用PreferenceKeysGeometryReader !

此解决方案的本质是每个数字 Text 将具有根据其文本大小绘制的宽度。 GeometryReader 可以检测到这个宽度,然后我们可以使用 PreferenceKey 将其冒泡到列表本身,其中可以跟踪最大宽度,然后分配给每个数字Text 的框架宽度。

PreferenceKey 是您使用关联类型创建的类型(可以是符合 Equatable 的任何结构,这是您存储有关首选项的数据的位置),即附加到任何View,当它附加时,它会在 View 树中向上冒泡,并且可以使用.onPreferenceChange(PreferenceKeyType.self)在祖先 View 中监听。

首先,我们将创建 PreferenceKey 类型及其包含的数据:

struct WidthPreferenceKey: PreferenceKey {
    typealias Value = [WidthPreference]
    
    static var defaultValue: [WidthPreference] = []
    
    static func reduce(value: inout [WidthPreference], nextValue: () -> [WidthPreference]) {
        value.append(contentsOf: nextValue())
    }
}

struct WidthPreference: Equatable {
    let width: CGFloat
}

接下来,我们将创建一个名为 WidthPreferenceSettingView 的 View ,它将附加到我们想要调整大小的任何内容的背景(在本例中为数字标签)。这将负责设置首选项,该首选项将通过 PreferenceKeys 传递此数字标签的首选宽度。

struct WidthPreferenceSettingView: View {
    var body: some View {
        GeometryReader { geometry in
            Rectangle()
                .fill(Color.clear)
                .preference(
                    key: WidthPreferenceKey.self,
                    value: [WidthPreference(width: geometry.frame(in: CoordinateSpace.global).width)]
                )
        }
    }
}

最后是列表本身!我们有一个 @State 变量,它是数字“列”的宽度(实际上不是一列,因为数字不会直接影响代码中的其他数字)。通过 .onPreferenceChange(WidthPreference.self) ,我们监听我们创建的首选项的变化,并将最大宽度存储在我们的宽度状态中。绘制所有数字标签并由 GeometryReader 读取其宽度后,宽度会向上传播,最大宽度由 .frame(width: width)

指定
struct ContentView: View {
    @State private var width: CGFloat? = nil
    
    var body: some View {
        List {
            HStack {
                Text("1. ")
                    .frame(width: width, alignment: .leading)
                    .lineLimit(1)
                    .background(WidthPreferenceSettingView())
                Text("John Smith")
            }
            HStack {
                Text("20. ")
                    .frame(width: width, alignment: .leading)
                    .lineLimit(1)
                    .background(WidthPreferenceSettingView())
                Text("Jane Done")
            }
            HStack {
                Text("2000. ")
                    .frame(width: width, alignment: .leading)
                    .lineLimit(1)
                    .background(WidthPreferenceSettingView())
                Text("Jax Dax")
            }
        }.onPreferenceChange(WidthPreferenceKey.self) { preferences in
            for p in preferences {
                let oldWidth = self.width ?? CGFloat.zero
                if p.width > oldWidth {
                    self.width = p.width
                }
            }
        }
    }
}

如果您有多列数据,缩放的一种方法是对列进行枚举或对它们进行索引,并且宽度的 @State 将成为一个字典,其中每个键都是一列和 。 onPreferenceChange 与列的最大宽度的键值进行比较。

要显示结果,this就是打开较大文本后的样子,就像一个魅力:)。

这篇关于 PreferenceKey 和检查 View 树的文章有很大帮助:https://swiftui-lab.com/communicating-with-the-view-tree-part-1/

关于ios - 在 HStack 中以正确的方式对齐两个 SwiftUI TextView ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56588332/

相关文章:

iphone - 如何以编程方式为处于触摸状态的 UIButton 创建灰色覆盖?

ios - CloudStorageTools::serve() 音频无法在 iOS 上播放

ios - 如何检查一个实体是否与另一个实体相关

SwiftUI:onDelete 无法正确更新 UI

ios - Firebase FCM 获取旧 iOS APNS token 的 token

swift - 根据@State属性设置变量

swiftui - 为什么 SwiftUI "Form"会导致我的按钮转到屏幕底部?

IOS - 是否需要在发布前将 ATT 实现到应用程序中

SwiftUI 不会更新第二个 NavigationLink 目的地

ios - CKDatabaseSubscription 不推送通知