Search code examples
swiftuivstackhstackswiftui-view

Aligning toggles in a SwiftUI View


I have this code that contains the labels and toggles shown in the screenshot below

 VStack(alignment: .leading) {
            HStack() {
                Text("Autostart:")
                    .font(.custom("SFProText-Medium", size: 12))
                
                Toggle("Launch on Login", isOn: $launchAtLogin).onChange(of: launchAtLogin) { newValue in
                    LaunchAtLogin.isEnabled = newValue
                    
                    if !isLoginItem() {
                        updateUserDefaults(key: "LaunchOnLogin", value: LaunchAtLogin.isEnabled)
                    }
                }.font(.custom("SFProText-Medium", size: 12))
            }
            .padding(.top, 10)
            .padding(.bottom, 10)
            
            HStack() {
                Text("Updates:")
                    .font(.custom("SFProText-Medium", size: 12))
                Toggle("Allow Automatic Updates", isOn: $allowAutomaticUpdates).onChange(of: allowAutomaticUpdates) { newValue in
                    allowAutomaticUpdates = newValue
                    updateUserDefaults(key: "AllowsAutomaticUpdates", value: allowAutomaticUpdates)
                }.font(.custom("SFProText-Medium", size: 12))
            }
        }
        .padding(.top, 10)
        .padding(.bottom, 10)

enter image description here

I'm trying to figure out how to get the two toggles to always be vertically aligned. I tried manually setting the frame size of them, as well wrapping them inside of another VStack, but none of that has seemed to work


Solution

  • One way to handle this is to define a custom horizontal alignment guide as follows:

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

    Then use this as the alignment parameter for the outer VStack. To align trailing edges of the Text labels within the HStacks use the modifier

    .alignmentGuide(.custom) { d in d[HorizontalAlignment.trailing] }
    

    Putting this all together…

    VStack(alignment: .custom) {
        HStack() {
            Text("Autostart:")
                .alignmentGuide(.custom) { d in d[HorizontalAlignment.trailing] }
    
            Toggle("Launch on Login", isOn: $launchAtLogin)
                .onChange(of: launchAtLogin) { _ in
            }
        }
        HStack() {
            Text("Updates:")
                .alignmentGuide(.custom) { d in d[HorizontalAlignment.trailing] }
            Toggle("Allow Automatic Updates", isOn: $allowAutomaticUpdates)
                .onChange(of: allowAutomaticUpdates) { _ in
            }
        }
    }
    .font(.custom("SFProText-Medium", size: 12))
    .padding()
    

    gives you…

    enter image description here