代码之家  ›  专栏  ›  技术社区  ›  New Dev

SwiftUI中的复杂对齐情况

  •  0
  • New Dev  · 技术社区  · 3 年前

    我有一个复杂的对齐情况,我希望有一些解决方案 alignmentGuide s、 但我想不通。

    我需要对齐下面的条目列表(以单色字体),这样作为一个组,它是水平居中的。

    而且 即使一行中的一个条目较长,它也以相同的方式定位,并沿前缘以相同的方法对齐:

    我不想硬编码任何尺寸或定位值。

    以下是一些要复制的简单代码:

    struct TestView: View {
    
        let values: [(String, String)] = [
            ("03", "30.123"),
            ("02", "33.222"),
            ("01", "completed")
        ]
    
        var body: some View {
            LazyVStack {
                ForEach(values, id: \.1) { tuple in 
                    HStack(spacing: 24) {
                        Text(tuple.0)
                        Text(tuple.1)
                    }
                }
            }
            .frame(width: 350, height: 250) // to simulate outer container dimensions
        }
    }
    
    0 回复  |  直到 3 年前
        1
  •  1
  •   swiftPunk    3 年前

    这里有一个简单的方法可以帮助您:

    struct ContentView: View {
        
        var body: some View {
            
            TestView()
    
        }
        
    }
    
    struct TestView: View {
        
        let values: [(String, String)] = [
            ("03", "30.123"),
            ("02", "33.222"),
            ("01", "completed")
        ]
        
        var body: some View {
            
            HStack(spacing: 24) {
                
                VStack { ForEach(values, id: \.1) { tuple in Text(tuple.0) } }
    
                VStack(alignment: .leading) { ForEach(values, id: \.1) { tuple in
    
                    Text("00.000")
                        .foregroundColor(Color.clear)
                        .overlay(Text(tuple.1).lineLimit(1).fixedSize(), alignment: .leading)
      
                } }
                
            }
            .font(Font.system(.body, design: .monospaced))
            .padding(5.0)
            .background(Color.pink.opacity(0.5).cornerRadius(5.0))
    
        }
    }
    

    enter image description here

        2
  •  0
  •   New Dev    3 年前

    我发现 解决这个问题的方法,尽管对我来说似乎很复杂。在这里发帖,但仍然希望有更好的解决方案。

    这个想法是使用一个带有伪内容的占位符(依赖于这是一个单空间字体的事实),并使用 anchorPreference 以围绕其前缘对齐。

    struct LeadingPreferenceKey: PreferenceKey {
       static var defaultValue: Anchor<CGPoint>? = nil
        
       static func reduce(value: inout Anchor<CGPoint>?, 
                          nextValue: () -> Anchor<CGPoint>?) {
          value = nextValue()
       }
    }
    

    然后,把它放进去 anchorReference ,并在中捕获 overlayPreferenceValue 和定位 offset(x:)

    LazyVStack(alignment: .center) {
       row(("00", "00.000")) // placeholder that's center-positioned
          .opacity(0)
          .anchorPreference(key: LeadingPreferenceKey.self,
                            value: .leading, , transform: { $0 })
    }
    .overlayPreferenceValue(LeadingPreferenceKey.self) { leading in
       GeometryReader { geo in 
          LazyVStack(alignment: .leading) {
             ForEach(values, id: \.1) { tuple in
                row(tuple)
             }
          }
          .offset(x: leading.map { geo[$0].x } ?? 0)
       }
    }
    
    func row(_ tuple: (String, String)) -> some View {
       HStack(spacing: 24) {
          Text(tuple.0)
          Text(tuple.1)
       }
    }
    
        3
  •  0
  •   Yrb    3 年前

    这里有一个解决方案,它将提供您的对齐方式以及对列宽度的控制。它是一个单独的视图,接受一个元组,并返回由width包含的HStack中的两个VStack:

    struct TwoItemLeadingAlignedColumn: View {
        let firstColumnItems: [String]
        let secondColumnItems: [String]
        let width: CGFloat?
        
        init(items: [(String, String)], width: CGFloat? = nil) {
            firstColumnItems = items.map { $0.0 }
            secondColumnItems = items.map { $0.1 }
            self.width = width
        }
        
        var body: some View {
            GeometryReader { geometry in
                HStack {
                    VStack(alignment: .center) {
                        ForEach(firstColumnItems, id: \.self) {item in
                            Text(item)
                        }
                    }
                    
                    Spacer()
                        .frame(width: geometry.size.width * 0.25)
                    
                    VStack(alignment: .leading) {
                        ForEach(secondColumnItems, id: \.self) {item in
                            Text(item)
                        }
                    }
                    Spacer()
                }
            .frame(width: width)
            }
        }
    }
        
    
    struct TestView: View {
        
        let values: [(String, String)] = [
            ("03", "30.123"),
            ("02", "33.222"),
            ("01", "completed")
        ]
        
        var body: some View {
            TwoItemLeadingAlignedColumn(items: values, width: 150)
                .frame(width: 350, height: 250) // to simulate outer container dimensions
        }
    }
    

    无论你是否使用等宽字体,这个答案都会起作用。

    更新:居中第一列