Search code examples
swiftswiftuialignmentswiftui-alignment-guide

How to align two elements in different views with leading edge in SwiftUI?


how do I align these two views?

I have this custom cell. I have tried different approach to align "Skill Range" and "Time" by adding .alignmentGuide(.leading) to both "Skill Range" and "Time" but seems not be working. How do I align "Skill Range" and "Time" start with same leading point? How do I align "Skill Range" leading edge to "Time" leading edge?

I have gone through multiple questions related to this. But No luck!! If anyone could help me, It would be great!

struct MatchInfoCell: View {
    // MARK: - Variables
    var matchListModel: MatchListModel
    var acceptButtonTitle: String? = "Accept Match"
    var acceptButtonAction: (()->Void)?
    
    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            HStack(spacing: 32) {
                VStack(alignment: .leading) {
                    // MARK: - Singles/Doubles
                    Text("Match Type")
                        .robotoRegularFont(size: 10)
                        .foregroundColor(Color.custom333333Color.opacity(0.5))
                    Text(self.matchListModel.singlesDoubles == "1" ? "Singles" : "Doubles")
                        .robotoMediumFont(size: 10)
                        .foregroundColor(Color.custom333333Color)
                }
                VStack(alignment: .leading) {
                    // MARK: - Skill Range
                    Text("Skill Range")
                        .robotoRegularFont(size: 10)
                        .foregroundColor(Color.custom333333Color.opacity(0.5))
                    Text("\(self.matchListModel.skillRangeMin) - \(self.matchListModel.skillRangeMax)")
                        .robotoMediumFont(size: 10)
                        .foregroundColor(Color.custom333333Color)
                }
                
                VStack(alignment: .leading) {
                    // MARK: - No of Participants
                    Text("Players")
                        .robotoRegularFont(size: 10)
                        .foregroundColor(Color.custom333333Color.opacity(0.5))
                    let combinedNames = self.matchListModel.nameOfParticipants
                        .filter { $0.firstName != "" && $0.lastName != "" }
                        .compactMap { "\($0.firstName) \($0.lastName.first ?? "a")" }
                    let str = combinedNames.joined(separator: ", ")
                    Text(str)
                        .robotoMediumFont(size: 10)
                        .foregroundColor(Color.custom333333Color)
                }
                .padding(.trailing)
            }
            VStack {
                HStack(spacing: 32) {
                    VStack(alignment: .leading) {
                        let dateStr = UtilityMethods.convertTimestampToDate(timestamp: TimeInterval(self.matchListModel.dateTime), format: E_D_MMM_YY)
                        // MARK: - Date
                        Text("Date:")
                            .robotoRegularFont(size: 10)
                            .foregroundColor(Color.custom333333Color.opacity(0.5))
                        Text(dateStr)
                            .robotoMediumFont(size: 10)
                            .foregroundColor(Color.custom333333Color)
                    }
                    
                    VStack(alignment: .leading) {
                        let timeStr = UtilityMethods.convertTimestampToDate(timestamp: TimeInterval(self.matchListModel.dateTime), format: HH_MM_A)
                        // MARK: - Time
                        Text("Time:")
                            .robotoRegularFont(size: 10)
                            .foregroundColor(Color.custom333333Color.opacity(0.5))
                        Text(timeStr)
                            .robotoMediumFont(size: 10)
                            .foregroundColor(Color.custom333333Color)
                    }
                }
            }
            Line()
                .stroke(style: StrokeStyle(lineWidth: 1, dash: [5]))
                .foregroundColor(Color.customCECECEColor)
                .frame(height: 1)
            
            HStack(spacing: 5) {
                // MARK: - Court
                Text("Court:")
                    .robotoRegularFont(size: 10)
                    .foregroundColor(Color.custom333333Color.opacity(0.5))
                Text(self.matchListModel.homeCourt)
                    .robotoMediumFont(size: 12)
                    .foregroundColor(Color.custom333333Color)
            }
            HStack {
                Spacer()
                
                // MARK: - Button Accept Match
                Button {
                    self.acceptButtonAction?()
                } label: {
                    Text(self.acceptButtonTitle ?? "")
                        .robotoMediumFont(size: 10)
                        .foregroundColor(.white)
                        .frame(width: 88, height: 30)
                        .background(Color.custom64B054Color)
                        .cornerRadius(5)
                }
            }
        }
        .padding(.all, 10)
        .frame(maxWidth: .infinity)
        .overlay(
            RoundedRectangle(cornerRadius: 9)
                .stroke(Color.custom64B054Color, lineWidth: 1)
        )
        .background(
            RoundedRectangle(
                cornerRadius: 9
            )
            .foregroundColor(Color.white)
            .shadow(
                color: Color.black.opacity(0.25),
                radius: 9,
                x: 0,
                y: 0
            )
        )
    }
}

Solution

  • The VStack with the detail only contain a label and a value. These pairs are being aligned correctly - the labels and values can be seen to be leading-aligned.

    To align the entries in the second row with the entries in the first, I would suggest re-arranging the stacks:

    instead of 2 HStack inside a VStack
    use 3 VStack inside an HStack

    VStack(alignment: .leading, spacing: 12) {
        HStack(alignment: .top, spacing: 32) {
            VStack(alignment: .leading, spacing: 12) {
                VStack(alignment: .leading) {
                    // MARK: - Singles/Doubles
                }
    
                VStack(alignment: .leading) {
                    // MARK: - Date
                }
            }
    
            VStack(alignment: .leading, spacing: 12) {
                VStack(alignment: .leading) {
                    // MARK: - Skill Range
                }
    
                VStack(alignment: .leading) {
                    // MARK: - Time
                }
            }
    
            VStack(alignment: .leading) {
                // MARK: - No of Participants
            }
        }
        Line()
        // content below line
    }
    

    Screenshot

    A possible advantage of this approach vs. a grid, is that the last column with the names of the players can have more height than the entries in the first two columns, without causing the bottom row to move down. For example, if the names of the players would be on separate lines, the extra line(s) would not cause any change to the spacing of the first two columns.