andrewbuilder
andrewbuilder

Reputation: 3799

SwiftUI alignment of "rating" views in two separate rows in Form for macOS and iOS

Writing a Multiplatform app initially for macOS and iOS that uses a master detail view structure and within the detail view is a Form and many Sections, one of which contains two structs that represent a user's rating (of food) for enjoyment and consumption.

Here is the code for the enjoyment rating...

struct Rating: View {
    
    @Environment(\.colorScheme) var colourScheme
    @Binding var rating: Int
    
    var body: some View {
        HStack {
            Text("Enjoyment")
                .padding(.trailing, 12.0)
#if os(iOS)
            Spacer()
#endif
            ForEach(0..<5) { counter in
                Image(systemName: rating > counter ? "star.fill" : "star")
                    .onTapGesture(count: 1) {
                        if rating == 1 {
                            rating = 0
                        }
                        else {
                            rating = counter + 1
                        }
                    }
                    // this line for image system name = "circle.fill" : "circle"
                    //.padding(.trailing, counter != 4 ? 2.5 : 0) 
            }
            .shadow(color: colourScheme == .light ? .gray : .white, radius: colourScheme == .light ? 1.0 : 2.0, x: 0, y: 0)
        }
        .foregroundColor(Color.accentColor)
    }
}

The consumption rating is very similar with minor changes.

So far these look good on iOS because the (iOS only) Spacer() pushes each Rating view to the right or trailing edge of the row and my .padding modifier hack for the circle image makes the spacing between each image "about right".

While I'm struggling to figure out how to align the "dots" for the macOS target, I'm also struggling to figure out how to align each image programmatically, so that if I changed the image the alignment would work.

See screenshots below (that illustrate how the five Images do not align).

iOS

iOS screenshot

macOS

macOS screenshot

I've read a few blogs on the .alignmentGuide modifier including Alignment guides in SwiftUI by Majid Jabrayilov.

It seems to be the way I should go but I'm stuck in my attempts on how to work this out.

Upvotes: 0

Views: 236

Answers (1)

ScottM
ScottM

Reputation: 10502

I've added a comment about the use of Spacer() between your label and your rating control, but separately it's worth looking at the rating control itself.

Firstly, right now you're relying on the five elements sharing an HStack with the label, and using conditional padding logic within the loop to control the spacing between elements.

That part would be easier if you give your rating element its own HStack. That way, spacing between elements can be determined using the stack's spacing attribute without having to worry about whether or not you're on the last loop iteration. For example:

HStack(spacing: 2.5) {
  ForEach(0..<5) {
    Image(systemName: "circle")
    // etc.
  }
}

In terms of aligning the child elements of a rating view so that they align with a similar view below regardless of the symbol being used, you can constrain the frame width of each child element to be the same, regardless of what image they're displaying.

You can accomplish that by adding a .frame() modifier to each child element in the loop:

HStack {
  ForEach(0..<5) {
    Image(systemName: "xxx")
      .frame(width: 40, alignment: .center)
  }
}

You'd obviously need to pick a width that works for you - and you could mix this with a spacing attribute on the HStack as well.

Upvotes: 1

Related Questions