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

Related Post