iosswiftxcodeswiftuiside-menu

Xcode Swift UI Side menu contents are not aligned


I am trying to create side menu in Swift UI (Xcode version 11.5). Menu is created and it seems to be working but only issue i am facing is that menu text are not properly aligned ( i need it to be left (leading) aligned).

Home page view -

    struct HomePageView: View {
        @State var size = UIScreen.main.bounds.width / 1.6
    
        var body: some View {
            ZStack{
                Color.orange
                // main home page components here....
                NavigationView{
                    List(0..<5){_ in
                        
                        Text("Hello")
                    }
                    .navigationBarTitle("Home")
                    .navigationBarItems(leading: Button(action: {
                        self.size = 10
                        
                    }, label: {
                        Image("menu")
                            .resizable()
                            .frame(width: 30, height: 30)
                    }).foregroundColor(.black))
                }
                HStack{
                    menu(size: $size)
                        .cornerRadius(20)
                        .padding(.leading, -size)
                        .offset(x: -size)
                        //.shadow(color: Color.lairShadowGray.opacity(0.5), radius: 0, x: 0, y: 0)
                    Spacer()
                }
                //.shadow(color: Color.lairShadowGray.opacity(0.5), radius: 0, x: 0, y: 0)
            }.animation(.spring())
        }
    }

I tried to create menu dynamically using state variable which is working. For menu text alignment, i tried to apply padding, frame, offset but it did not work, not sure if i am applying at right place or not

struct menu : View {
    @Binding var size : CGFloat
    @State var expand: Bool = false
    
    @State var sideMenuEntries = [
        SideMenuData(index: 0, imageName: "info.circle.fill",
                     menuText: "Menu Text 1",
                     subMenu: [""],
                     expand: false),
        SideMenuData(index: 1, imageName: "doc.on.doc.fill",
                     menuText: "Menu 2",
                     subMenu: [""],
                     expand: false)
    ]
    

    var body : some View{
        VStack{
            HStack{
                Spacer()
                Button(action: {
                    self.size =  UIScreen.main.bounds.width / 1.6
                }) {
                    Image("close").resizable()
                        .frame(width: 15, height: 15)
                        .padding()
                }
                .foregroundColor(.white)
                .clipShape(Circle())
            }
            ForEach (sideMenuEntries.indices) { index in
                //VStack (alignment: .leading, spacing: 0, content: {
                VStack (alignment: .leading, spacing: 0, content: {
                    HStack{
                        Image(systemName: self.sideMenuEntries[index].imageName).resizable().frame(width: 25, height: 25)//.padding(.leading, 5)
                            .foregroundColor(.white)
                        Text(self.sideMenuEntries[index].menuText).fontWeight(.heavy).foregroundColor(.white)
                        Image(systemName: self.sideMenuEntries[index].expand ? "chevron.compact.up" : "chevron.compact.down")
                            .resizable()
                            .frame(width: 10, height: 10)
                            .foregroundColor(.white)
                    }
                    .contentShape(Rectangle())
                    .onTapGesture {
                        self.sideMenuEntries[index].expand.toggle()
                    }
                    if self.sideMenuEntries[index].expand {
                        Button(action: {
                        }){
                            VStack (alignment: .leading){
                                Text("Sub Menu 1")
                                    .foregroundColor(.white)
                                Text("Sub 2")
                                    .foregroundColor(.white)
                                Text("Sub Menu 3")
                                    .foregroundColor(.white)
                                Text("Sub 4")
                                    .foregroundColor(.white)
                            }
                            .padding([.top, .bottom], 4)
                            .padding(.leading, 14)
                        }
                    }
                })
            }
            Spacer()
        }
        .frame(width: UIScreen.main.bounds.width / 1.3)
        .background(LinearGradient(
            gradient: Gradient(
                colors: [.buttonGradientStartColor, .buttonGradientEndColor]),
            startPoint: .top,
            endPoint: .bottom))
       
    }
}

Solution

  • Here is possible fix

    Tested with replicated code on Xcode 11.4 / iOS 13.4

    demo

    struct menu : View {
        @Binding var size : CGFloat
        @State var expand: Bool = false
    
        @State var sideMenuEntries = [
            SideMenuData(index: 0, imageName: "info.circle.fill",
                         menuText: "Menu Text 1",
                         subMenu: [""],
                         expand: false),
            SideMenuData(index: 1, imageName: "doc.on.doc.fill",
                         menuText: "Menu 2",
                         subMenu: [""],
                         expand: false)
        ]
    
    
        var body : some View{
            VStack(alignment: .leading) {           // << here !!
                HStack{
                    Spacer()
                    Button(action: {
                        self.size =  UIScreen.main.bounds.width / 1.6
                    }) {
                        Image("close").resizable()
                            .frame(width: 15, height: 15)
                            .padding()
                    }
                    .foregroundColor(.white)
                    .clipShape(Circle())
                }
                ForEach (sideMenuEntries.indices) { index in
                    //VStack (alignment: .leading, spacing: 0, content: {
                    VStack (alignment: .leading, spacing: 0, content: {
                        HStack{
                            Image(systemName: self.sideMenuEntries[index].imageName).resizable().frame(width: 25, height: 25)//.padding(.leading, 5)
                                .foregroundColor(.white)
                            Text(self.sideMenuEntries[index].menuText).fontWeight(.heavy).foregroundColor(.white)
                            Image(systemName: self.sideMenuEntries[index].expand ? "chevron.compact.up" : "chevron.compact.down")
                                .resizable()
                                .frame(width: 10, height: 10)
                                .foregroundColor(.white)
                        }
                        .contentShape(Rectangle())
                        .onTapGesture {
                            self.sideMenuEntries[index].expand.toggle()
                        }
                        if self.sideMenuEntries[index].expand {
                            Button(action: {
                            }){
                                VStack (alignment: .leading){
                                    Text("Sub Menu 1")
                                        .foregroundColor(.white)
                                    Text("Sub 2")
                                        .foregroundColor(.white)
                                    Text("Sub Menu 3")
                                        .foregroundColor(.white)
                                    Text("Sub 4")
                                        .foregroundColor(.white)
                                }
                                .padding([.top, .bottom], 4)
                                .padding(.leading, 14)
                            }
                        }
                    })
                }
                Spacer()
            }.padding(.leading, 30)                             // << here !!
            .frame(width: UIScreen.main.bounds.width / 1.3)
            .background(LinearGradient(
                gradient: Gradient(
                    colors: [.buttonGradientStartColor, .buttonGradientEndColor]),
                startPoint: .top,
                endPoint: .bottom))
    
        }
    }