article

Thursday, December 2, 2021

SwiftUI Furniture Shop App UI

SwiftUI Furniture Shop App UI

ContentView.swift
 
//
//  ContentView.swift
//  SwiftUITest
//
//  Created by Cairocoders
//

import SwiftUI

struct ContentView: View {
    @State private var search: String = ""
    @State private var selectedIndex: Int = 1
    
    private let categories = ["All", "Chair", "Sofa", "Sofabeds", "Mattresses", "Table"]
    var body: some View {
        NavigationView {
            ZStack {
                Color("Bg")
                    .ignoresSafeArea()
                
                ScrollView (showsIndicators: false) {
                    VStack (alignment: .leading) {
                        
                        AppBarView()
                        
                        TagLineView()
                            .padding()
                        
                        SearchAndScanView(search: $search)
                        
                        ScrollView (.horizontal, showsIndicators: false) {
                            HStack {
                                ForEach(0 ..< categories.count) { i in
                                    Button(action: {selectedIndex = i}) {
                                        CategoryView(isActive: selectedIndex == i, text: categories[i])
                                    }
                                }
                            }
                            .padding()
                        }
                        
                        Text("Popular")
                            .font(.system(size: 24))
                            .padding(.horizontal)
                        
                        ScrollView (.horizontal, showsIndicators: false) {
                            HStack (spacing: 0) {
                                ForEach(popular) { i in
                                    NavigationLink(
                                        destination: DetailScreen(viewmodel: i),
                                        label: {
                                            ProductCardView(image: Image(i.imageName), size: 210, title: i.title, rating: i.rating)
                                        })
                                        .navigationBarHidden(true)
                                        .foregroundColor(.black)
                                }
                                .padding(.leading)
                            }
                        }
                        .padding(.bottom)
                        
                        Text("Best")
                            .font(.system(size: 24))
                            .padding(.horizontal)
                        
                        ScrollView (.horizontal, showsIndicators: false) {
                            HStack (spacing: 0) {
                                ForEach(best) { i in
                                    ProductCardView(image: Image(i.imageName), size: 180, title: i.title, rating: i.rating)
                                }
                                .padding(.leading)
                            }
                        }
                        
                    }
                }
                
                VStack {
                    Spacer()
                    BottomNavBarView()
                }
            }
        }
    }
}

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


struct BottomNavBarView: View {
    var body: some View {
        HStack {
            BottomNavBarItem(image: Image(systemName: "house"), action: {})
            BottomNavBarItem(image: Image(systemName: "suit.heart"), action: {})
            BottomNavBarItem(image: Image(systemName: "cart"), action: {})
            BottomNavBarItem(image: Image(systemName: "person"), action: {})
        }
        .padding()
        .background(Color("Primary")).foregroundColor(Color.white)
        .clipShape(Capsule())
        .padding(.horizontal)
        .shadow(color: Color.blue.opacity(0.15), radius: 8, x: 2, y: 6)
    }
}

struct BottomNavBarItem: View {
    let image: Image
    let action: () -> Void
    var body: some View {
        Button(action: action) {
            image
                .frame(maxWidth: .infinity)
        }
    }
}
AppBarView.swift
 
//
//  AppBarView.swift
//  SwiftUITest
//
//  Created by Cairocoders
//

import SwiftUI

struct AppBarView: View {
    var body: some View {
        HStack {
            Button(action: {}) {
                Image(systemName: "slider.horizontal.3")
                    .padding()
                    .background(Color.white)
                    .cornerRadius(10.0)
            }
            
            Spacer()
            
            Button(action: {}) {
                Image(uiImage: #imageLiteral(resourceName: "photo1"))
                    .resizable()
                    .frame(width: 42, height: 42)
                    .cornerRadius(10.0)
            }
        }
        .padding(.horizontal)
    }
}

struct AppBarView_Previews: PreviewProvider {
    static var previews: some View {
        AppBarView()
    }
}
TagLineView.swift
 
//
//  TagLineView.swift
//  SwiftUITest
//
//  Created by Cairocoders
//

import SwiftUI

struct TagLineView: View {
    var body: some View {
        Text("Shop \nBest ")
            .font(.system(size: 28))
            .foregroundColor(Color("Primary"))
            + Text("Furniture!")
            .font(.system(size: 28))
            .fontWeight(.bold)
            .foregroundColor(Color("Primary"))
    }
}

struct TagLineView_Previews: PreviewProvider {
    static var previews: some View {
        TagLineView()
    }
}
SearchAndScanView.swift
 
//
//  SearchAndScanView.swift
//  SwiftUITest
//
//  Created by Cairocoders
//

import SwiftUI

struct SearchAndScanView: View {
    @Binding var search: String
    var body: some View {
        HStack {
            HStack {
                Image(systemName: "magnifyingglass")
                    .padding(.trailing, 8)
                TextField("Search Furniture", text: $search)
            }
            .padding(.all, 20)
            .background(Color.white)
            .cornerRadius(10.0)
            .padding(.trailing, 8)
            
            Button(action: {}) {
                Image(systemName: "text.magnifyingglass")
                    .padding().foregroundColor(Color.white)
                    .background(Color("Primary"))
                    .cornerRadius(10.0)
            }
        }
        .padding(.horizontal)
    }
}

struct SearchAndScanView_Previews: PreviewProvider {
    @State static var search: String = ""
    static var previews: some View {
        SearchAndScanView(search: $search)
    }
}
CategoryView.swift
 
//
//  CategoryView.swift
//  SwiftUITest
//
//  Created by Cairocoders
//

import SwiftUI

struct CategoryView: View {
    let isActive: Bool
    let text: String
    var body: some View {
        VStack (alignment: .leading, spacing: 0) {
            Text(text)
                .font(.system(size: 18))
                .fontWeight(.medium)
                .foregroundColor(isActive ? Color("Primary") : Color.black.opacity(0.5))
            if (isActive) { Color("Primary")
                .frame(width: 15, height: 2)
                .clipShape(Capsule())
            }
        }
        .padding(.trailing)
    }
}
ProductCardView.swift
 
//
//  ProductCardView.swift
//  SwiftUITest
//
//  Created by Cairocoders
//

import SwiftUI

struct ProductCardView: View {
    let image: Image
    let size: CGFloat
    let title: String
    let rating: Int
    var body: some View {
        VStack {
            image
                .resizable()
                .frame(width: size, height: 200 * (size/210))
                .cornerRadius(20.0)
            Text(title).font(.title3).fontWeight(.bold)
            
            HStack (spacing: 2) {
                ForEach(0..<rating) { rating in
                    Image(systemName: "star.fill")
                        .resizable()
                        .frame(width: 20, height: 20)
                        .foregroundColor(.yellow)
                }
                Spacer()
                Text("$1299")
                    .font(.title3)
                    .fontWeight(.bold)
            }
        }
        .frame(width: size)
        .padding()
        .background(Color.white)
        .cornerRadius(20.0)
        
    }
}
DetailScreen.swift
 
//
//  DetailScreen.swift
//  SwiftUITest
//
//  Created by Cairocoders
//

import SwiftUI

struct DetailScreen: View {
    
    let viewmodel: Furniture
    
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    var body: some View {
        ZStack {
            Color("Bg")
            ScrollView  {
                //Product Image
                Image(viewmodel.imageName)
                        .resizable()
                        .aspectRatio(1,contentMode: .fit)
                        .edgesIgnoringSafeArea(.top)
                
                DescriptionView(title: viewmodel.title, rating: viewmodel.rating)
                
            }
            .edgesIgnoringSafeArea(.top)
            
            HStack {
                Text("$1299")
                    .font(.title)
                    .foregroundColor(.white)
                Spacer()
                
                Text("Add to Cart")
                    .font(.title3)
                    .fontWeight(.semibold)
                    .foregroundColor(Color("Primary"))
                    .padding()
                    .padding(.horizontal, 8)
                    .background(Color.white)
                    .cornerRadius(10.0)
                
            }
            .padding()
            .padding(.horizontal)
            .background(Color("Primary"))
            .cornerRadius(60.0, corners: .topLeft)
            .frame(maxHeight: .infinity, alignment: .bottom)
            .edgesIgnoringSafeArea(.bottom)
        }
        .navigationBarBackButtonHidden(true)
        .navigationBarItems(leading: BackButton(action: {presentationMode.wrappedValue.dismiss()}), trailing: Image(systemName: "lineweight"))
    }
}


struct RoundedCorner: Shape {

    var radius: CGFloat = .infinity
    var corners: UIRectCorner = .allCorners

    func path(in rect: CGRect) -> Path {
        let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        return Path(path.cgPath)
    }
}

extension View {
    func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View {
        clipShape( RoundedCorner(radius: radius, corners: corners) )
    }
}

struct DetailScreen_Previews: PreviewProvider {
    static var previews: some View {
        DetailScreen(viewmodel: Furniture(id: 1, title: "Sofas", imageName: "1", rating: 5))
    }
}


struct ColorDotView: View {
    let color: Color
    var body: some View {
        color
            .frame(width: 24, height: 24)
            .clipShape(Circle())
    }
}

struct DescriptionView: View {
    let title : String
    let rating : Int
    
    @State var selected = -1
    @State var message = false
    @State private var count = 0
    
    var body: some View {
        VStack (alignment: .leading) {
            //                Title
            Text(title)
                .font(.title)
                .fontWeight(.bold)
            //                Rating
            HStack (spacing: 4) {
                ForEach(0..<5) { rate in
                    Image(systemName: "star.fill")
                        .resizable()
                        .frame(width: 25, height: 25)
                        .foregroundColor(self.selected >= rate ? .yellow : .gray)
                        .onTapGesture {
                            self.selected = rate
                            self.message.toggle()
                        }
                }
                Text("(\(rating))")
                    .opacity(0.5)
                    .padding(.leading, 8)
                Spacer()
            }
            .alert(isPresented: $message) {
               Alert(title: Text("Rating Submit"), message: Text("You Rated \(self.selected + 1) out of 5 Star Rating"), dismissButton: .none)
            }
            
            Text("Description")
                .fontWeight(.medium)
                .padding(.vertical, 8)
            Text("A Mid-Century Modern Modern dining chair with arm rest inspired by Eiffel chair made from polycarbonate plastic and has metal legs.")
                .lineSpacing(8.0)
                .opacity(0.6)
            
            //                Info
            HStack (alignment: .top) {
                VStack (alignment: .leading) {
                    Text("Size")
                        .font(.system(size: 16))
                        .fontWeight(.semibold)
                    Text("W53xD46.5xH83.5 cm")
                        .opacity(0.6)
                }
            }
            .padding(.vertical)
            
            //                Colors and Counter
            HStack {
                VStack (alignment: .leading) {
                    Text("Colors")
                        .fontWeight(.semibold)
                    HStack {
                        ColorDotView(color: Color.red)
                        ColorDotView(color: Color.orange)
                        ColorDotView(color: Color.green)
                    }
                }
                .frame(maxWidth: .infinity, alignment: .leading)
                
                HStack {
                    //                        Minus Button
                    Button(action: {
                        stepCountminus()
                    }) {
                        Image(systemName: "minus")
                            .padding(.all, 8)
                        
                    }
                    .frame(width: 30, height: 30)
                    .overlay(RoundedCorner(radius: 50).stroke())
                    .foregroundColor(.black)
                    
                    Text("\(self.count)")
                        .font(.title2)
                        .fontWeight(.semibold)
                        .padding(.horizontal, 8)
                    
                    //                        Plus Button
                    Button(action: {
                        stepCount()
                    }) {
                        Image(systemName: "plus")
                            .foregroundColor(.white)
                            .padding(.all, 8)
                            .background(Color("Primary"))
                            .clipShape(Circle())
                    }
                }
                
            }
        }
        .padding()
        .padding(.top)
        .background(Color("Bg"))
        .cornerRadius(30, corners: [.topLeft, .topRight])
        .offset(x: 0, y: -30.0)
    }
    
    func stepCount() {
        count += 1
    }
    func stepCountminus() {
        count -= 1
    }
}


struct BackButton: View {
    let action: () -> Void
    var body: some View {
        Button(action: action) {
            Image(systemName: "chevron.backward")
                .foregroundColor(.black)
                .padding(.all, 12)
                .background(Color.white)
                .cornerRadius(8.0)
        }
    }
}
Model.swift
 
//
//  Model.swift
//  SwiftUITest
//
//  Created by Cairocoders
//

import SwiftUI
 
struct Furniture : Identifiable {
    var id : Int
    var title : String
    var imageName : String
    var rating: Int
}
 
var popular = [Furniture(id: 0, title: "Pea Swivel Accent Chair", imageName: "1", rating: 4),
                   Furniture(id: 1, title: "Adelie Accent Chair", imageName: "2", rating: 3),
                   Furniture(id: 2, title: "Asha II Accent Chair", imageName: "3", rating: 5),
                   Furniture(id: 3, title: "Jenpeg Center Table", imageName: "4", rating: 4)]

var best = [Furniture(id: 0, title: "Cologne Sofabed", imageName: "5", rating: 3),
                   Furniture(id: 1, title: "Cleve Sofabed", imageName: "6", rating: 4),
                   Furniture(id: 2, title: "Cleve Sofabed", imageName: "7", rating: 5),
                   Furniture(id: 3, title: "Ivy Dining Chair", imageName: "4", rating: 2)]

Related Post