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