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)
}
}
