0
votes

I have a VStack which has some HStack as you can see in my codes, inside my each Hstack there is an Image and Text, after running my codes the Alignmet of all codes is ugly, I want the Image alignment center together and Text alignment leading. How I can solve the problem? I can make all Image .center Alignment, and also all Text .leading Alignment. But I can not make both happen at same time.

enter image description here

enter image description here enter image description here

struct CustomAlignment: AlignmentID
{
    static func defaultValue(in context: ViewDimensions) -> CGFloat
    {
        return context[HorizontalAlignment.center]
    }
}


struct CustomAlignment2: AlignmentID
{
    static func defaultValue(in context: ViewDimensions) -> CGFloat
    {
        return context[HorizontalAlignment.leading]
    }
}



extension HorizontalAlignment
{
    static let custom: HorizontalAlignment = HorizontalAlignment(CustomAlignment.self)
    static let custom2: HorizontalAlignment = HorizontalAlignment(CustomAlignment2.self)
}




import SwiftUI

struct ContentView: View {
    var body: some View {
    
    

    
    
    VStack(alignment: .custom)
    {
        

        HStack()
        {
            Image(systemName: "folder")
                .alignmentGuide(.custom) { $0[HorizontalAlignment.center] }
            Text("Some texts here.")
                .alignmentGuide(.custom2) { $0[HorizontalAlignment.leading] }
            Spacer()
        }
            


        HStack()
        {
            Image(systemName: "music.note")
                .alignmentGuide(.custom) { $0[HorizontalAlignment.center] }
            Text("Some texts here.")
                .alignmentGuide(.custom2) { $0[HorizontalAlignment.leading] }
            Spacer()
        }
      


        HStack()
        {
            Image(systemName: "person.fill.questionmark")
                .alignmentGuide(.custom) { $0[HorizontalAlignment.center] }
            Text("Some texts here.")
                .alignmentGuide(.custom2) { $0[HorizontalAlignment.leading] }
            Spacer()
        }
     
        
        
    }
    .padding()
    
    
    
    Spacer()
    
    
    
}

}

3

3 Answers

2
votes

You have to give a frame to the image as some SF Symbols are larger than others, also try to create reusable views.

try something like this:

struct ContentView: View {
    var body: some View {
        VStack {
            RowView(title: "Some texts here.", image: "folder")
            
            RowView(title: "Some texts here.", image: "person.fill.questionmark")
            
            RowView(title: "Some texts here.", image: "snow")
            
            RowView(title: "Some texts here.", image: "forward.end.alt.fill")
        }
        .padding()
        
        Spacer()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct RowView: View {
    let title: String
    let image: String
    
    var body: some View {
        // Option 1 with Label
//        Label(
//            title: {
//                Text(title)
//            },
//            icon: {
//                Image(systemName: image)
//                    .frame(width: 30, height: 30)
//            }
//        )
        // Option 2 with HStack
        HStack {
            Image(systemName: image)
                .frame(width: 30, height: 30)
            Text(title)
            Spacer()
        }
    }
}
2
votes

Use custom alignment guide if you want precise control.

About your comment on using fixed frame, here is an article which explains how frame works in SwiftUI. Basically, frame modifier just adds a fixed size frame around the SF in this case, but it won't alter the intrinsic size.

struct ContentView: View {
    var body: some View {
        VStack(alignment: .sfView) {
            SFView(title: "This is some text", image: "folder")
            SFView(title: "SwiftUI is cool. Combine is cooler.", image: "snow")
            SFView(title: "This is a music note. This has a different length.", image: "music.note")
        }
    }
}

private struct SFView: View {
    let title: String
    let image: String
    var body: some View {
        HStack(spacing: 8) {
            Image(systemName: image)
                .font(.system(size: 20))
                .frame(width: 32, height: 32)
                .alignmentGuide(.sfView) { d in d[HorizontalAlignment.center] }
            Text(title)
                .alignmentGuide(.sfView) { d in d[HorizontalAlignment.leading] }
        }
    }
}

private extension HorizontalAlignment {
    struct SFViewAlignment: AlignmentID {
        static func defaultValue(in d: ViewDimensions) -> CGFloat {
            d[HorizontalAlignment.leading]
        }
    }
    static let sfView = HorizontalAlignment(SFViewAlignment.self)
}

enter image description here

0
votes

You are WAY overcomplicating it. You don't need to use all these custom alignments and constraints for something this simple.

Go back to basics and use a regular VStack / HStack and just set the icon to be an exact frame. The issue was arising because the icons had slightly different widths.

struct TestView: View {
    var body: some View {

        VStack(alignment: .leading) {
            HStack {
                Image(systemName: "folder")
                    .frame(width: 30, height: 30, alignment: .center)
                Text("Some text here")
            }
            
            HStack {
                Image(systemName: "person.fill.questionmark")
                    .frame(width: 30, height: 30, alignment: .center)

                Text("Some text here")
            }
            
            HStack {
                Image(systemName: "snow")
                    .frame(width: 30, height: 30, alignment: .center)

                Text("Some text here")
            }
            
            HStack {
                Image(systemName: "music.note")
                    .frame(width: 30, height: 30, alignment: .center)

                Text("Some text here")
            }
        }
    }
}