4

I am having difficultly horizontally aligning two elements inside different container views. I want to align the two scores (in red) horizontally. I tried using a custom alignment guide (and custom CoordinateSpace), and although this did align the two scores, it also caused the corresponding stacks two change. What am I missing? Surely there must be an easy way to do this.

struct ContentViewExample: View {
    var body: some View {
        VStack(alignment: .leading) {
            HStack {
                Label("Team 1", systemImage: "seal")
                    .font(.title3)
                Spacer()
                Text("65")
                    .background(Color.red)
                Text("Final")
                    .font(.caption)
                    .padding(.leading)
            }
            HStack {
                Label("Team No 2", systemImage: "seal")
                    .font(.title3)
                Spacer()
                Text("70")
                    .background(Color.red)
            }
        }.padding(.horizontal)
    }
}

enter image description here

Cory
  • 2,302
  • 20
  • 33

2 Answers2

2

My solution would be to use a (Lazy?)VGrid:

struct Result : Identifiable, Hashable {
    var id = UUID()
    var name: String
    var label: String 
    var score: Int
    var final: Bool
}
var finalScore = [Result(name: "Team 1", label: "seal.fill", score: 167, final: true),
                  Result(name: "Team No 2", label: "seal", score: 65, final: false)]

struct ContentView: View {
    private var columns: [GridItem] = [
        GridItem(alignment: .leading),
        GridItem(alignment: .trailing),
        GridItem(alignment: .leading)
    ]
    
    var body: some View {
            LazyVGrid(
                columns: columns,
                alignment: .center,
                spacing: 16,
                pinnedViews: [.sectionHeaders, .sectionFooters]
            ) {
                Section(header: Text("Results").font(.title)) {
                    ForEach(finalScore) { thisScore in
                        Label(thisScore.name, systemImage: thisScore.label)
                            .background(Color(UIColor.secondarySystemBackground))
                        Text(String(thisScore.score))
                            .background(Color(UIColor.secondarySystemBackground))
                        Text(thisScore.final == true ? "Final" : "")
                            .background(Color(UIColor.secondarySystemBackground))
                    }
                }
            }
            .border(Color(.blue))
    }
}

Preview in SwiftUI Playground

gotta fiddle with the colors though, I just put some backgrounds and a border to see which things are where... And maybe optimize the column's widths if needed. To read up on Grids I suggest this: https://swiftwithmajid.com/2020/07/08/mastering-grids-in-swiftui/

And btw: custom alignment won't work because the entire HStack would be moved left and right, you'd have to adjust the width of the "columns" there, that would be extra hassle.

Gin Tonyx
  • 383
  • 1
  • 11
1

I decided to write a medium.com article to answer this question and did just that. Here is the solution looks like and here is the code too. Here is the article and well the solution.

https://marklucking.medium.com/a-real-world-alignment-challenges-in-swiftui-2-0-ff440dceae5a

In short I setup the four labels with the correct alignment and then aligned the four containers with each other.

In this code I included a slider so that you can better understand how it works.

enter image description here

import SwiftUI

struct ContentView: View {
private var newAlignment1H: HorizontalAlignment = .leading
private var newAlignment1V: VerticalAlignment = .top
private var newAlignment2H: HorizontalAlignment = .trailing
private var newAlignment2V: VerticalAlignment = .top

@State private var zeroX: CGFloat = 160

var body: some View {

    VStack {
        ZStack(alignment: .theAlignment) {
            HStack {
                Label {
                    Text("Team 1")
                        .font(.system(size: 16, weight: .semibold, design: .rounded))
                } icon: {
                    Image(systemName:"seal")
                        .resizable()
                        .scaledToFit()
                        .frame(width: 30)
                }.labelStyle(HorizontalLabelStyle())
            }
            .border(Color.blue)
            .alignmentGuide(.theHorizontalAlignment, computeValue: {d in zeroX})
//              .alignmentGuide(.theHorizontalAlignment, computeValue: {d in d[self.newAlignment2H]})
//              .alignmentGuide(.theVerticalAlignment, computeValue: {d in d[self.newAlignment1V]})
                HStack {
                    Text("65")
                        .font(.system(size: 16, weight: .semibold, design: .rounded))
                        .background(Color.red.opacity(0.2))
                        .frame(width: 128, height: 32, alignment: .trailing)
                        
                    Text("Final")
                        .font(.system(size: 16, weight: .semibold, design: .rounded))
                        .background(Color.red.opacity(0.2))
                        .frame(width: 48, height: 32, alignment: .leading)
                        
                }
                .border(Color.green)
        }
        
        ZStack(alignment: .theAlignment) {
            HStack {
                Label {
                    Text("Team No 2")
                        .font(.system(size: 16, weight: .semibold, design: .rounded))
                } icon: {
                    Image(systemName:"seal")
                        .resizable()
                        .scaledToFit()
                        .frame(width: 30)
                }.labelStyle(HorizontalLabelStyle())
                
            }
//                .alignmentGuide(.theHorizontalAlignment, computeValue: {d in d[self.newAlignment2H]})
//                .alignmentGuide(.theVerticalAlignment, computeValue: {d in d[self.newAlignment1V]})
            .alignmentGuide(.theHorizontalAlignment, computeValue: {d in zeroX})
            .border(Color.pink)
            
            HStack {
                Text("70")
                    .font(.system(size: 16, weight: .semibold, design: .rounded))
                    .background(Color.red.opacity(0.2))
                    .frame(width: 128, height: 32, alignment: .trailing)
                Text("")
                    .background(Color.red.opacity(0.2))
                    .frame(width: 48, height: 32, alignment: .leading)
            }
            .border(Color.orange)
        }
        
        VStack {
            Slider(value: $zeroX, in: 0...200, step: 10)
            Text("\(zeroX)")
        }
        
    }
    
    
}
}



struct VerticalLabelStyle: LabelStyle {
func makeBody(configuration: Configuration) -> some View {
    VStack(alignment: .center, spacing: 8) {
        configuration.icon
        configuration.title
    }
}
}

struct HorizontalLabelStyle: LabelStyle {
func makeBody(configuration: Configuration) -> some View {
    HStack(alignment: .center, spacing: 8) {
        configuration.icon
        configuration.title
    }
}
}

extension VerticalAlignment {
private enum TheVerticalAlignment : AlignmentID {
    static func defaultValue(in d: ViewDimensions) -> CGFloat {
        return d[VerticalAlignment.top]
    }
}

static let theVerticalAlignment = VerticalAlignment(TheVerticalAlignment.self)
}

extension HorizontalAlignment {
private enum TheHorizontalAlignment : AlignmentID {
    static func defaultValue(in d: ViewDimensions) -> CGFloat {
        return d[HorizontalAlignment.leading]
    }
}

static let theHorizontalAlignment = HorizontalAlignment(TheHorizontalAlignment.self)
}

extension Alignment {
static let theAlignment = Alignment(horizontal: .theHorizontalAlignment, vertical: .theVerticalAlignment)
}


struct ContentView_Previews: PreviewProvider {
static var previews: some View {
    ContentView()
}
}
user3069232
  • 8,587
  • 7
  • 46
  • 87