article

Friday, June 11, 2021

SwiftUI Recipe App Using Carousel List and Grid with Details View

SwiftUI Recipe App Using Carousel List and Grid with Details View 

In this tutorial I'm going to show how to create a recipe app with carousel list and grid

ContentView.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//
//  ContentView.swift
//  Test
//
//  Created by Cairocoders
//
 
import SwiftUI
 
struct ContentView: View {
    var body: some View {
         
        Home()
    }
}
 
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
 
struct Home : View {
     
    @State var search = ""
    @State var index = 0
    @State var columns = Array(repeating: GridItem(.flexible(), spacing: 15), count: 2)
     
    var body: some View{
 
        ScrollView(.vertical, showsIndicators: false) {
             
            LazyVStack{
                 
                HStack{
                     
                    Text("Recipes")
                        .font(.title)
                        .fontWeight(.bold)
                     
                    Spacer()
                }
                .padding(.horizontal)
                 
                TextField("Search", text: self.$search)
                    .padding(.vertical,10)
                    .padding(.horizontal)
                    .background(Color.black.opacity(0.07))
                    .cornerRadius(10)
                    .padding(.horizontal)
                    .padding(.top,25)
                 
                // Carousel List...
                 
                TabView(selection: self.$index){
                     
                    ForEach(1...5,id: \.self) {index in
                        Image("banner\(index)")
                            .resizable()
                            // adding animation...
                            .frame(height: self.index == index ?  230 : 180)
                            .cornerRadius(15)
                            .padding(.horizontal)
                            // for identifying current index....
                            .tag(index)
                    }
                }
                .frame(height: 230)
                .padding(.top,25)
                .tabViewStyle(PageTabViewStyle())
                .animation(.easeOut)
                 
                // adding custom Grid....
                 
                HStack{
                     
                    Text("Popular")
                        .font(.title)
                        .fontWeight(.bold)
                     
                    Spacer()
                     
                    Button {
                         
                        // reducing to row.....
                         
                        withAnimation(.easeOut){
                             
                            if self.columns.count == 2{
                                 
                                self.columns.removeLast()
                            }
                            else{
                                 
                                self.columns.append(GridItem(.flexible(), spacing: 15))
                            }
                        }
                         
                    } label: {
                     
                        Image(systemName: self.columns.count == 2 ? "rectangle.grid.1x2" : "square.grid.2x2")
                            .font(.system(size: 24))
                            .foregroundColor(.black)
                    }
 
                }
                .padding(.horizontal)
                .padding(.top,25)
                 
                LazyVGrid(columns: self.columns,spacing: 25){
                     
                    ForEach(data){recipe in
                         
                        // GridView....
                         
                        PopularRecipes(recipes: recipe,columns: self.$columns, setid: recipe.id) //PopularRecipes.swift
                    }
                }
                .padding([.horizontal,.top])
                 
            }
            .padding(.vertical)
        }
        .background(Color.black.opacity(0.05).edgesIgnoringSafeArea(.all))
    }
}
Recipes.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//
//  Recipes.swift
//  Test
//
//  Created by Cairocoders
//
 
import Foundation
 
struct Recipe : Identifiable {
     
    var id : Int
    var name : String
    var image : String
    var rating : Int
    var details : String
}
 
var data = [
 
    Recipe(id: 0, name: "Bistek Tagalog", image: "recipe1", rating: 2, details: "A dish made of strips of salted and peppered sirloin beef, usually flattened with a meat tenderizing tool, slowly cooked in soy sauce, calamansi juice, garlic and onions, a specialty of the Tagalog region"),
    Recipe(id: 1, name: "Boogie flight", image: "recipe2", rating: 5, details: "A boodle fight is a meal that dispenses with cutlery and dishes. Diners instead practice kamayan, Filipino for eating with the hands"),
    Recipe(id: 2, name: "Sinigang Na Baboy", image: "recipe3", rating: 3,details: "Sinigang na baboy with Gabi is a Filipino pork soup with taro cooked in a sour broth."),
    Recipe(id: 3, name: "Ginisang Togue", image: "recipe4", rating: 2,details: "Ginisang Togue is basically Sauteed Mung Bean Sprout with carrots, bell pepper, shrimp, and tofu."),
    Recipe(id: 4, name: "Ginisang Munggo (Monggo)", image: "recipe5", rating: 4, details: "Munggo or Mung bean (or even green bean to some) is a seed of Vigna radiata, a plant native to India and Pakistan. Since the plant originated in Asia, it was easy to spread along the nearby countries. This seed became a hit when it reached the Philippines."),
    Recipe(id: 5, name: "Pork Estofado (Sweet Pork Stew)", image: "recipe6", rating: 2, details: "Pork Estofado with saba bananas, carrots, Chinese sausage, and a sweet and savory sauce. Perfect with steamed rice!"),
    Recipe(id: 6, name: "Pata Tim", image: "recipe7", rating: 4, details: "Brimming in a pork stew infused with aromatic peppercorn, sesame oil and soy sauce, Pata Tim is a classic Filipino dish with traces in Chinese cuisine"),
    Recipe(id: 7, name: "Pancit Palabok", image: "recipe8", rating: 3, details: "Pancit Palabok is a noodle dish with shrimp sauce and topped with several ingredients such as cooked shrimp, boiled pork, crushed chicharon, tinapa flakes, fried tofu, scallions, and fried garlic. "),
]
PopularRecipes.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//
//  PopularRecipes.swift
//  Test
//
//  Created by Cairocoders
//
 
import SwiftUI
 
struct PopularRecipes: View {
     
    var recipes : Recipe
    @Binding var columns : [GridItem]
    @Namespace var namespace
     
    @State var showAlert: Bool = false
    @State var msg: String = ""
     
    @State var show = false
    @State var setid: Int
     
    var body: some View{
          
  
        VStack{
              
            if self.columns.count == 2{
                  
                VStack(spacing: 15){
                      
                    ZStack(alignment: Alignment(horizontal: .trailing, vertical: .top)) {
                          
                        Image(recipes.image)
                            .resizable()
                            .frame(height: 250)
                            .cornerRadius(15)
                         
                        Button {
                            self.showAlert = true
                            self.msg = "\(recipes.name) Done Heart..."
                        } label: {
                          
                            Image(systemName: "heart.fill")
                                .foregroundColor(.red)
                                .padding(.all,10)
                                .background(Color.white)
                                .clipShape(Circle())
                        }
                        .padding(.all,10)
  
                    }
                    .matchedGeometryEffect(id: "image", in: self.namespace)
                      
                    Text(recipes.name)
                        .fontWeight(.bold)
                        .lineLimit(1)
                        .matchedGeometryEffect(id: "title", in: self.namespace)
  
                    Button {
                        setid = (recipes.id)
                        show.toggle()
                    } label: {
                      
                        Text("Learn More")
                            .foregroundColor(.white)
                            .padding(.vertical,10)
                            .padding(.horizontal,25)
                            .background(Color.green)
                            .cornerRadius(5)
                    }
                    .matchedGeometryEffect(id: "learnmore", in: self.namespace)
                     
                }//End VStack
                .alert(isPresented: $showAlert, content: {
                    Alert(title: Text(self.msg))
                })
                 
                .sheet(isPresented: $show, content: {
                    Details(setid: setid, data: recipes)
                })
            }
            else{
                  
                // Row View....
                  
                // adding animation...
                  
                HStack(spacing: 15){
                      
                    ZStack(alignment: Alignment(horizontal: .trailing, vertical: .top)) {
                          
                        Image(recipes.image)
                            .resizable()
                            .frame(width: (UIScreen.main.bounds.width - 45) / 2,height: 250)
                            .cornerRadius(15)
                          
                        Button {
                            self.showAlert = true
                            self.msg = "\(recipes.name) Done Heart..."
                        } label: {
                          
                            Image(systemName: "heart.fill")
                                .foregroundColor(.red)
                                .padding(.all,10)
                                .background(Color.white)
                                .clipShape(Circle())
                        }
                        .padding(.all,10)
  
                    }
                    .matchedGeometryEffect(id: "image", in: self.namespace)
                    .alert(isPresented: $showAlert, content: {
                        Alert(title: Text(self.msg))
                    })
                     
                    VStack(alignment: .leading, spacing: 10) {
                          
                        Text(recipes.name)
                            .fontWeight(.bold)
                            .matchedGeometryEffect(id: "title", in: self.namespace)
                          
                        // Rating Bar...
                          
                        HStack(spacing: 10){
                              
                            ForEach(1...5,id: \.self) {rating in
                                Image(systemName: "star.fill")
                                    .foregroundColor(self.recipes.rating >= rating ? .yellow : .gray)
                            }
                              
                            Spacer(minLength: 0)
                        }
                          
                        Button {
                            setid = (recipes.id)
                            show.toggle()
                        } label: {
                          
                            Text("Learn More")
                                .foregroundColor(.white)
                                .padding(.vertical,10)
                                .padding(.horizontal,25)
                                .background(Color.green)
                                .cornerRadius(5)
                        }
                        .matchedGeometryEffect(id: "learnmore", in: self.namespace)
                    }
                    .sheet(isPresented: $show, content: {
                        Details(setid: setid, data: recipes)
                    })
                      
                }
                .padding(.trailing)
                .background(Color.white)
                .cornerRadius(15)
            }
             
        }//End VStack
    }//End body
}
Details.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//
//  Details.swift
//  Test
//
//  Created by Cairocoders
//
 
import SwiftUI
 
struct Details: View {
     
    var setid: Int
    var data : Recipe
    var body: some View {
        //    Text("\(setid)")
        //    Text(data.name)
        VStack {
            Image(data.image)
                .resizable()
                .aspectRatio(contentMode: .fill)
            Text(data.name)
                .font(.title)
                .fontWeight(.medium)
            Form {
                Section {
                    HStack {
                        VStack {
                            Text("Detail")
                                .foregroundColor(.gray)
                                .font(.callout)
                                .frame(alignment: .leading)
                            Text(data.details)
                                .foregroundColor(.gray)
                                .font(.callout)
                                .frame(alignment: .leading)
                        }
                    }
                    HStack {
                        Text("Rating")
                        Spacer()
                        ForEach(1...5,id: \.self) {rating in
                            Image(systemName: "star.fill")
                                .foregroundColor(self.data.rating >= rating ? .yellow : .gray)
                        }
                    }
                     
                }
 
            }
        }
        .environment(\.colorScheme, .light)
    }
}

Related Post