article

Showing posts with label SwiftUI-iOS-Xcode. Show all posts
Showing posts with label SwiftUI-iOS-Xcode. Show all posts

Thursday, May 5, 2022

Swiftui custom navigationbar

Swiftui custom navigationbar

ContentView.swift
 
//
//  ContentView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct ContentView: View {
    
    var body: some View {
        NavigationView {
            List(0..<20) { rs in
                NavigationLink(destination: DetailsView().navigationBarBackButtonHidden(true)) {
                    Text("Details Custom Navigation Bar \(rs)")
                }
            }
            .navigationBarTitle("Custom Navigation Bar")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
DetailsView.swift
 
//
//  DetailsView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct DetailsView: View {
    @Environment(\.presentationMode) var presentationMode
    @State var items = [Item]()
    
    var body: some View {
        //Wrap ContentView into a NavigationView
            List(items) { item in
                Text(item.name)
            }
            //Add navigation bar title
            .navigationTitle("Todo List")
            //Initialize toolbar
            .toolbar(content: {
                //Declare one ToolBarItem for each navigation bar button and set the position
                ToolbarItem(placement: .navigationBarLeading) {
                    Button(action: {
                        self.presentationMode.wrappedValue.dismiss()    
                    }) {
                        Image(systemName: "chevron.backward")
                            .imageScale(.large)
                    }
                }
                
                ToolbarItem(placement: .navigationBarLeading) {
                    Button(action: {
                            addTask()
                    }) {
                        Image(systemName: "plus.circle")
                            .imageScale(.large)
                    }
                }
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button(action: {
                            removeTask()
                    }) {
                        Image(systemName: "minus.circle")
                            .imageScale(.large)
                    }
                }
            })
    }
    
    func addTask() {
        let item = Item(id: items.count+1, name: "Sample Todo List")
        items.append(item)
    }
    
    func removeTask() {
        items.removeLast()
    }
}

struct DetailsView_Previews: PreviewProvider {
    static var previews: some View {
        DetailsView()
    }
}


struct Item : Identifiable {
    var id : Int
    var name : String
}

SwiftUI Dessert App UI

SwiftUI Dessert App UI
ContentView.swift
 
//
//  ContentView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct ContentView: View {
 
    @State private var showDetails = false
    @State private var selectedItem = dessertData[0]
    
    var body: some View {
        NavigationView {
            ZStack {
                Color("Color")
                VStack {
                    HomeTopBar()
                    SearchBarView()
                    ScrollView(.vertical, showsIndicators: false) {
                        VStack {
                            ForEach(dessertData, id: \.self) { item in
                                Button(action: {
                                    showDetails = true
                                    selectedItem = item
                                }, label: {
                                    DessertItemView(item: item)
                                })
                            }
                            .background(
                            NavigationLink(
                                destination: DessertDetails(dessert: selectedItem)
                                    .navigationBarBackButtonHidden(true), isActive: $showDetails) {
                                    EmptyView()
                                }
                            )
                        }
                    }
                    Spacer()
                }
            }
            .edgesIgnoringSafeArea(.all)
        }
    }
}

struct DessertItemView: View {
    var item: Dessert
    
    var body: some View {
        ZStack(alignment: .topLeading) {
            HStack(spacing: 16) {
                Image(item.image)
                    .resizable()
                    .scaledToFill()
                    .clipShape(Circle())
                    .frame(width: 80, height: 80)
                  
                VStack(alignment: .leading) {
                    Text(item.name)
                        .font(.system(size: 16, weight: .regular))
                        .padding(.trailing, 20)
                        .foregroundColor(.gray)
                }
                Spacer()
                  
                Text(item.price)
                    .font(.system(size: 14, weight: .semibold))
                    .padding()
                    .foregroundColor(.green)
                
            }
        }
        .background(Color.white)
        .cornerRadius(40)
        .padding(.horizontal, 20)
        .padding(.vertical,5)
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
HomeTopBar.swift
 
//
//  HomeTopBar.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct HomeTopBar: View {
    
    var height = UIScreen.main.bounds.height
    
    var body: some View {
        HStack{
            Text("Home")
                .fontWeight(.bold)
                .frame(alignment: .center)
                .navigationBarItems(
                    leading:
                        Button(action: {}) {
                            Image(systemName: "slider.horizontal.3")
                                .font(.title)
                                .padding(.horizontal)
                        }
                    )
        }
        .foregroundColor(Color.black)
        .padding()
        .padding(.top, height < 750 ? 0 : 50)
    }
}

struct HomeTopBar_Previews: PreviewProvider {
    static var previews: some View {
        HomeTopBar()
    }
}
SearchBarView.swift
 
//
//  SearchBarView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct SearchBarView: View {
    
    @State var searchKey: String = ""
    
    var body: some View {
        HStack {
            Image(systemName: "magnifyingglass")
                .foregroundColor(.black)
                .padding()
            TextField("Search ...", text: $searchKey)
        }
        .background(Color.white)
        .cornerRadius(30)
        .padding(.horizontal, 25)
        .padding(.bottom)
    }
}

struct SearchBarView_Previews: PreviewProvider {
    static var previews: some View {
        SearchBarView()
    }
}
DessertDetails.swift
 
//
//  DessertDetails.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct DessertDetails: View {
    @Environment(\.presentationMode) var presentationMode
    @State var dessert: Dessert = dessertData[2]
    var body: some View {
        VStack(alignment: .leading) {
            Header(image: dessert.image)
            VStack(alignment: .leading) {
                ScrollView(.vertical, showsIndicators: false) {
                    VStack(alignment: .leading) {
                        Text(dessert.name)
                            .foregroundColor(.primary)
                            .font(.title)
                            .fontWeight(.bold)
                            .padding(.horizontal)
                            .padding(.vertical, 10)
                        HStack {
                            Text(dessert.price)
                                .font(.title3)
                                .fontWeight(.bold)
                            
                            Spacer()
                            AmountView()
                        }
                        .padding(.horizontal)
                        
                        HStack {
                            SubInfoView(image: "car", info: "Free Delivery")
                            Spacer()
                            SubInfoView(image: "timer", info: "20min")
                        }
                        .padding(.top, 20)
                        .padding()
                        
                        Text("Description :")
                            .fontWeight(.medium)
                            .padding(.horizontal)
                        
                        Text(dessert.description)
                            .foregroundColor(.gray)
                            .fontWeight(.light)
                            .padding()
                    }
                    
                }
            }
            
            Button(action: {
                
            }) {
                Text("Add to Cart")
                    .foregroundColor(.white)
            }
            .padding()
            .frame(width: UIScreen.main.bounds.width / 1.1)
            .background(Color.green)
            .cornerRadius(35)
            .padding()
            
            Spacer()
        }
        .edgesIgnoringSafeArea(.all)
        .statusBar(hidden: true)
        .toolbar(content: {
            ToolbarItem(placement: .navigationBarLeading) {
                Button(action: {
                    self.presentationMode.wrappedValue.dismiss()
                }) {
                    Image(systemName: "chevron.backward")
                        .imageScale(.large)
                }
             }
             ToolbarItem(placement: .navigationBarTrailing) {
                 Button(action: {
                                        
                 }) {
                     Image(systemName: "heart")
                         .imageScale(.large).foregroundColor(.red)
                 }
             }
        })
    }
}

struct SubInfoView: View {
    var image: String
    var info: String
    var body: some View {
        HStack(spacing: 8) {
            Image(systemName: image)
            Text(info)
        }
    }
}

struct AmountView: View {
    
    @State var count = 1
    
    var body: some View {
        HStack {
            Button(action: {
                if self.count != 0{
                    self.count -= 1
                }
            }) {
                Text("-")
                    .font(.title)
                    .foregroundColor(.black)
                    .frame(width: 35, height: 35)
                    .background(Circle().stroke().foregroundColor(Color.green))
            }
            
            Text("\(self.count)")
                .font(.title2)
                .fontWeight(.bold)
            
            Button(action: {
                self.count += 1
            }) {
                Text("+")
                    .font(.title)
                    .foregroundColor(.black)
                    .frame(width: 35, height: 35)
                    .background(Circle().stroke().foregroundColor(Color.green))
            }
        }
    }
}

struct Header: View {
    var image: String
    var body: some View {
        ZStack(alignment: .top) {
            Image(image)
                .resizable()
                .scaledToFill()
                .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height / 2)
                .cornerRadius(45)
        }
    }
}

struct DessertDetails_Previews: PreviewProvider {
    static var previews: some View {
        DessertDetails()
    }
}
Model.swift
 
//
//  Model.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import Foundation

struct Dessert: Identifiable, Hashable {
    public var id: Int
    public var image: String
    public var name: String
    public var price: String
    public var description: String
}

var dessertData = [
    Dessert(id: 0, image: "desert1", name: "Leche Flan", price: "$2.99", description: "Leche Flan is a dessert made-up of eggs and milk with a soft caramel on top. It resembles crรจme caramel and caramel custard."),
    Dessert(id: 1, image: "desert2", name: "Maja Blanca", price: "$2.02", description: "Maja Blanca is a Filipino dessert made from coconut milk, cornstarch, and sugar. Often called Coconut Pudding, this luscious dessert is easy to make"),
    Dessert(id: 2, image: "desert3", name: "Yema", price: "$1.00", description: "Yema is a type of Filipino candy named after the Spanish term for egg yolks. I don't see the reason as to why not because egg yolk is a major ingredient "),
    Dessert(id: 3, image: "desert4", name: "Ube Halaya", price: "$3.99", description: "Ube Halaya or is a type of Filipino jam made from purple yam. It's commonly served as a midday snack or after-meal dessert"),
    Dessert(id: 4, image: "desert5", name: "Buko Salad", price: "$1.99", description: "The Buko Salad Recipe is prepared with young shredded coconut, canned fruits, cream and sweetened milk. A very popular dessert in every parties or occasion."),
    Dessert(id: 5, image: "desert6", name: "Polvoron", price: "$0.99", description: "Polvoron is a type of shortbread popular in Spain and its former colonies in Latin America and the Philippines."),
    Dessert(id: 6, image: "desert7", name: "Pastillas", price: "$0.85", description: "Pastillas de leche are sweet milk candies that are usually served for dessert. An authentic recipe will require the use of Carabao's"),
    Dessert(id: 7, image: "desert8", name: "Cassava Cake", price: "$1.99", description: "Cassava Cake is a classic Filipino dessert made from grated cassava (manioc). Cassava is also known as kamoteng kahoy and balinghoy"),
]

Thursday, April 21, 2022

SwiftUI Custom Badge Button

SwiftUI Custom Badge Button

ContentView.swift
 
//
//  ContentView.swift
//  CairocodersDev
//
//  Created by Cairocoders
//

import SwiftUI

struct ContentView: View {

    @State var count = 0
    @State var show = false
    
    var body: some View {
           
        GeometryReader { geometry in
            VStack {
                if self.show && self.count != 0 {
                    HStack(spacing: 12) {
                        
                        Image(systemName: "suit.heart.fill")
                            .resizable()
                            .frame(width: 20, height: 18)
                            .foregroundColor(.red)
                        
                        Text("\(self.count) Likes")
                    }
                    .padding([.horizontal,.top], 15)
                    .padding(.bottom, 30)
                    .background(Color.white)
                    .clipShape(ArrowShape())
                }
                
                Button(action: {
                    self.count += 1
                    self.show.toggle()
                }) {
                    Image(systemName: !self.show ? "suit.heart" : "suit.heart.fill")
                        .resizable()
                        .frame(width: 20, height: 18)
                        .foregroundColor(.red)
                        .padding()
                        .background(Color.white)
                        .clipShape(Circle())
                }
            }
            .frame(width: geometry.size.width, height: geometry.size.height)
        }
        .background(Color.black.opacity(0.06)).edgesIgnoringSafeArea(.all)
        .animation(.interactiveSpring(response: 0.6, dampingFraction: 1, blendDuration: 1))
        .onTapGesture {
            self.show.toggle()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct ArrowShape : Shape {
    
    func path(in rect: CGRect) -> Path {
        return Path{path in
            path.move(to: CGPoint(x: 0, y: 0))
            path.addLine(to: CGPoint(x: rect.width, y: 0))
            path.addLine(to: CGPoint(x: rect.width, y: rect.height - 10))
            
            path.addLine(to: CGPoint(x: (rect.width / 2) - 10, y: rect.height - 10))
            path.addLine(to: CGPoint(x: (rect.width / 2), y: rect.height))
            path.addLine(to: CGPoint(x: (rect.width / 2) + 10, y: rect.height - 10))
            
            path.addLine(to: CGPoint(x: 0, y: rect.height - 10))
        }
    }
}

Wednesday, April 13, 2022

SwiftUI how to implement infinite scrolling with view details

SwiftUI how to implement infinite scrolling with view details

ContentView.swift
 
//
//  ContentView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI
 
struct ContentView: View {
    
    @ObservedObject var sc = SourceCountry()
    @State var nextIndex = 1
    
    init() {
        sc.getCountry(at: 0)
    }
    
    @State var show = false
    
    var body: some View {
        NavigationView {
            ScrollView {
                LazyVStack(alignment: .leading) {
                    ForEach(sc.countries.indices, id: \.self) { countryIndex in
                        let country = sc.countries[countryIndex]
                        
                        NavigationLink(destination: DetailsView(viewdata: Country(id: country.id, emoji: country.emoji, name: country.name))) {
                            
                            VStack {
                                Text(country.name)
                                    .font(.title)
                                    .fontWeight(.bold)
                                
                                Text("\(country.emoji)")
                                    .font(.system(size: 200))
                                    .padding(.horizontal)
                                    .onAppear {
                                        if countryIndex == sc.countries.count - 3 {
                                            sc.getCountry(at: nextIndex)
                                            nextIndex += 1
                                        }
                                    }
                            }
                        }
                    }
                }
                .padding()
            }
            .navigationTitle("Country")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Model.swift
 
//
//  Model.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct Country: Identifiable {
    let id: Int
    let emoji: String
    let name: String
}

class SourceCountry: ObservableObject {
    
    @Published var countries = [Country]()
    
    func getCountry(at index: Int) {
        
        print("page", index)
        
        switch index {
        case 0:
            countries.append(contentsOf: [
                Country(id: 1, emoji: "๐Ÿ‡ฆ๐Ÿ‡ฉ", name: "AD"),
                Country(id: 2, emoji: "๐Ÿ‡ฆ๐Ÿ‡ช", name: "AE"),
                Country(id: 3, emoji: "๐Ÿ‡ฆ๐Ÿ‡ซ", name: "AF"),
                Country(id: 4, emoji: "๐Ÿ‡ฆ๐Ÿ‡ฌ", name: "AG"),
                Country(id: 5, emoji: "๐Ÿ‡ฆ๐Ÿ‡ฎ", name: "AI"),
                Country(id: 6, emoji: "๐Ÿ‡ฆ๐Ÿ‡ฑ", name: "AL"),
                Country(id: 7, emoji: "๐Ÿ‡ฆ๐Ÿ‡ฒ", name: "AM"),
                Country(id: 8, emoji: "๐Ÿ‡ฆ๐Ÿ‡ด", name: "AO"),
                Country(id: 9, emoji: "๐Ÿ‡ฆ๐Ÿ‡ถ", name: "AQ"),
                Country(id: 10, emoji: "๐Ÿ‡ฆ๐Ÿ‡ท", name: "AR")
            ])
            
        case 1:
            countries.append(contentsOf: [
                Country(id: 11, emoji: "๐Ÿ‡ฆ๐Ÿ‡ธ", name: "AS"),
                Country(id: 12, emoji: "๐Ÿ‡ฆ๐Ÿ‡น", name: "AT"),
                Country(id: 13, emoji: "๐Ÿ‡ฆ๐Ÿ‡บ", name: "AU"),
                Country(id: 14, emoji: "๐Ÿ‡ฆ๐Ÿ‡ผ", name: "AW"),
                Country(id: 15, emoji: "๐Ÿ‡ฆ๐Ÿ‡ฝ", name: "AX"),
                Country(id: 16, emoji: "๐Ÿ‡ฆ๐Ÿ‡ฟ", name: "AZ"),
                Country(id: 17, emoji: "๐Ÿ‡ง๐Ÿ‡ฆ", name: "BA"),
                Country(id: 18, emoji: "๐Ÿ‡ง๐Ÿ‡ง", name: "BB"),
                Country(id: 19, emoji: "๐Ÿ‡ง๐Ÿ‡ฉ", name: "BD"),
                Country(id: 20, emoji: "๐Ÿ‡ง๐Ÿ‡ช", name: "BE")
            ])
            

            
        case 2:
            countries.append(contentsOf: [
                Country(id: 21, emoji: "๐Ÿ‡ง๐Ÿ‡ซ", name: "BF"),
                Country(id: 22, emoji: "๐Ÿ‡ง๐Ÿ‡ฌ", name: "BG"),
                Country(id: 23, emoji: "๐Ÿ‡ง๐Ÿ‡ญ", name: "BH"),
                Country(id: 24, emoji: "๐Ÿ‡ง๐Ÿ‡ฎ", name: "BI"),
                Country(id: 25, emoji: "๐Ÿ‡ง๐Ÿ‡ฏ", name: "BJ"),
                Country(id: 26, emoji: "๐Ÿ‡ง๐Ÿ‡ฑ", name: "BL"),
                Country(id: 27, emoji: "๐Ÿ‡ง๐Ÿ‡ฒ", name: "BM"),
                Country(id: 28, emoji: "๐Ÿ‡ง๐Ÿ‡ณ", name: "BN"),
                Country(id: 29, emoji: "๐Ÿ‡ง๐Ÿ‡ด", name: "BO"),
                Country(id: 30, emoji: "๐Ÿ‡ง๐Ÿ‡ถ", name: "BQ")
            ])
            
        case 3:
            countries.append(contentsOf: [
                Country(id: 31, emoji: "๐Ÿ‡ง๐Ÿ‡ท", name: "BR"),
                Country(id: 32, emoji: "๐Ÿ‡ง๐Ÿ‡ธ", name: "BS"),
                Country(id: 33, emoji: "๐Ÿ‡ง๐Ÿ‡น", name: "BT"),
                Country(id: 34, emoji: "๐Ÿ‡ง๐Ÿ‡ป", name: "BV"),
                Country(id: 35, emoji: "๐Ÿ‡ง๐Ÿ‡ผ", name: "BW"),
                Country(id: 36, emoji: "๐Ÿ‡ง๐Ÿ‡พ", name: "BY"),
                Country(id: 37, emoji: "๐Ÿ‡ง๐Ÿ‡ฟ", name: "BZ"),
                Country(id: 38, emoji: "๐Ÿ‡จ๐Ÿ‡ฆ", name: "CA"),
                Country(id: 39, emoji: "๐Ÿ‡จ๐Ÿ‡จ", name: "CC"),
                Country(id: 40, emoji: "๐Ÿ‡จ๐Ÿ‡ฉ", name: "CD")
            ])
            
        default:
            break
        }
    }
}
DetailsView.swift
 
//
//  DetailsView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct DetailsView: View {
    
    var viewdata: Country
    
    var body: some View {
        VStack {
            Text("\(viewdata.emoji) \(viewdata.name)")
                .font(.system(size: 200))
                .padding(.horizontal)
        }
    }
}

struct DetailsView_Previews: PreviewProvider {
    static var previews: some View {
        DetailsView(viewdata: Country(id: 1, emoji: "๐Ÿ‡ฆ๐Ÿ‡ฉ", name: "Ph"))
    }
}

Tuesday, April 12, 2022

SwiftUI Clone Instagram Home Page

SwiftUI Clone Instagram Home Page

ContentView.swift
 
//
//  ContentView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI
 
struct ContentView: View {

    var body: some View {
        VStack {
            TitleView()
            ScrollView {
                VStack {
                    StoryView()
                    Divider()
                    Spacer()
                    
                    ForEach(userpost) { post in
                        PostView(viewpost: post)
                    }
                }
                .navigationBarHidden(true)
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
TitleView.swift
 
//
//  TitleView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct TitleView: View {
    var body: some View {
        HStack {
            Text("Instagram").font(.title)
            Spacer() // spacer to push buttons to right
            HStack(spacing: 16) { // right buttons
                Button (action: {
                   
                }) {
                    Image(systemName: "plus")
                        .resizable()
                        .frame(width: 10, height: 10)
                        .padding(4)
                }
                .overlay(RoundedRectangle(cornerSize: CGSize(width: 4, height: 4)).stroke(Color.black))
                Button (action: {
                }) {
                    Image(systemName: "heart")
                        .resizable()
                        .frame(width: 20, height: 20)
                }
                Button (action: {
                }) {
                    Image(systemName: "message")
                        .resizable()
                        .frame(width: 20, height: 20)
                }
            }
            .foregroundColor(.black) // make sure all button tint is black
        }
        .padding(EdgeInsets(top: 12, leading: 12, bottom: 4, trailing: 12))
    }
}

struct TitleView_Previews: PreviewProvider {
    static var previews: some View {
        TitleView()
    }
}
StoryView.swift
 
//
//  StoryView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct StoryView: View {
    
    @State var user: Userlist?
    
    var body: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            LazyHStack(alignment: .top, spacing: 0) {
                ForEach(users) { rs in
                    StoryItemView(viewdata: rs)
                        .padding(4)
                }
            }
        }
        .padding(.leading, 10)
        .listRowInsets(EdgeInsets())
    }
}

struct StoryItemView: View {
    
    @State var viewdata: Userlist
    
    var body: some View {
        VStack(spacing: 8) {
            Image(viewdata.photo) // image with red border
                .resizable()
                .frame(width: 64, height: 64, alignment: .center)
                .clipShape(Circle())
                .overlay(Circle().stroke(Color.red, lineWidth: 2))
            Text(viewdata.username)
                .font(.caption)
                .lineLimit(1)
        }
        .frame(width: 72) // fixed size
    }
}

struct StoryView_Previews: PreviewProvider {
    static var previews: some View {
        StoryView()
    }
}
PostView.swift
 
//
//  PostView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct PostView: View {
    
    @State var viewpost: UserPost
    
    @State private var comment: String = ""
    var body: some View {
        VStack (alignment: .leading) {
            // User profile
            HStack {
                Image(viewpost.photo)
                    .resizable()
                    .frame(width: 40, height: 40)
                    .clipShape(Circle())
                VStack(alignment: .leading, spacing: 4) {
                    Text(viewpost.username).font(.subheadline).bold()
                    Text(viewpost.place)
                        .font(.caption)
                        .opacity(0.8)
                }
                Spacer()
                Button(action: {
                    
                }) {
                    Text("...").bold()
                }
                .foregroundColor(.black)
            }
            .padding(.leading, 12)
            .padding(.trailing, 12)
            // image
            Image(viewpost.postimage)
                .resizable()
                .scaledToFill()
                .frame(height: 280)
                .frame(maxWidth: .infinity)
                .clipped()
            // buttons
            HStack (spacing: 16) {
                Button (action: {
                    // add action here
                }) {
                    Image(systemName: "heart")
                        .resizable()
                        .frame(width: 18, height: 18)
                }
                Button (action: {
                    // add action here
                }) {
                    Image(systemName: "message")
                        .resizable()
                        .frame(width: 18, height: 18)
                }
                Button (action: {
                    // add action here
                }) {
                    Image(systemName: "location")
                        .resizable()
                        .frame(width: 18, height: 18)
                }
                Spacer()
                Button (action: {
                    // add action here
                }) {
                    Image(systemName: "bookmark")
                        .resizable()
                        .frame(width: 18, height: 18)
                }
            }
            .foregroundColor(.black)
            .padding(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16))
            // Text
            VStack (alignment: .leading, spacing: 4) {
                Text("5.3K likes").font(.caption).bold()
                Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et").font(.caption)
                // profile image and comment textfield
                HStack {
                    Image("4")
                        .resizable()
                        .frame(width: 30, height: 30)
                        .clipShape(Circle())
                    TextField("Add a comment...", text: $comment).font(.subheadline)
                }
                // datetime
                Text("2 hours ago")
                    .font(.caption)
                    .foregroundColor(.gray)
            }
            .padding(.leading, 16)
            .padding(.trailing, 16)
        }
        .padding(.bottom, 24)
    }
}

struct PostView_Previews: PreviewProvider {
    static var previews: some View {
        PostView(viewpost: UserPost(id: 1, username: "cairocoders", place: "olongapo", description: "sample description", photo: "1", postimage: "post1"))
    }
}
Model.swift
 
//
//  Model.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct Userlist: Identifiable {
 
    let id: Int
    let username: String
    let photo : String
}
 
var users = [
    Userlist(id: 0, username: "Olivia", photo: "1"),
    Userlist(id: 1, username: "Emma", photo: "2"),
    Userlist(id: 2, username: "Ava", photo: "3"),
    Userlist(id: 3, username: "Charlotte", photo: "4"),
    Userlist(id: 4, username: "Sophia", photo: "5"),
    Userlist(id: 5, username: "Amelia", photo: "6")
]

struct UserPost: Identifiable {
 
    let id: Int
    let username: String
    let place: String
    let photo : String
    let postimage : String
}

var userpost = [
    UserPost(id: 0, username: "Olivia", place: "Olongapo City", photo: "1", postimage: "post1"),
    UserPost(id: 1, username: "Emma", place: "Angeles City", photo: "2", postimage: "post2"),
    UserPost(id: 2, username: "Ava", place: "San Fernando", photo: "3", postimage: "post3"),
    UserPost(id: 3, username: "Charlotte", place: "Manila", photo: "4", postimage: "post4"),
    UserPost(id: 4, username: "Sophia", place: "Pasay", photo: "5", postimage: "post5"),
    UserPost(id: 5, username: "Amelia", place: "Baliwag", photo: "6", postimage: "post6")
]

Wednesday, April 6, 2022

SwiftUI Infinite Scroll Load More Data github user API with sheet view

SwiftUI Infinite Scroll Load More Data github user API with sheet view

Github API
https://api.github.com/users?per_page=15

SDWebImage
https://github.com/SDWebImage/SDWebImageSwiftUI.git

https://docs.github.com/en/rest/reference/users#list-users

ContentView.swift
 
//
//  ContentView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import Combine
import SwiftUI

struct ContentView: View {
    @ObservedObject private var userViewModel = UserViewModel()
    
    @State var columns = Array(repeating: GridItem(.flexible(), spacing: 15), count: 2)
    
    var body: some View {
        ScrollView(.vertical, showsIndicators: false) {
            HStack{
                 
                Text("GitHub User")
                    .font(.title)
                    .fontWeight(.bold)
                 
                Spacer()
                 
                Button {
                     
                } label: {
                 
                    Image(systemName: "rectangle.grid.1x2")
                        .font(.system(size: 24))
                        .foregroundColor(.black)
                }

            }
            .padding(.horizontal)
            .padding(.top,25)
             
            LazyVGrid(columns: self.columns,spacing: 25){
                 
                ForEach(userViewModel.users, id: \.id) { user in
                    UserRow(user: user)
                } 
                LoaderView(isFailed: userViewModel.isRequestFailed)
                    .onAppear(perform: fetchData)
                    .onTapGesture(perform: onTapLoadView)
            }
            .padding([.horizontal,.top])
        }
        .background(Color.black.opacity(0.05).edgesIgnoringSafeArea(.all))
        
    }
    
    private func fetchData() {
        userViewModel.getUsers()
    }
    
    private func onTapLoadView() {
        // tap to reload
        if userViewModel.isRequestFailed {
            userViewModel.isRequestFailed = false
            fetchData()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
APIService.swift
 
//
//  APIService.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import Foundation
import Combine

class APIService {
    static let shared = APIService()
    func getUsers(perPage: Int = 30, sinceId: Int? = nil) -> AnyPublisher<[User], Error> {
        var components = URLComponents(string: "https://api.github.com/users")!
        components.queryItems = [
            URLQueryItem(name: "per_page", value: "\(perPage)"),
            URLQueryItem(name: "since", value: (sinceId != nil) ? "\(sinceId!)" : nil)
        ]
        
        let request = URLRequest(url: components.url!, timeoutInterval: 5)
        return URLSession.shared.dataTaskPublisher(for: request)
            .map(\.data)
            .decode(type: [User].self, decoder: JSONDecoder())
            .eraseToAnyPublisher()
    }
}
LoaderView.swift
 
//
//  LoaderView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct LoaderView: View {
    let isFailed: Bool
    var body: some View {
        Text(isFailed ? "Failed. Tap to retry." : "Loading..")
            .foregroundColor(isFailed ? .red : .green)
            .padding()
            .font(.title)
    }
}

struct LoaderView_Previews: PreviewProvider {
    static var previews: some View {
        LoaderView(isFailed: false)
    }
}
User.swift
 
//
//  User.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import Foundation

struct User: Decodable, Identifiable {
    let id: Int
    let name: String
    let avatarUrl: String
    
    enum CodingKeys: String, CodingKey {
        case id
        case name = "login"
        case avatarUrl = "avatar_url"
    }
}
UserRow.swift
 
//
//  UserRow.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI
import SDWebImageSwiftUI //https://github.com/SDWebImage/SDWebImageSwiftUI.git

struct UserRow: View {
    let user: User
    @State var show = false
    
    var body: some View {
        VStack(spacing: 15){
            ZStack(alignment: Alignment(horizontal: .trailing, vertical: .top)) {
                
                Button {
                    show.toggle()
                } label: {
                    AnimatedImage(url: URL(string: user.avatarUrl)!)
                        .resizable()
                        .frame(height: 250)
                        .cornerRadius(15)
                }
                Button {
                } label: {
                                      
                    Image(systemName: "heart.fill")
                            .foregroundColor(.red)
                            .padding(.all,10)
                            .background(Color.white)
                            .clipShape(Circle())
                }
                .padding(.all,10)
            }
            Text(user.name)
                .fontWeight(.bold)
        }
        .sheet(isPresented: $show, content: {
            DetailsView(user: User(id: user.id, name: user.name, avatarUrl: user.avatarUrl))
        })
    }
}

struct UserRow_Previews: PreviewProvider {
    static var previews: some View {
        let mockUser = User(id: 1, name: "cairocoders", avatarUrl: "")
        UserRow(user: mockUser)
    }
}
UserViewModel.swift
 
//
//  UserViewModel.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import Foundation
import Combine

class UserViewModel: ObservableObject {
    @Published var users: [User] = []
    @Published var isRequestFailed = false
    private let pageLimit = 10
    private var currentLastId: Int? = nil
    private var cancellable: AnyCancellable?
    
    func getUsers() {
        cancellable = APIService.shared.getUsers(perPage: pageLimit, sinceId: currentLastId)
            .receive(on: DispatchQueue.main)
            .sink { completion in
                switch completion {
                case .failure(let error):
                    self.isRequestFailed = true
                    print(error)
                case .finished:
                    print("finished loading")
                }
            } receiveValue: { users in
                self.users.append(contentsOf: users)
                self.currentLastId = users.last?.id
            }

    }
}
DetailsView.swift
 
//
//  DetailsView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI
import SDWebImageSwiftUI

struct DetailsView: View {
    var user: User
    
    var body: some View {
        VStack {
            HStack {
                AnimatedImage(url: URL(string: user.avatarUrl)!)
                    .resizable()
                    .frame(width: 250, height: 250)
                    .cornerRadius(15)
            }
            
            HStack {
                Text("User Name :")
                Text(user.name)
            }
        }
    }
}

Tuesday, April 5, 2022

SwiftUI News API App - Load More Data List View Infinite Scroll

SwiftUI News API App - Load More Data List View Infinite Scroll

News API : https://newsapi.org/

ContentView.swift
 
//
//  ContentView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct ContentView: View {

    var body: some View {
        NewsFeedView()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct NewsFeedView: View {
    @ObservedObject var newsFeed = NewsFeed()
    
    var body: some View {
        NavigationView {
            List(newsFeed) { (article: NewsListItem) in
                NavigationLink(destination: NewsListItemView(article: article)) {
                    NewsListItemListView(article: article)
                        .onAppear {
                            self.newsFeed.loadMoreArticles(currentItem: article)
                    }
                }
            }
        .navigationBarTitle("Apple News")
        }
    }
}

struct NewsListItemView: View {
    var article: NewsListItem
    
    var body: some View {
        VStack {
            UrlWebView(urlToDisplay: URL(string: article.url)!)
                .edgesIgnoringSafeArea(.all)
                .navigationBarTitle(article.title)
        }
    }
}

struct NewsListItemListView: View {
    var article: NewsListItem
    
    var body: some View {
        HStack {
            VStack(alignment: .leading) {
                Text("\(article.title)")
                    .font(.headline)
                Text("\(article.author ?? "No Author")")
                    .font(.subheadline)
            }
        }
    }
}
NewsFeedModels.swift
 
//
//  NewsFeedModels.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import Foundation

class NewsFeed: ObservableObject, RandomAccessCollection {
    typealias Element = NewsListItem
    
    @Published var newsListItems = [NewsListItem]()
    
    var startIndex: Int { newsListItems.startIndex }
    var endIndex: Int { newsListItems.endIndex }
    var loadStatus = LoadStatus.ready(nextPage: 1)
    
    var urlBase = "https://newsapi.org/v2/everything?q=apple&sortBy=popularity&apiKey=55c250f06e1144c29a3ec4d2530adbe5&language=en&page="
 
    init() {
        loadMoreArticles()
    }
    
    subscript(position: Int) -> NewsListItem {
        return newsListItems[position]
    }
    
    func loadMoreArticles(currentItem: NewsListItem? = nil) {
        
        if !shouldLoadMoreData(currentItem: currentItem) {
            return
        }
        guard case let .ready(page) = loadStatus else {
            return
        }
        loadStatus = .loading(page: page)
        let urlString = "\(urlBase)\(page)"
        
        let url = URL(string: urlString)!
        let task = URLSession.shared.dataTask(with: url, completionHandler: parseArticlesFromResponse(data:response:error:))
        task.resume()
    }
    
    func shouldLoadMoreData(currentItem: NewsListItem? = nil) -> Bool {
        guard let currentItem = currentItem else {
            return true
        }
        
        for n in (newsListItems.count - 4)...(newsListItems.count-1) {
            if n >= 0 && currentItem.uuid == newsListItems[n].uuid {
                return true
            }
        }
        return false
    }
    
    func parseArticlesFromResponse(data: Data?, response: URLResponse?, error: Error?) {
        guard error == nil else {
            print("Error: \(error!)")
            loadStatus = .parseError
            return
        }
        guard let data = data else {
            print("No data found")
            loadStatus = .parseError
            return
        }
        
        let newArticles = parseArticlesFromData(data: data)
        DispatchQueue.main.async {
            self.newsListItems.append(contentsOf: newArticles)
            if newArticles.count == 0 {
                self.loadStatus = .done
            } else {
                guard case let .loading(page) = self.loadStatus else {
                    fatalError("loadSatus is in a bad state")
                }
                self.loadStatus = .ready(nextPage: page + 1)
            }
        }
    }
    
    func parseArticlesFromData(data: Data) -> [NewsListItem] {
        var response: NewsApiResponse
        do {
            response = try JSONDecoder().decode(NewsApiResponse.self, from: data)
        } catch {
            print("Error parsing the JSON: \(error)")
            return []
        }
        
        if response.status != "ok" {
            print("Status is not ok: \(response.status)")
            return []
        }
        
        return response.articles ?? []
    }
    
    enum LoadStatus {
        case ready (nextPage: Int)
        case loading (page: Int)
        case parseError
        case done
    }
}

class NewsApiResponse: Codable {
    var status: String
    var articles: [NewsListItem]?
}

class NewsListItem: Identifiable, Codable {
    var uuid = UUID()
    
    var author: String?
    var title: String
    var url: String
    
    enum CodingKeys: String, CodingKey {
        case author, title, url
    }
}
UrlWebView.swift
 
//
//  UrlWebView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI
import WebKit

struct UrlWebView: UIViewRepresentable {
    typealias UIViewType = WKWebView
    
    var urlToDisplay: URL
    
    func makeUIView(context: Context) -> WKWebView {
        let webView = WKWebView()
        
        webView.load(URLRequest(url: urlToDisplay))
        
        return webView
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {
        
    }
}

Thursday, March 24, 2022

SwiftUI Highlight and Shake Invalid Username and Password with show password toggle

SwiftUI Highlight and Shake Invalid Username and Password with show password toggle

ContentView.swift
 
//
//  ContentView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct ContentView: View {
    
    @State var username = ""
    @State var password = ""
    @State var invalidpassword = 0
    @State var invalidusername = 0
    @State private var showText: Bool = false
    
    var body: some View {
        VStack {
            HStack{
                Image("logo")
                    .resizable()
                    .frame(width: 150, height: 130, alignment: .center)
            }
            
            HStack {
                Image(systemName: "person")
                    .foregroundColor(.gray)
                TextField("Username", text: $username)
            }
            .padding()
            .background(Color("Color"))
            .cornerRadius(8)
            .overlay(RoundedRectangle(cornerRadius: 8)
                .stroke(lineWidth: 1)
                .foregroundColor(invalidusername == 0 ? Color.clear : Color.red)
            )
            .padding(.bottom, 20)
            .modifier(ShakeEffect(animatableData: CGFloat(invalidusername)))
            
            HStack {
                Image(systemName: "lock")
                    .foregroundColor(.gray)
                if showText {
                    TextField("", text: $password)
                }else {
                    SecureField("Password", text: $password)
                }
                Button(action: {
                    showText.toggle()
                }, label: {
                    Image(systemName: showText ? "eye.slash.fill" : "eye.fill")
                        .foregroundColor(.gray)
                })
            }
            .padding()
            .background(Color("Color"))
            .cornerRadius(8)
            .overlay(RoundedRectangle(cornerRadius: 8)
                .stroke(lineWidth: 1)
                .foregroundColor(invalidpassword == 0 ? Color.clear : Color.red)
            )
            .padding(.bottom, 20)
            .modifier(ShakeEffect(animatableData: CGFloat(invalidpassword)))
            
            Button(action: {
            
            }, label: {
                Text("Forgot password").padding()
            })
            
            Button(action: {
                withAnimation(.default) {
                    loginUser()
                }
            }, label: {
                Text("Login")
                    .font(.headline).bold().foregroundColor(.white).padding()
                    .frame(width: 250, height: 50)
                    .background(Color.blue)
                    .cornerRadius(10)
            })
            
            HStack {
                Text("Don't have an account?")
                
                Button(action: {
                    
                }, label: {
                    Text("Sign Up")
                })
            }.padding()
            
        }.padding()
    }
    
    private func loginUser() {
        if username == "" {
            self.invalidusername += 1
            print("Username is required")
        } else if password == "" {
            self.invalidpassword += 1
            self.invalidusername = 0
            print("password is required")
        }else {
            self.invalidpassword = 0
            print("email : \(username) pass : \(password)")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct ShakeEffect : GeometryEffect {
    var travelDistance : CGFloat = 6
    var numOfShake : CGFloat = 4
    var animatableData: CGFloat
    
    func effectValue(size: CGSize) -> ProjectionTransform {
        ProjectionTransform(CGAffineTransform(translationX: travelDistance * sin(animatableData * .pi * numOfShake), y: 0))
    }
}

Thursday, March 17, 2022

SwiftUI Note App UserDefaults

SwiftUI Note App UserDefaults

ContentView.swift
 
//
//  ContentView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct ContentView: View {
    @StateObject var notes = Notes()
    @State private var sheetIsShowing = false
    
    var body: some View {
        NavigationView {
            VStack {
                NoteView()
                    .sheet(isPresented: $sheetIsShowing) {
                        AddNew()
                    }
            }
            .navigationTitle("Notes")
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button {
                        withAnimation {
                            self.sheetIsShowing.toggle()
                        }
                    } label: {
                        Label("Add Note", systemImage: "plus.circle.fill")
                    }
                }
            }
        }
        .environmentObject(notes)
    }
    
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
NotesData.swift
 
//
//  NotesData.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import Foundation
import SwiftUI

struct Note : Codable, Identifiable {
    var id = UUID()
    var title: String
    var content: String
    var timeStamp: String
}

@MainActor class Notes : ObservableObject {
    private let NOTES_KEY = "cairocodersnoteskey"
    let date = Date()
    var notes: [Note] {
        didSet {
            saveData()
            objectWillChange.send()
        }
    }
    
    init() {
        // Load saved data
        if let data = UserDefaults.standard.data(forKey: NOTES_KEY) {
            if let decodedNotes = try? JSONDecoder().decode([Note].self, from: data) {
                notes = decodedNotes
                return
            }
        }
        // Tutorial Note
        notes = [Note(title: "Test Note", content: "Tap add button. You can delete any note by swiping to the left!", timeStamp: date.getFormattedDate(format: "yyyy-MM-dd HH:mm:ss"))]
    }
    
    func addNote(title: String, content: String) {
        let tempNote = Note(title: title, content: content, timeStamp: date.getFormattedDate(format: "yyyy-MM-dd HH:mm:ss"))
        notes.insert(tempNote, at: 0)
        saveData()
    }
    
    // Save data
    private func saveData() {
        if let encodedNotes = try? JSONEncoder().encode(notes) {
            UserDefaults.standard.set(encodedNotes, forKey: NOTES_KEY)
        }
    }
}

extension Date {
   func getFormattedDate(format: String) -> String {
        let dateformat = DateFormatter()
        dateformat.dateFormat = format
        return dateformat.string(from: self)
    }
}
NoteView.swift
 
//
//  NoteView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct NoteView: View {
    @EnvironmentObject var notes: Notes
    
    var body: some View {
        List {
            ForEach(notes.notes) { note in
                VStack(alignment: .leading) {
                    Text(note.title)
                        .foregroundColor(.blue)
                        .font(.headline)
                    Text(note.content)
                        .font(.body)
                        .padding(.vertical)
                
                    HStack {
                        Spacer()
                        Text("\(note.timeStamp)")
                            .foregroundColor(.gray)
                            .italic()
                    }
                }
            }
            .onDelete(perform: deleteNote)
        }
    }
    
    func deleteNote(at offsets: IndexSet) {
        notes.notes.remove(atOffsets: offsets)
    }
}

struct NoteView_Previews: PreviewProvider {
    static var previews: some View {
        NoteView()
            .environmentObject(Notes())
    }
}
AddNew.swift
 
//
//  AddNew.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct AddNew: View {
    @State private var title = ""
    @State private var content = ""
    @EnvironmentObject var notes: Notes
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        Form {
            Section {
                TextField("Give your note a title", text: $title)
                ZStack {
                    TextEditor(text: $content)
                        .frame(height: 200)
                    VStack {
                        Spacer()
                        HStack {
                            Spacer()
                            Text("\(content.count)/120")
                                .foregroundColor(.gray)
                                .padding()
                        }
                    }
                }
                HStack {
                    Spacer()
                    Button("Add note!") {
                        notes.addNote(title: title, content: content)
                        dismiss()
                    }
                    Spacer()
                }
            }
        }
    }
}

struct AddNew_Previews: PreviewProvider {
    static var previews: some View {
        AddNew()
            .environmentObject(Notes())
    }
}

Wednesday, March 16, 2022

SwiftUI Sliding Tabs | SlidingTab Package

SwiftUI Sliding Tabs | SlidingTab Package

https://github.com/QuynhNguyen/SlidingTabView

SlidingTabView is a simple Android-Like tab view that is built using the latest and greatest SwiftUI. Almost everything is customizable!



ContentView.swift
 
//
//  ContentView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI
import SlidingTabView

struct ContentView: View {
    @State private var tabIndex = 0
    
    var body: some View {
        VStack {
            SlidingTabView(selection: $tabIndex, tabs: ["Home", "Friends", "Settings"], animation: .easeInOut)
            Spacer()
            
            if tabIndex == 0 {
                TabAView()
            } else if tabIndex == 1 {
                TabBView()
            } else if tabIndex == 2 {
                TabCView()
            }
            Spacer()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct TabAView: View {
    var body: some View {
        VStack {
            Image("1")
                .resizable()
            Text("Tab Home")
                .font(.title)
        }
    }
}

struct TabBView: View {
    var body: some View {
        VStack {
            Image("2")
                .resizable()
            Text("Tab Friends")
                .font(.title)
        }
    }
}

struct TabCView: View {
    var body: some View {
        VStack {
            Image("3")
                .resizable()
            Text("Tab Setting")
                .font(.title)
        }
    }
}

SwiftUI Alert - SPAlert Package

SwiftUI Alert - SPAlert Package

SPAlert https://github.com/ivanvorobei/SPAlert

ContentView.swift
 
//
//  ContentView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI
import SPAlert

struct ContentView: View {
    @State var showAlert = false
    @State var showAlert2 = false
    
    
    var body: some View {
        VStack(spacing: 20) {
            Button("Show Alert") {
                showAlert.toggle()
            }
            .SPAlert(
                isPresent: $showAlert,
                message: "SwiftUI Alert - SPAlert Package",
                duration: 1.0)
            
            Button("Show Alert 2") {
                showAlert2.toggle()
            }
            .SPAlert(
                isPresent: $showAlert2,
                title: "SPAlert Package",
                message: "This tutorial has been completed",
                duration: 2.0,
                dismissOnTap: false,
                preset: .done,
                //preset: .custom(UIImage(systemName: "checkmark.seal.fill")!),
                haptic: .success,
                layout: .init(),
                completion: {
                    print("This is a completion log.")
                    showAlert.toggle()
                })
            .tint(.orange)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Tuesday, March 15, 2022

SwiftUI HalfASheet package | half sheet

SwiftUI HalfASheet package | half sheet

Half Sheet Package : https://github.com/franklynw/HalfASheet

ContentView.swift
 
//
//  ContentView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI
import HalfASheet

struct ContentView: View {
    @State private var isShowing = false
    @State private var amount = 0.0
    
    var body: some View {
        ZStack {
            Button("Show sheet") {
                isShowing.toggle()
            }
            HalfASheet(isPresented: $isShowing, title: "Rotation") {
                VStack(spacing: 20) {
                    Image(systemName: "airplane")
                        .font(.system(size: 80))
                        .foregroundColor(.blue)
                        .rotationEffect(Angle(degrees: amount))
                    
                    Slider(value: $amount, in: 0...90)
                    
                    Text("Degrees: \(Int(amount))")
                        .italic()
                }
                .padding()
            }
            // Customise by editing these.
            .height(.proportional(0.40))
            .closeButtonColor(UIColor.white)
            .backgroundColor(.white)
            .contentInsets(EdgeInsets(top: 30, leading: 10, bottom: 30, trailing: 10))
        }
        .ignoresSafeArea()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Thursday, March 10, 2022

SwiftUI Login Register Page UI Design with show hide password

SwiftUI Login Register Page UI Design with show hide password

ContentView.swift
 
//
//  ContentView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct ContentView: View {
    var body: some View {
        
        NavigationView{
            
            SignIn()
            .navigationBarTitle("")
            .navigationBarHidden(true)
        }
        
        
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct SignIn : View {
    
    @State var user = ""
    @State var pass = ""
    @State var show = false
    
    var body : some View{
        
        ZStack{
            NavigationLink(destination: Register(show: self.$show), isActive: self.$show) {
                Text("")
            }
            
            VStack{
                HStack{
                    Image("bgtop")
                        .resizable()
                    
                    Spacer()
                }
                
                VStack{
                    Image("logo")
                        .resizable()
                        .frame(width: 150, height: 130, alignment: .center)
                    
                }.offset(y: -40)
                .padding(.bottom,-60)
                
                VStack(spacing: 20){
                    
                    Text("Sign In").font(.title).fontWeight(.bold)
                    
                    Text("Sign Into Your Account").fontWeight(.bold)
                    
                    CustomField(value: self.$user, isemail: true)
                    
                    CustomField(value: self.$pass, isemail: false)
                    
                    HStack{
                        
                        Spacer()
                        
                        Button(action: {
                            
                        }) {
                            Text("Forget Password ?").foregroundColor(Color.black.opacity(0.1))
                        }
                    }
                    
                    Button(action: {
                        loginUser()
                    }) {
                        Text("Login")
                            .frame(width: UIScreen.main.bounds.width - 100)
                            .padding(.vertical)
                            .foregroundColor(.white)
                        
                    }.background(Color("Color1"))
                    .clipShape(Capsule())
                    
                    Text("Or Login Using Social Media").fontWeight(.bold)
                    
                    SocialMedia()
                    
                }.padding()
                .background(Color.white)
                .cornerRadius(5)
                .padding()
                
                HStack{
                    
                    Text("Don't Have an Account ?")
                    
                    Button(action: {
                        self.show.toggle()
                    }) {
                        Text("Register Now").foregroundColor(Color("Color1"))
                    }
                    
                }.padding(.top)
                
                Spacer(minLength: 0)
                
            }.edgesIgnoringSafeArea(.top)
            .background(Color("Color").edgesIgnoringSafeArea(.all))
        }
    }
    
    private func loginUser() {
        print("email : \(user) pass : \(pass)")
        
    }
}

struct CustomField : View {
    
    @Binding var value : String
    var isemail = false
    var reenter = false
    
    @State private var showText: Bool = false
    
    var body : some View{
        
        VStack(spacing: 8){
            
            HStack{
                Text(self.isemail ? "Email ID" : self.reenter ? "Re-Enter" : "Password").foregroundColor(Color.black.opacity(0.1))
                
                Spacer()
            }
            HStack{
                if self.isemail{
                    TextField("", text: self.$value)
                    
                    Image(systemName: "envelope.fill").foregroundColor(Color("Color1"))
                }
                else{
                    if showText {
                        TextField("", text: self.$value)
                    }else{
                        SecureField("", text: self.$value)
                            .disableAutocorrection(true)
                            .autocapitalization(.none)
                    }
                    
                    Button(action: {
                        showText.toggle()
                    }, label: {
                        Image(systemName: showText ? "eye.slash.fill" : "eye.fill")
                            .foregroundColor(Color("Color1"))
                    })
                }
            }
            
            Divider()
        }
    }
}
Register.swift
//
//  Register.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct Register : View {
    
    @State var user = ""
    @State var pass = ""
    @State var repass = ""
    @State var agree = false
    @Binding var show : Bool
    
    var body : some View{
        
        ZStack(alignment: .topLeading) {
            
            VStack{
                VStack{
                    
                    Image("logo")
                        .resizable()
                        .frame(width: 150, height: 130, alignment: .center)
                    
                }.offset(y: 50)
                .padding(.bottom,90)
                
                VStack(spacing: 20){
                    
                    Text("Sign Up").font(.title).fontWeight(.bold)
                    
                    Text("Sign Into Your Account").fontWeight(.bold)
                    
                    CustomField(value: self.$user, isemail: true)
                    
                    CustomField(value: self.$pass, isemail: false)
                    
                    CustomField(value: self.$repass, isemail: false,reenter: true)
                    
                    HStack{
                        Button(action: {
                            self.agree.toggle()
                        }) {
                            ZStack{
                                Circle().fill(Color.black.opacity(0.05)).frame(width: 20, height: 20)
                                if self.agree{
                                    Image("check").resizable().frame(width: 10, height: 10)
                                        .foregroundColor(Color("Color1"))
                                }
                            }
                        }
                        
                        Text("I Read And Agree The Terms And Conditions").font(.caption)
                            .foregroundColor(Color.black.opacity(0.1))
                        
                        Spacer()

                    }
                    
                    Button(action: {
                        
                    }) {
                        Text("Register Now")
                            .frame(width: UIScreen.main.bounds.width - 100)
                            .padding(.vertical)
                            .foregroundColor(.white)
                        
                    }.background(Color("Color1"))
                    .clipShape(Capsule())
                    
                    
                    Text("Or Register Using Social Media").fontWeight(.bold)
                    
                    SocialMedia()
                    
                }.padding()
                .background(Color.white)
                .cornerRadius(5)
                .padding()
                
                
                Spacer(minLength: 0)
                
            }.edgesIgnoringSafeArea(.top)
            .background(Color("Color").edgesIgnoringSafeArea(.all))
            
            Button(action: {
                self.show.toggle()
            }) {
                Image(systemName: "arrow.left").resizable().frame(width: 18, height: 15).foregroundColor(.black)
                
            }.padding()
            
        }.navigationBarTitle("")
        .navigationBarHidden(true)
    }
}

struct Register_Previews: PreviewProvider {
    @State static var show = false
    static var previews: some View {
        Register(show: $show)
    }
}
SocialMedia.swift
//
//  SocialMedia.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct SocialMedia : View {
    
    var body : some View{
        
        HStack(spacing: 40){
            
            Button(action: {
                
            }) {
                Image("fb").renderingMode(.original)
            }
            
            Button(action: {
                
            }) {
                Image("twitter").renderingMode(.original)
            }
        }
    }
}

struct SocialMedia_Previews: PreviewProvider {
    static var previews: some View {
        SocialMedia()
    }
}

Tuesday, March 8, 2022

SwiftUI onChange Event Count Characters input

SwiftUI onChange Event Count Characters input

ContentView.swift
 
//
//  ContentView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct ContentView: View {
   @State private var text: String = ""
   @State private var color: Color = Color.indigo
   @State private var count: Int = 0
   
   var body: some View {
      ZStack {
         HStack {
            TextField("Enter Text", text: $text)
               .onChange(of: text) { text in
                  let letters = text.trimmingCharacters(in: .whitespaces).count
                  self.count = letters
                  
                  switch letters {
                  case 1..<10:
                     self.color = .red
                  case 10..<20:
                      self.color = .blue
                  case 20...:
                     self.color = .green
                  default:
                      self.color = .indigo
                  }
               }
               .foregroundColor(color)
            
            
            Text("\(count)")
               .bold()
               .foregroundColor(.gray)
         }
         .padding()
         .overlay(
            RoundedRectangle(cornerRadius: 30)
            .stroke(color, lineWidth: 5)
         )
         .padding()
      }
   }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Monday, March 7, 2022

SwiftUI Simple Custome Modifier

SwiftUI Simple Custome Modifier

ContentView.swift
 
//
//  ContentView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

extension View {
    func addNavigationView(title: String) -> some View {
        NavigationView {
            self
                .navigationTitle(title)
        }
    }
    
    func centerHorizontal() -> some View {
        HStack {
            Spacer()
            self
            Spacer()
        }
    }
    
    func customfont() -> some View {
        self
            .font(.system(size: 20, weight: .bold, design: .monospaced))
            .foregroundColor(.indigo)
    }
}

struct ContentView: View {
    var body: some View {
        Form {
            Text("Hello, world!")
                .customfont()
                .centerHorizontal()
            
            Button("Submit") {
                print("Button pressed.")
            }
            .centerHorizontal()
        }
        .addNavigationView(title: "Custom Modifier")
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Sunday, March 6, 2022

SwiftUI 3D Scroll Effect

SwiftUI 3D Scroll Effect

 
//
//  ContentView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct ContentView: View {
    
    @State var user: Userlist?
    
    var body: some View {
        VStack {
            ScrollView(.horizontal, showsIndicators: false) {
                HStack(alignment: .center, spacing: 230) {
                    ForEach(users) { rs in
                        GeometryReader { geometry in
                            Image(rs.photo)
                                .resizable().frame(width: 210, height: 350, alignment: .center).cornerRadius(16)
                                .overlay(RoundedRectangle(cornerRadius: 10)
                                    .stroke(Color.gray, lineWidth: 4)
                                    .frame(width: 210, height: 350, alignment: .center)
                                    .cornerRadius(16)
                                    .shadow(color: Color.gray.opacity(0.2), radius: 20, x: 0, y: 0)
                                )
                                .shadow(radius: 16)
                                .rotation3DEffect(Angle(degrees: (Double(geometry.frame(in: .global).minX) - 210) / -20), axis: (x: 0, y: 1.0, z: 0))
                            HStack {
                                Text(rs.username)
                                    .foregroundColor(.white)
                                    .font(.title)
                            
                            }.frame(width: 210, alignment: .center)
                        }
                    }
                }.padding(.horizontal, 210)
                .padding(.top, 150)

            }
        }
        .background(Color.gray)
        .edgesIgnoringSafeArea(.all)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


struct Userlist: Identifiable {
  
    let id: Int
    let username: String
    let photo : String
}
  
var users = [
    Userlist(id: 0, username: "Olivia", photo: "1"),
    Userlist(id: 1, username: "Emma", photo: "2"),
    Userlist(id: 2, username: "Ava", photo: "3"),
    Userlist(id: 3, username: "Charlotte", photo: "4"),
    Userlist(id: 4, username: "Sophia", photo: "5"),
    Userlist(id: 5, username: "Caite", photo: "6"),
    Userlist(id: 6, username: "Amelia", photo: "7")
]

Thursday, March 3, 2022

SwiftUI FeedBack | Rating Emoji

SwiftUI FeedBack | Rating Emoji

ContentView.swift
 
//
//  ContentView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct ContentView: View {
    
    @State var show = false
    
    var body: some View {
        
        HStack(spacing: 20){
            VStack(alignment: .leading,spacing: 12){
                Button(action: {
                    self.show.toggle()
                }) {
                    Image("feedback")
                        .resizable().frame(width: 300, height: 150)
                }
            }
        }.sheet(isPresented: $show) {
            feedback()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
feedback.swift
 
//
//  feedback.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct feedback: View {
    
    let reactions = ["๐Ÿ˜ฃ" , "☹️" , "๐Ÿ˜" , "๐Ÿ™‚" , "๐Ÿ˜"]
    @State var selectedEmoji: String = ""
    
    @State private var text = "Your Feedback"
    
    var body: some View {
        ZStack {
            Color("Color1")
                .ignoresSafeArea()
            VStack {
                HStack {
                    Text("Cairocoders")
                    Spacer()
                    Text("Your feedback")
                        .font(.title)
                }.padding(.bottom, 10)
                
                Divider()
                
                VStack {
                    Text("We would like your feedback to imporve our service")
                        .font(.system(size: 19))
                        .foregroundColor(Color.blue)
                        .multilineTextAlignment(.center)
                    Text("it will help us to serve you better")
                        .font(.system(size: 16))
                        .foregroundColor(Color.blue)
                        .padding(.top,5)
                    
                    HStack(alignment: .center, spacing: 10){
                        ForEach(reactions , id: \.self) { reaction in
                            ZStack {
                                emojiView(selectedEmoji: $selectedEmoji, emojiText: reaction)
                                    .frame(width: 50 , height: 50)
                                    .background(Color.white)
                                    .overlay(
                                            Circle()
                                                .stroke(Color.init(red: 236/255, green: 61/255, blue: 107/255), lineWidth: selectedEmoji == reaction ? 6 : 0)
                                    )
                                    .cornerRadius(25)
                                    .shadow(color: Color.init(red: 0, green: 0, blue: 0, opacity: 0.1) , radius: 10 , x: 0 , y: 15)
                                    .opacity(selectedEmoji == "" || selectedEmoji == reaction ? 1 : 0.5)
                                if selectedEmoji == reaction {
                                    Image("check")
                                        .resizable()
                                        .frame(width: 20, height: 20)
                                        .offset(x: 18, y: -18)
                                }
                            }
                        }
                    } //: HSTACK
                    .padding(.top , 20)
                    .padding(.bottom, 20)
                    
                    if selectedEmoji != "" {
                        Text(getResponseWithEmoji(selectedEmoji))
                            .font(.system(size: 17))
                            .foregroundColor(Color.pink)
                    }
                } //end VStack
                
                Text("Please leave your feedback below:")
                
                Divider()
                
                VStack {
                   TextEditor(text: $text)
                     .frame(width: 300, height: 120)
                     .lineSpacing(10)
                     .autocapitalization(.words)
                     .disableAutocorrection(true)
                     .padding()
                                        
                }.overlay(
                         RoundedRectangle(cornerRadius: 25)
                           .stroke(Color.yellow, lineWidth: 5)
                         )
                .padding()
  
                
                Button {
                    
                } label: {
                    ZStack {
                        Capsule()
                            .fill(Color.pink)
                            .frame(width: 160, height: 45)
                        
                        Text("Send feedBack")
                            .font(.system(size: 20))
                            .foregroundColor(Color.white)
                        
                    }
                    .opacity(selectedEmoji == "" ? 0.7 : 1)
                }
            }
            .padding(30)
            .frame(width: 370)
            .background(Color.white)
            .cornerRadius(20)
            .shadow(color: Color.init(red: 0, green: 0, blue: 0, opacity: 0.1) , radius: 20 , x: 10 , y: 20)
        }
    }
    
    func getResponseWithEmoji(_ emoji: String) -> String {
        switch selectedEmoji {
        case "๐Ÿ˜ฃ":
            return "Not satisfied"
        case "☹️":
            return "Somewhat satisfied"
        case "๐Ÿ˜":
            return "Satisfied"
        case "๐Ÿ™‚":
            return "Very satisfied"
        case "๐Ÿ˜":
            return "Satisfied enought to tell others"
        default:
            return ""
        }
    }
}

struct feedback_Previews: PreviewProvider {
    
    static var previews: some View {
        feedback()
    }
}
emojiView.swift
 
//
//  emojiView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct emojiView: View {
    @Binding var selectedEmoji: String
    var emojiText: String
    
    var body: some View {
        Button {
            withAnimation(.spring(response:0.3, dampingFraction: 0.5)){
                selectedEmoji = emojiText
            }
            
        } label:{
            Text(emojiText)
                .font(Font.custom("Avenir", size: 23))
        }
    }
}

Tuesday, March 1, 2022

SwiftUI Tinder Card Swipe

SwiftUI Tinder Card Swipe

ContentView.swift
//
//  ContentView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct ContentView: View {
    
    @State var user: Userlist?
    
    var body: some View {
        VStack {
            ZStack {
                ForEach(users) { rs in
                    CardView(viewdata: rs)
                }
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
CardView.swift
//
//  CardView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct CardView: View {
    
    @State private var offset = CGSize.zero
    @State private var color: Color = .black

    @State var viewdata: Userlist
    
    var body: some View {
        ZStack {
            Rectangle()
                .frame(width: 325, height: 425)
                .border(.white, width: 6.0)
                .cornerRadius(4)
                .foregroundColor(color.opacity(0.9))
                .shadow(radius: 4)
            HStack {
                Image(viewdata.photo).resizable()
                    .frame(width: 320, height: 420)
            }
            
        }
        .offset(x: offset.width * 1, y: offset.height * 0.4)
        .rotationEffect(.degrees(Double(offset.width / 40)))
        .gesture(
            DragGesture()
                .onChanged { gesture in
                    offset = gesture.translation
                    withAnimation {
                        changeColor(width: offset.width)
                    }
                }
                .onEnded { _ in
                    withAnimation {
                        swipeCard(width: offset.width)
                        changeColor(width: offset.width)
                    }
                }
        )
    }
    
    func swipeCard(width: CGFloat) {
        switch width {
        case -500...(-150):
            print("\(viewdata.username) removed")
            offset = CGSize(width: -500, height: 0)
        case 150...500:
            print("\(viewdata.username) added")
            offset = CGSize(width: 500, height: 0)
        default:
            offset = .zero
        }
    }
    
    func changeColor(width: CGFloat) {
        switch width {
        case -500...(-130):
            color = .red
        case 130...500:
            color = .green
        default:
            color = .black
        }
    }
    
    
}

struct CardView_Previews: PreviewProvider {
    static var previews: some View {
        CardView(viewdata: Userlist(id: 1, username: "Cairocoders", photo: "1"))
    }
}
Model.swift
//
//  Model.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct Userlist: Identifiable {
 
    let id: Int
    let username: String
    let photo : String
}
 
var users = [
    Userlist(id: 0, username: "Olivia", photo: "1"),
    Userlist(id: 1, username: "Emma", photo: "2"),
    Userlist(id: 2, username: "Ava", photo: "3"),
    Userlist(id: 3, username: "Charlotte", photo: "4"),
    Userlist(id: 4, username: "Sophia", photo: "5"),
    Userlist(id: 5, username: "Amelia", photo: "6")
]

Monday, February 28, 2022

SwiftUI MVVM (Model View View Model) design pattern

SwiftUI MVVM (Model View View Model) design pattern with ObservableObject — @Published — @ObservedObject fetch JSON data

ContentView.swift
 
//
//  ContentView.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct ContentView: View {
    
   @ObservedObject var viewModel = ContentViewModel()
    
   var body: some View {
      NavigationView {
          VStack {
              List(viewModel.items, id: \.id) { item in
                   VStack(alignment: .leading) {
                      Text(item.title)
                      Text(item.completed.description)
                        .font(.system(size: 11))
                        .foregroundColor(.gray)
                   }
              }
              .listStyle(GroupedListStyle())
              
          }.onAppear(perform: {
             viewModel.fetchData()
          })
        .navigationBarTitle("Fetch JSON Datas")
      }
   }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
ViewModel.swift
 
//
//  ViewModel.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

class ContentViewModel: ObservableObject {
  @Published var items = [Model]()
  func fetchData() {
    let api = "https://jsonplaceholder.typicode.com/todos"
    guard let url = URL(string: api) else { return }
    URLSession.shared.dataTask(with: url) { (data, response, error) in
      do {
         if let data = data {
           let result = try JSONDecoder().decode([Model].self, from: data)
           DispatchQueue.main.async {
              self.items = result
           }
         } else {
           print("No data")
         }
      } catch (let error) {
         print(error.localizedDescription)
      }
     }.resume()
  }
}
Model.swift
 
//
//  Model.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//

import SwiftUI

struct Model: Decodable {
   let id: Int
   let userId: Int
   let title: String
   let completed: Bool
}

Related Post