In this tutorial I'm going to show how to create a recipe app with carousel list and grid
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)) } } |
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. " ), ] |
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 } |
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) } } |