user-interfaceswiftuistackspace

Facing issue in UI design with SwiftUI


I have been facing problems in UI design with SwiftUI. I feel UIKit is somewhat better when compared to SwiftUI because using SwiftUI I am not able to give proper spaces here every stack calculates spaces separately. I don't understand how to give spaces with its neighbour elements.

My major question is to get space from its neighbour element do we need to give leading space to be subtract its neighbour element width area?

For example, I have given image stack leading, top to 20 and width and height 100. Now if I want to add label leading to that image then do I need to give label leading spaces to -100? How spaces will work to give proper leading, trailing, top, bottom?

I made simple design like this using UIKit here if you take only top blue view person image to be top and leading to 20 and remaining labels and icons should be leading to that imageview to 15

enter image description here

Now I tried only top blue color design same in SwiftUI: I am new to UI so I tried this way. If there are any shortcuts please do let me know.

If any unnecessary code is there please let me know and please guide me how to write short codes using SwiftUI.

And could you please guide me how to embed stacks in one another. Is there any pattern to add one inside another?

With this code also I didn't get the proper design. Please guide me to fix it.

struct SecondView: View {
    var body: some View {
        ZStack{
            Color.yellow.ignoresSafeArea()
            
            ScrollView{
                VStack{
                    
                    ZStack {
                        Color.blue // Background color of the view
                        
                        VStack{
                            HStack (){
                                Image(systemName: "person.circle.fill")
                                
                                    .resizable()
                                    .aspectRatio(contentMode: .fit)
                                    .frame(width: 70, height: 70)
                                    .padding(.leading, 10)
                                    .padding(.top, 20)
                                
                                Spacer()
                            }
                            
                            Spacer()
                        }
                        
                        VStack(spacing: 10){
                            
                            Text("Jhon Thomas").foregroundColor(Color.white)
                                .padding(.bottom, 10).font(.system(size: 20))
                            
                            HStack (){
                                Image(systemName: "phone.fill")
                                
                                    .resizable()
                                    .aspectRatio(contentMode: .fit)
                                    .frame(width: 17, height: 17)
                                    .padding(.leading, 10)
                                Text("0000000000").foregroundColor(Color.white)
                            }
                            HStack (){
                                Image(systemName: "bag.fill")
                                
                                    .resizable()
                                    .aspectRatio(contentMode: .fit)
                                    .frame(width: 17, height: 17)
                                    .padding(.leading, 10)
                                Text("Singer").foregroundColor(Color.white)
                            }
                            HStack (){
                                Image(systemName: "location.fill")
                                
                                    .resizable()
                                    .aspectRatio(contentMode: .fit)
                                    .frame(width: 17, height: 17)
                                    .padding(.leading, 10)
                                Text("test loc").foregroundColor(Color.white)
                                Spacer()
                                Image(systemName: "map.fill")
                                
                                    .resizable()
                                    .aspectRatio(contentMode: .fit)
                                    .frame(width: 17, height: 17)
                                    .padding(.leading, 10)
                                Text("Jhon Thomas").foregroundColor(Color.white)
                            }
                        }.padding()
                        //  .padding(.leading, -80)
                     }.padding()
                     .frame(height: 200)
                }
            }
        }
    }
}

SwiftUI only blue colour design: how to make like above design all names and icons in same alignment and only leading 15 to imageview?

enter image description here


Solution

  • I'm here to help! First of all I would suggesto you learn the basics of SwiftUI going here: Intro to SwiftUI, and here: SwiftUI tutorial. Now, to the matter at hand. I started iOS developing with SwiftUI, and trust me, now that I've learnt and use UIKit daily for work there is no worse thing! About your code, there's a lot that can be improved. I took the chance to write some custom modifier to avoid repeating the same modifier for images over and over again. I removed redunant Stacks, created a resuable View for those repeating HStacks. I'm going to show you now:

    struct SecondView: View {
        var body: some View {
            ZStack {
                
                // This is ok but you can use .background() modifier too
                //Color.yellow.ignoresSafeArea()
                
                ScrollView {
                    
                    HStack {
                        //Color.white // Background color of the view
                        
                        VStack {
                            /// Alignment top to keep the imafge
                            HStack(alignment: .top) {
                                Image(systemName: "person.circle.fill")
                                    .resizable()
                                    .aspectRatio(contentMode: .fit)
                                    .modifier(ImageModifier(width: 70, height: 79))
                                    .padding(.top, 20)
                                
                                /// Removed this to avoid pushing the texts and icon to the right
                                //Spacer()
                                
                                /// I used this line to show you the separations of two
                                Rectangle()
                                    .background(.red)
                                    .frame(width: 1)
                            }
                            
                            /// I used the frame modifier instead
                            //Spacer()
                        }
                        /// I used this instead of the Spacer above to achieve the same result
                        .frame(maxHeight: .infinity, alignment: .top)
                        
                        VStack(spacing: 10) {
                            
                            Group {
                                
                                Text("Jhon Thomas")
                                //.foregroundColor(Color.white)
                                    /// Another way of setting padding. I had to use padding leading 10 to match the RowItems
                                    .padding(EdgeInsets(top: 0, leading: 10, bottom: 10, trailing: 0))
                                    .font(.system(size: 20))
                                
                                RowItem(text: "000000000", icon: "phone.fill")
                                
                                RowItem(text: "Singer", icon: "bag.fill")
                            }
                            .frame(maxWidth: .infinity, alignment: .leading)
                            
                            HStack {
                                RowItem(text: "Jhon Thomas", icon: "location.fill")
                                
                                Spacer()
                                
                                RowItem(text: "Jhon Thomas", icon: "map.fill")
                                
                                
                            }
                        }
                        .frame(maxWidth: .infinity, alignment: .leading)
                    }
                    /// Padding placed here (before the frame) will push views to the inside. Modifier order matters in SwiftUI.
                    .padding()
                    .frame(height: 200)
                    /// Padding placed here will make this view larger. The default padding value is 16.
                    //.padding()
                    .background(.white)
                    .frame(maxWidth: .infinity, alignment: .leading)
                }
            }
            .background(.yellow)
        }
        
        @ViewBuilder
        private func RowItem(text: String, icon: String) -> some View {
            HStack {
                Image(systemName: icon)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .modifier(ImageModifier())
                
                Text(text)
                    .foregroundStyle(.black)
            }
        }
        
    }
    

    And here's the custom modifier for Images:

    struct ImageModifier: ViewModifier {
        
        var width: CGFloat = 17
        var height: CGFloat = 17
        
        func body(content: Content) -> some View {
            content
                .frame(width: width, height: height)
                .padding(.leading, 10)
        }
    }
    

    This is, of course, to give you an idea of how to accomplish things in SwiftUI. If you now how to use it, you can build UIs really really fast. My point of view is that it gives you almost unlimited possibilities to control every pixel on the screen.

    Going into the detail of what I did is that I replaced Spacers with frame modifier specifying the maxWidth or maxHeight to take all the available space. The frame modifier also allows you to specify the alignment. Also, unless you are targetting iOS 14 you can use foregroundStyle to modify the color of the View. For the backgorund you can do basically the same, there's a background modifier too. The background modifier is used to draw things below the view it is appplied to, while the overlay modifier draws on top. You can use the @ViewBuilder macro to create functions that returns Views. I have an answere here that talks about it: ViewBuilder.

    There's too much to say, but the best you can do is getting your hands dirty and code a lot! I hope my answer was useful to you friend!