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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//
//  UserRow.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//
 
import SwiftUI
 
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//
//  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)
     
  
    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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//
//  ContentView.swift
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//
//  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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//
//  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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//
//  ViewModel.swift
//  SwiftUIProject
//
//  Created by Cairocoders
//
 
import SwiftUI
 
class ContentViewModel: ObservableObject {
  @Published var items = [Model]()
  func fetchData() {
    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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//
//  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