Search code examples
swiftuialignmenthstack

align text in HStack


I need to make it so that no matter what the item.number is 1, 10, 100, 1000. and whether the number is highlighted - the item.userName is always exactly under each other, and the numbers are right-aligned.

struct ContentFirst: Identifiable {
        var id = UUID()
        let number: Int
        let userName: String
        let info: String
    }
    
    struct FirstViewFourthSegment: View {
        var contentFirst: [ContentFirst] = [ContentFirst(number: 5, userName: "user5", info: "123"), ContentFirst(number: 11, userName: "user11", info: "123"), ContentFirst(number: 3, userName: "user3", info: "123"),ContentFirst(number: 7, userName: "user7", info: "123"),ContentFirst(number: 8, userName: "user8", info: "123"),ContentFirst(number: 10, userName: "user10", info: "123"),ContentFirst(number: 2, userName: "user2", info: "123"),ContentFirst(number: 4, userName: "user4", info: "123"),ContentFirst(number: 6, userName: "user6", info: "123"),ContentFirst(number: 9, userName: "user9", info: "123"),ContentFirst(number: 12, userName: "user12", info: "123"),ContentFirst(number: 13, userName: "user13", info: "123"),ContentFirst(number: 15, userName: "user15", info: "123"),ContentFirst(number: 14, userName: "user14", info: "123"),ContentFirst(number: 16, userName: "user16", info: "123"),ContentFirst(number: 1, userName: "user1", info: "123")]
    
        var body: some View {
            VStack {
                List(contentFirst) { item in
                    HStack {
                        Text("\(item.number)")
                            .font(.system(size: 12, weight: .regular))
                            .padding([.top, .bottom, .leading, .trailing], 7.5)
                            .background(item.number <= 3 ? Color(Const.grayText) : Color.clear)
                            .clipShape(Circle())
                            .foregroundColor(item.number <= 3 ? Color.white : Color(Const.grayText))
                        
                        Text(item.userName)
                            .font(.system(size: 12, weight: .regular))
                            .foregroundColor(Color.black)
                        
                        Spacer()
                        
                        Text(item.info)
                            .font(.system(size: 16, weight: .bold))
                            .foregroundColor(Color.black)
                            .padding(.trailing, 18)
                    }
                    .listRowSeparator(.hidden)
                }
            }
        }
    }

now, depending on how wide the number is, the more indentation to userName.


Solution

  • You could use a hidden placeholder to reserve the space needed for displaying the largest number, then show the actual number in an overlay with the alignment required. This way, the first column always has a fixed width, so the usernames will also be aligned.

    Like this:

    HStack {
        Text("9999")
            .hidden()
            .overlay(alignment: .trailing) {
                Text("\(item.number)")
                    // all modifiers except font as before
            }
            .font(.system(size: 12, weight: .regular))
        Text(item.userName)
            // modifiers as before
    
        Spacer()
    
        Text(item.info)
            // modifiers as before
    }
    

    Screenshot