Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Align views in Picker

How do I align the Color views in a straight line with the text to the side?

To look like so (text aligned leading):

█  red
█  green
█  blue

Or this (text aligned center):

█    red
█  green
█   blue

Current code:

struct ContentView: View {
    @State private var colorName: Colors = .red
    
    var body: some View {
        Picker("Select color", selection: $colorName) {
            ForEach(Colors.allCases) { color in
                HStack {
                    color.asColor.aspectRatio(contentMode: .fit)
                    
                    Text(color.rawValue)
                }
            }
        }
    }
}


enum Colors: String, CaseIterable, Identifiable {
    case red
    case green
    case blue
    
    var id: String { rawValue }
    var asColor: Color {
        switch self {
        case .red:      return .red
        case .green:    return .green
        case .blue:     return .blue
        }
    }
}

Result (not aligned properly):

Result

Without the Picker, I found it is possible to use alignmentGuide(_:computeValue:) to achieve the result. However, this needs to be in a Picker.

Attempt:

VStack(alignment: .custom) {
    ForEach(Colors.allCases) { color in
        HStack {
            color.asColor.aspectRatio(contentMode: .fit)
                .alignmentGuide(.custom) { d in
                    d[.leading]
                }
            
            Text(color.rawValue)
                .frame(maxWidth: .infinity)
                .fixedSize()
        }
    }
    .frame(height: 50)
}

/* ... */

extension HorizontalAlignment {
    struct CustomAlignment: AlignmentID {
        static func defaultValue(in context: ViewDimensions) -> CGFloat {
            return context[HorizontalAlignment.leading]
        }
    }
    
    static let custom = HorizontalAlignment(CustomAlignment.self)
}

Result of attempt:

Result 2

like image 499
George Avatar asked Oct 19 '25 03:10

George


1 Answers

Possible solution is to use dynamic width for labels applied by max calculated one using view preferences.

Here is a demo. Tested with Xcode 13beta / iOS15

demo

Note: the ViewWidthKey is taken from my other answer https://stackoverflow.com/a/63253241/12299030

struct ContentView: View {
    @State private var colorName: Colors = .red
    @State private var maxWidth = CGFloat.zero

    var body: some View {
        Picker("Select color", selection: $colorName) {
            ForEach(Colors.allCases) { color in
                HStack {
                    color.asColor.aspectRatio(contentMode: .fit)
                    Text(color.rawValue)
                }
                .background(GeometryReader {
                    Color.clear.preference(key: ViewWidthKey.self, 
                        value: $0.frame(in: .local).size.width)
                })
                .onPreferenceChange(ViewWidthKey.self) {
                    self.maxWidth = max($0, maxWidth)
                }
                .frame(minWidth: maxWidth, alignment: .leading)
            }
        }
    }
}
like image 66
Asperi Avatar answered Oct 20 '25 17:10

Asperi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!