I am new to SwiftUI and trying doing wrap-up challenge.
I do not know why when I pass a list as Environment Object, change the element's attribute of the list to update the UI of the button, the button cannot display the change despite its attribute has been changed successfully.
Here is my project structure: https://i.stack.imgur.com/7KEaK.png
Main file: BookLibraryAppApp
import SwiftUI
#main
struct BookLibraryAppApp: App {
var body: some Scene {
WindowGroup {
BookListView()
.environmentObject(BookModel())
}
}
}
Model file: Book
import Foundation
class Book : Decodable, Identifiable {
var id = 1
var title = "Title"
var author = "Author"
var content = ["I am a test book."]
var isFavourite = false
var rating = 2
var currentPage = 0
}
On view model file: BookModel
import Foundation
class BookModel : ObservableObject {
#Published var books = [Book]()
init() {
books = getLoadData()
}
func getLoadData() -> [Book] {
let fileName = "Data"
let fileExtension = "json"
var books = [Book]()
// Get link to data file
let url = Bundle.main.url(forResource: fileName, withExtension: fileExtension)
guard url != nil else {
print("Could not retrieve category data: \(fileName).\(fileExtension) not found.")
return books
}
do {
// Decode the data and return it
let data = try Data(contentsOf: url!)
books = try JSONDecoder().decode([Book].self, from: data)
return books
} catch {
print("Error retrieving category data: \(error.localizedDescription)")
}
return books
}
func updateFavourite(forId: Int) {
if let index = books.firstIndex(where: { $0.id == forId }) {
books[index].isFavourite.toggle()
}
}
func getBookFavourite(forId: Int) -> Bool {
if let index = books.firstIndex(where: { $0.id == forId }) {
return books[index].isFavourite
}
return false
}
}
On View folder:
BookListView
import SwiftUI
struct BookListView: View {
#EnvironmentObject var model : BookModel
var body: some View {
NavigationView {
ScrollView {
LazyVStack(spacing: 50) {
ForEach(model.books, id: \.id) {
book in
NavigationLink {
BookPreviewView(book: book)
} label: {
BookCardView(book: book)
}
}
}
.padding(25)
}
.navigationTitle("My Library")
}
}
}
struct BookListView_Previews: PreviewProvider {
static var previews: some View {
BookListView()
.environmentObject(BookModel())
}
}
On smaller view: BookCardView
import SwiftUI
struct BookCardView: View {
var book : Book
var body: some View {
ZStack {
// MARK: container
Rectangle()
.cornerRadius(20)
.foregroundColor(.white)
.shadow(color: Color(.sRGB, red: 0, green: 0, blue: 0, opacity: 0.5), radius: 10, x: -5, y: 5)
// MARK: card book content
VStack (alignment: .leading, spacing: 9) {
// MARK: title and favourite
HStack {
Text(book.title)
.font(.title2)
.bold()
Spacer()
if (book.isFavourite) {
// Image(systemName: "star.fill")
// .resizable()
// .aspectRatio(contentMode: .fit)
// .frame(width: 30)
// .foregroundColor(.yellow)
}
}
// MARK: author
Text(book.author)
.font(.subheadline)
.italic()
// MARK: image
Image("cover\(book.id)")
.resizable()
.aspectRatio(contentMode: .fit)
}
.padding(.horizontal, 35)
.padding(.vertical, 30)
}
}
}
struct BookCardView_Previews: PreviewProvider {
static var previews: some View {
BookCardView(book: BookModel().books[0])
}
}
On smaller view: BookPreviewView
import SwiftUI
struct BookPreviewView: View {
#EnvironmentObject var model : BookModel
// #State var select =
var book : Book
var body: some View {
VStack {
// MARK: banner
Text("Read Now!")
Spacer()
// MARK: cover image
Image("cover\(book.id)")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 250)
Spacer()
// MARK: bookmark
Text("Mark for later")
Button {
// destination
self.model.updateFavourite(forId: book.id)
// self.select.toggle()
print(model.getBookFavourite(forId: book.id))
} label: {
// label
Image(systemName: self.book.isFavourite ? "star.fill" : "star")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 28)
.foregroundColor(.yellow)
}
Spacer()
// MARK: rating
Text("Rate Amazing Words")
Spacer()
}
}
}
struct BookPreviewView_Previews: PreviewProvider {
static var previews: some View {
BookPreviewView(book: BookModel().books[0])
}
}
Here is Data.json file:
[
{
"title": "Amazing Words",
"author": "Sir Prise Party",
"isFavourite": true,
"currentPage": 0,
"rating": 2,
"id": 1,
"content": [
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla eu congue lacus",
"ac laoreet felis. Integer eros tortor, blandit id magna non, pharetra sodalesurna."
]},
{
"title": "Text and More",
"author": "Sir Vey Sample",
"isFavourite": false,
"currentPage": 0,
"rating": 2,
"id": 3,
"content": [
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla eu congue lacus",
"ac laoreet felis. Integer eros tortor, blandit id magna non, pharetra sodalesurna.
]}
]
When I try to click the Yellow start on the BookPreviewView, instead of changing from "star" to "star.fill", it shows nothing. May I ask what's wrong with my code?
Thank you very much!
This is a pretty simple error. The #Published property wrapper will send a notification to the view to update itself as soon as its value changes.
But in your case this never happens. You defined Book as a class (reference type), so changing one of its property doesn´t force the array (valuetype) to change, so #Published doesn´t pick up the change.
Two solutions here:
If you insist on keeping the class use:
func updateFavourite(forId: Int) {
if let index = books.firstIndex(where: { $0.id == forId }) {
objectWillChange.send() // add this
books[index].isFavourite.toggle()
}
}
this will send the notification by hand.
the prefered solution should be to make your model Book a struct and it will pick up the changes.
Related
My AudioPlayerView(audioSelected:, audioTitle:)
is not being updated when the func checkIndex() is being triggered.
I'm trying unsuccessfully to update the audio file when using the checkIndex() function.
Not finding a way to update the AudioPlayerView() accordingly. Everything else works and gets updated on each view.
Get a warning Result of 'AudioPlayerView' initializer is unused
Please have a look at my code. Much appreciate any help. I'm new to swift. I do have a zip of project on GitHub if need more information. Thank you.
AudioPlayerView(audioSelected: vm.animals[animalID -
1].audio, audioTitle: "")
Button {
checkIndex()
} label: {
HStack {
Image(systemName:
"arrowshape.turn.up.right")
if animalID == 5 {
Text("Go to \
(vm.animals[0].animalName)")
} else {
Text("Go to \
(vm.animals[animalID].animalName)")
}
}//HSTACK
}//LABEL
// MARK: - Function
func checkIndex() {
let numAnimals = vm.animals.count
if animalID == numAnimals {
animalID = 1
} else {
animalID += 1
}//ELSE
//if AudioPlayerView(audioSelected: "", audioTitle:
"").self == 1 {
//vm.animals[animalID - 1].audio += 1
//}
//if animalID == 1 {
//AudioPlayerView(audioSelected: vm.animals[animalID -
//2].audio, audioTitle: "")
//}
}//INDEX
//full code
import SwiftUI
import Foundation
struct DetailView: View {
// MARK: - Properties
#State var animalID: DataModel.ID
#ObservedObject var vm: DetailViewModel
// MARK: - Body
var body: some View {
ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .center, spacing: 10){
// View will be updated when #State changes
// In here, the view is just a Text but you can put more complex
views...
Image(vm.animals[animalID - 1].image)
.resizable()
.scaledToFit()
.cornerRadius(0)
Text(vm.animals[animalID - 1].animalName)
.font(.largeTitle)
.padding()
//AUDIO
AudioPlayerView(audioSelected: vm.animals[animalID -
1].audio, audioTitle: "")
//END AUDIO
Text(verbatim: vm.animals[animalID -
1].description)
.multilineTextAlignment(.leading)
.layoutPriority(1)
.padding()
Spacer()
Button {
checkIndex()
} label: {
HStack {
Image(systemName:
"arrowshape.turn.up.right")
if animalID == 5 {
Text("Go to \
(vm.animals[0].animalName)")
} else {
Text("Go to \
(vm.animals[animalID].animalName)")
}
}//HSTACK
}//LABEL
.padding(.all, 10)
.background(Color(.lightGray))
.clipShape(RoundedRectangle(cornerRadius: 20))
//Spacer()
}//VStack
}//SCROLL
}//VIEW
//}//END VIEW
// MARK: - Function
func checkIndex() {
let numAnimals = vm.animals.count
if animalID == numAnimals {
animalID = 1
} else {
animalID += 1
}//ELSE
//This is what I've tried but did not work
//if AudioPlayerView(audioSelected: "", audioTitle:
//"").self == 1 {
//vm.animals[animalID - 1].audio += 1
//}
Warning;Result of 'AudioPlayerView' initializer is unused
if animalID == 1 {
AudioPlayerView(audioSelected: vm.animals[animalID -
2].audio, audioTitle: "")
}
}//INDEX
}//ENDVIEW
struct AudioPlayerView: View {
//var AudioPLayer: String
var audioSelected: String
var audioTitle: String
//var audioPlayer: AVAudioPlayer?
#State var audioPlayer: AVAudioPlayer!
var body: some View {
VStack {
HStack {
Spacer()
Button(action: {
self.audioPlayer.play()
}) {
Image(systemName:
"play.circle.fill").resizable()
.frame(width: 50, height: 50)
.aspectRatio(contentMode: .fit)
.accentColor(/
*#START_MENU_TOKEN#*/.black/*#END_MENU_TOKEN#*/)
}
Spacer()
Button(action: {
self.audioPlayer.pause()
}) {
Image(systemName:
"pause.circle.fill").resizable()
.frame(width: 50, height: 50)
.aspectRatio(contentMode: .fit)
.accentColor(/
*#START_MENU_TOKEN#*/.black/*#END_MENU_TOKEN#*/)
}
Spacer()
}//HStack
}//VStack
.onAppear {
let url = Bundle.main.path(forResource: audioSelected,
ofType: "mp3")
self.audioPlayer = try! AVAudioPlayer(contentsOf:
URL(fileURLWithPath: url!))
}
}
//models
import SwiftUI
class DetailViewModel: ObservableObject {
#Published var animals: [DataModel] =
Bundle.main.decode("animals.json")
}
import Foundation
struct Audio: Codable, Identifiable {
let id: String
let name: String
let headline: String
//Computed Property
var thumbnail: String {
"audio-\(id)"
}
}
Intro
Hi there. I recently asked a different question asking how to implement user-triggered FCMs. I quickly realised that in order to implement FCMs I needed to add another feature to my app, which is why I am here now. I'm trying, trust me.
My question / problem
In the picture I attached, you can see the text that is supposed to show when the user clicks on the friends tab. But it doesn't do that. It does when I refresh the list, because I put the function in the refreshable attribute. I did also put the function in the view initialisation, but it doesn't do what I want it to do. So my question would be, a) why it doesn't load? and b) what would be an approach to solve my problem?
Code reference
This function is called in the init{} of the view and .refreshable{} of the list inside the view. (I also tried adding it via .onAppear{} in the NavigationLink of the parent view.)
#State var bestfriend: String = ""
func getBestie() {
let db = Firestore.firestore()
let docRef = db.collection("users").document(email)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let bestie = document.get("bestie") as? String ?? "error: bestie"
bestfriend = String(bestie)
} else {
print("Document does not exist")
}
}
}
Image for reference
Thanks and annotation
Thank you very much in advance, I'm amazed every day by how amazing this community is and how so many people are willing to help. If you need me to add anything else, of course I will do that. I hope I'll be wise enough one day to help other people with their problems as well.
Edit
The view
import Firebase
import FirebaseAuth
import SDWebImage
import SDWebImageSwiftUI
import SwiftUI
struct View_Friend_Tab: View {
#ObservedObject var friends_model = Model_User()
#State var friendWho = ""
init() {
friends_model.getFriendlist()
//friends_model.getBestie()
getBestie()
}
//VARS
let gifurl = URL(string: "https://c.tenor.com/BTCEb08QgBgAAAAC/osita-iheme-aki-and-pawpaw.gif")
let avatarURL = URL(
string:
"https://firebasestorage.googleapis.com/v0/b/universerp-72af2.appspot.com/o/avatars%2Fanime-girl-white-hair-1-cropped.jpg?alt=media&token=efba4215-850d-41c8-8c90-385f7a572e94"
)
#State var showingAlert = false
#State var showFriendRequests = false
#State var bestfriend: String = ""
func getBestie() {
let db = Firestore.firestore()
let docRef = db.collection("users").document(email)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let bestie = document.get("bestie") as? String ?? "error: bestie"
bestfriend = String(bestie)
} else {
print("Document does not exist")
}
}
}
var body: some View {
if !showFriendRequests {
VStack {
NavigationView {
/*
List (friends_model.friend_list) { item in
HStack {
Text(item.email)
Spacer()
}
}
.refreshable{
friends_model.getFriendlist()
}
.listStyle(.grouped)
*/
List {
Section(header: Text("management")) {
NavigationLink(destination: View_Friend_Best_Tab()) {
Label("Select your bestie", systemImage: "star.fill")
}
NavigationLink(destination: View_Friend_Requests_Tab()) {
Label("Manage friend requests", systemImage: "person.fill.questionmark")
}
NavigationLink(destination: View_Friend_Add_Tab()) {
Label("Add friend", systemImage: "person.fill.badge.plus")
}
}
ForEach(friends_model.friend_list) { item in
let avURL = URL(string: item.avatarURL)
Section(header: Text(item.username)) {
HStack {
VStack(alignment: .leading) {
if bestfriend == item.email {
Text("Is your Shin'yū")
.foregroundColor(Color("lightRed"))
.fontWeight(.bold)
.font(.footnote)
}
Text(item.username)
.fontWeight(.bold)
.frame(alignment: .leading)
Text(item.email)
.font(.footnote)
.multilineTextAlignment(.leading)
}
Spacer()
WebImage(url: avURL)
.resizable(resizingMode: .stretch)
.aspectRatio(contentMode: .fit)
.frame(width: 50, height: 50)
.clipShape(Circle())
.shadow(radius: 5)
.overlay(Circle().stroke(Color.black, lineWidth: 1))
}
Button("Remove", role: .destructive) {
showingAlert = true
}
.alert("Do you really want to remove this friend?", isPresented: $showingAlert) {
HStack {
Button("Cancel", role: .cancel) {}
Button("Remove", role: .destructive) {
friendWho = item.email
removeFriend()
withAnimation {
friends_model.getFriendlist()
}
}
}
}
}
}
}
.navigationTitle("Your Friends")
.navigationViewStyle(.automatic)
.refreshable {
friends_model.getFriendlist()
getBestie()
}
.listStyle(.insetGrouped)
Spacer()
}
}
} else {
View_Friend_Requests_Tab()
}
}
}
struct View_Friend_Tab_Previews: PreviewProvider {
static var previews: some View {
View_Friend_Tab()
}
}
As previously stated, the function is being called in the init block and when the list is refreshed.
As a general recommendation, use view models to keep your view code clean.
In SwiftUI, a view's initialiser should not perform any expensive / long-running computations. Keep in mind that in SwiftUI, a view is only a description of your UI, not the UI itself. Any state management should be handled outside of the initialiser.
In your case, use .onAppear or .task:
import Firebase
import FirebaseAuth
import SDWebImage
import SDWebImageSwiftUI
import SwiftUI
class FriendsViewModel: ObservableObject {
#Published var bestfriend: String = ""
func getBestie() {
let db = Firestore.firestore()
let docRef = db.collection("users").document(email)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
// consider using Codable for mapping - see https://peterfriese.dev/posts/firestore-codable-the-comprehensive-guide/
let bestie = document.get("bestie") as? String ?? "error: bestie"
self.bestfriend = String(bestie)
} else {
print("Document does not exist")
}
}
}
// add other functions and properties here.
}
struct FriendsView: View {
#ObservedObject var viewModel = FriendsViewModel()
//VARS
let gifurl = URL(string: "https://c.tenor.com/BTCEb08QgBgAAAAC/osita-iheme-aki-and-pawpaw.gif")
let avatarURL = URL(
string:
"https://firebasestorage.googleapis.com/v0/b/universerp-72af2.appspot.com/o/avatars%2Fanime-girl-white-hair-1-cropped.jpg?alt=media&token=efba4215-850d-41c8-8c90-385f7a572e94"
)
#State var showingAlert = false
#State var showFriendRequests = false
#State var bestfriend: String = ""
var body: some View {
if !showFriendRequests {
VStack {
NavigationView {
List {
Section(header: Text("management")) {
NavigationLink(destination: SelectBestieView()) {
Label("Select your bestie", systemImage: "star.fill")
}
NavigationLink(destination: ManageFriendRequestsView()) {
Label("Manage friend requests", systemImage: "person.fill.questionmark")
}
NavigationLink(destination: AddFriendsView()) {
Label("Add friend", systemImage: "person.fill.badge.plus")
}
}
ForEach(viewModel.friends) { friend in
let avURL = URL(string: friend.avatarURL)
Section(header: Text(friend.username)) {
HStack {
VStack(alignment: .leading) {
if bestfriend == friend.email {
Text("Is your Shin'yū")
.foregroundColor(Color("lightRed"))
.fontWeight(.bold)
.font(.footnote)
}
Text(friend.username)
.fontWeight(.bold)
.frame(alignment: .leading)
Text(friend.email)
.font(.footnote)
.multilineTextAlignment(.leading)
}
Spacer()
WebImage(url: avURL)
.resizable(resizingMode: .stretch)
.aspectRatio(contentMode: .fit)
.frame(width: 50, height: 50)
.clipShape(Circle())
.shadow(radius: 5)
.overlay(Circle().stroke(Color.black, lineWidth: 1))
}
Button("Remove", role: .destructive) {
showingAlert = true
}
.alert("Do you really want to remove this friend?", isPresented: $showingAlert) {
HStack {
Button("Cancel", role: .cancel) {}
Button("Remove", role: .destructive) {
friendWho = friend.email
viewModel.removeFriend()
withAnimation {
viewModel.getFriendlist()
}
}
}
}
}
}
}
.navigationTitle("Your Friends")
.navigationViewStyle(.automatic)
.onAppear {
viewModel.getFriendlist()
viewModelgetBestie()
}
.refreshable {
viewModel.getFriendlist()
viewModelgetBestie()
}
.listStyle(.insetGrouped)
Spacer()
}
}
} else {
FriendRequestsView()
}
}
}
struct FriendsViewPreviews: PreviewProvider {
static var previews: some View {
FriendsView()
}
}
It's very hard to explain without a recording from a second device that I don't have, but when I try to slide my slider, it will stop when my finger is definitely still moving.
I have my code posted below. I'd be happy to answer any questions and explain whatever. I'm sure it's something really simple that I should know. Any help would be very much appreciated, thanks!
import SwiftUI
class SettingsViewModel: ObservableObject {
#Published var selectedTips = [
10.0,
15.0,
18.0,
20.0,
25.0
]
func addTip() {
selectedTips.append(0.0)
selectedTips.sort()
}
func removeTip(index: Int) {
selectedTips.remove(at: index)
selectedTips = selectedTips.compactMap{ $0 }
}
}
struct SettingsTipsView: View {
#StateObject var model = SettingsViewModel()
var body: some View {
List {
HStack {
Text("Edit Suggested Tips")
.font(.title2)
.fontWeight(.semibold)
Spacer()
if(model.selectedTips.count < 5) {
Button(action: { model.addTip() }, label: {
Image(systemName: "plus.circle.fill")
.renderingMode(.original)
.font(.title3)
.padding(.horizontal, 10)
})
.buttonStyle(BorderlessButtonStyle())
}
}
ForEach(model.selectedTips, id: \.self) { tip in
let i = model.selectedTips.firstIndex(of: tip)!
//If I don't have this debug line here then the LAST slider in the list tries to force the value to 1 constantly, even if I remove the last one, the new last slider does the same. It's from a separate file but it's pretty much the same as the array above. An explanation would be great.
Text("\(CalculatorViewModel.suggestedTips[i])")
HStack {
Text("\(tip, specifier: "%.0f")%")
Slider(value: $model.selectedTips[i], in: 1...99, label: { Text("Label") })
if(model.selectedTips.count > 1) {
Button(action: { model.removeTip(index: i) }, label: {
Image(systemName: "minus.circle.fill")
.renderingMode(.original)
.font(.title3)
.padding(.horizontal, 10)
})
.buttonStyle(BorderlessButtonStyle())
}
}
}
}
}
}
Using id: \.self within a List or ForEach is a dangerous idea in SwiftUI. The system uses it to identify what it expects to be unique elements. But, as soon as you move the slider, you have a change of ending up with a tip value that is equal to another value in the list. Then, SwiftUI gets confused about which element is which.
To fix this, you can use items with truly unique IDs. You should also try to avoid using indexes to refer to certain items in the list. I've used list bindings to avoid that issue.
struct Tip : Identifiable {
var id = UUID()
var tip : Double
}
class SettingsViewModel: ObservableObject {
#Published var selectedTips : [Tip] = [
.init(tip:10.0),
.init(tip:15.0),
.init(tip:18.0),
.init(tip:20.0),
.init(tip:25.0)
]
func addTip() {
selectedTips.append(.init(tip:0.0))
selectedTips = selectedTips.sorted(by: { a, b in
a.tip < b.tip
})
}
func removeTip(id: UUID) {
selectedTips = selectedTips.filter { $0.id != id }
}
}
struct SettingsTipsView: View {
#StateObject var model = SettingsViewModel()
var body: some View {
List {
HStack {
Text("Edit Suggested Tips")
.font(.title2)
.fontWeight(.semibold)
Spacer()
if(model.selectedTips.count < 5) {
Button(action: { model.addTip() }, label: {
Image(systemName: "plus.circle.fill")
.renderingMode(.original)
.font(.title3)
.padding(.horizontal, 10)
})
.buttonStyle(BorderlessButtonStyle())
}
}
ForEach($model.selectedTips, id: \.id) { $tip in
HStack {
Text("\(tip.tip, specifier: "%.0f")%")
.frame(width: 50) //Otherwise, the width changes while moving the slider. You could get fancier and try to use alignment guides for a more robust solution
Slider(value: $tip.tip, in: 1...99, label: { Text("Label") })
if(model.selectedTips.count > 1) {
Button(action: { model.removeTip(id: tip.id) }, label: {
Image(systemName: "minus.circle.fill")
.renderingMode(.original)
.font(.title3)
.padding(.horizontal, 10)
})
.buttonStyle(BorderlessButtonStyle())
}
}
}
}
}
}
I'm using SwiftUI and having some problem passing my string array to a view.
Let's me explain the situation. I'm working on a Gallery app to show some artist's paintings.
I create a TabView to show all the paintings from each artist without any problem but I wanted to make each paintings clickable to see the detail view of the painting, and this where I get stuck.
Every time I click on a paintings it's show me the same paintings...
here is the samples code:
Model & View Model
let artistData: [Artist] = [
Artist(
name: "Piotre",
profilePic: "Piotre",
biography: "Piotre, est un...",
worksImages: [
"GO LOVE YOUR SELF 115.5-89",
"GRAFFITI THERAPIE 146-226",
"HELLO MY NAME IS 130-162",
"KING'S GARDEN 162-130",
"LION'S GARDEN 100-100",
"R'S GARDEN 162 130"
],
workName: [
"GO LOVE YOUR SELF",
"GRAFFITI THERAPIE",
"HELLO MY NAME IS",
"KING'S GARDEN",
"LION'S GARDEN",
"R'S GARDEN"
], workSize: [
"115.5-89",
"146-226",
"130-162",
"162-130",
"100-100",
"162 130"
]),
]
struct Artist: Identifiable {
var id = UUID()
var name: String
var profilePic: String
var biography: String
var worksImages: [String]
var workName: [String]
var workSize: [String]
}
struct ArtistGalleryView: View {
//MARK:- PROPERTIES
var work: Artist
//MARK:- BODY
var body: some View {
ZStack {
Color(#colorLiteral(red: 0.6549019608, green: 0.7137254902, blue: 0.862745098, alpha: 1)).opacity(0.2)
.edgesIgnoringSafeArea(.all)
VStack {
//MARK:- Tableaux
TabView {
ForEach(work.worksImages, id: \.self) { works in
Image(works)
.resizable()
.scaledToFit()
}
.padding()
}
.tabViewStyle(PageTabViewStyle())
}
}
.frame(width: 330, height: 400, alignment: .center)
.cornerRadius(10)
}
}
struct GalleryView: View {
//MARK:- PROPERTIES
var artist: [Artist] = artistData
init() {
UINavigationBar.appearance().titleTextAttributes = [.font: UIFont(name: "WorkSans-Bold", size: 20)!]
}
//MARK:- BODY
var body: some View {
NavigationView {
ScrollView(showsIndicators: false) {
VStack {
ForEach(artist) { item in
VStack(alignment: .leading) {
ArtistName(artist: item)
NavigationLink(destination: WorksDetailView(work: item)) {
ArtistGalleryView(work: item)
}
}
.buttonStyle(PlainButtonStyle())
}
.padding()
}
}
.navigationBarItems(trailing:Button(action: {
}) {
}
)
.navigationBarTitle(
Text("Gallery"), displayMode: .inline)
}
}
}
And my detail view where I want to see the corresponding image
struct WorksDetailView: View {
#State private var moveUp = false
var work: Artist
var body: some View {
ZStack {
VStack(alignment: .leading) {
Text(work.workName[0])
.modifier(CustomFontModifier(size: 22, name: "WorkSans-Bold"))
.padding(.horizontal)
Text(work.workSize[0])
.modifier(CustomFontModifier(size: 17, name: "WorkSans-Light"))
.foregroundColor(.secondary)
.padding(.horizontal)
VStack {
Image(work.worksImages[0])
.resizable()
.scaledToFit()
}
.padding()
}
.padding(.horizontal)
.padding(.bottom, 150)
Button(action: {
print("Show AR")
}) {
NeumorphicButton(moveUp: $moveUp)
}
.onAppear(perform: {
withAnimation(.easeInOut(duration: 1)) {
moveUp.toggle()
}
})
.offset(y: moveUp ? 225 : 450)
}
}
}
This is when I launch the app and it looks okThis is when I change some data from firebase, as you can see some data is not arranged in the right way and some rectangles are doubleI'm currently working on this project where I need do read data from Firebase and update my views from the changes. Every time my data changes on firebase my views bugs in strange ways.
This is my Student class
struct Student : Identifiable {
var id : String
var name : String
var surname : String
var isAbsent : Bool
var isBathroom : Bool
}
Here where I get my data from firebase and publish to get all other views the data
class getStudents: ObservableObject {
#Published var datas = [Student]()
init() {
let db = Firestore.firestore()
let classRef = db.collection("5SA")
classRef.order(by: "surname").addSnapshotListener {(snap,error) in
if error != nil{
print((error?.localizedDescription)!)
return
}
for i in snap!.documentChanges{
let id = i.document.documentID
let name = i.document.get("name")as! String
let surname = i.document.get("surname") as! String
let isAbsent = i.document.get("absent") as! Bool
let isBathroom = i.document.get("bathroom") as! Bool
self.datas.append(Student(id: id, name: name, surname: surname, isAbsent: isAbsent, isBathroom: isBathroom))
if i.type == .modified{
let isAbsent = i.document.get("absent") as! Bool
let isBathroom = i.document.get("bathroom") as! Bool
for j in 0..<self.datas.count{
if self.datas[j].id == id{
self.datas[j].isAbsent = isAbsent
self.datas[j].isBathroom = isBathroom
}
}
}
}
}
}
}
This are my views. Apart from the landscape on the iPad simulator the bugs every time.
What I'm trying to achieve is creating like a class app. Where there are students absent, in the toilet or present. I want to to show every student with a rectangle with his name on it and to change color of the rectangles every time the bools on my fire base changes(isAbsent,isBathroom). But when I try to change value on firebase the rectangles multiplies and do strange thinks.
struct ContentView: View {
#ObservedObject var students = getStudents()
var body: some View {
NavigationView{
VStack{
ScrollView{
Text("Menu")
Menu().background(Color.white)
Text("Future verifiche")
HStack{
ScrollView(.horizontal, showsIndicators: false){
RoundedRectangle(cornerRadius: 20).frame(width:300,height: 300).foregroundColor(.white).overlay(Text("V. Matematica"))
}
}
Text("Oggi")
HStack{
Spacer()
Text("Assenti ")
Spacer()
Text("In bagno")
Spacer()
Text("Presenti")
Spacer()
}
HStack{
Spacer()
ScrollView{
ForEach(students.datas,id: \.id){student in
VStack(spacing: 15){
if student.isAbsent{
RoundedRectangle(cornerRadius: 20).frame(width:200,height: 100).foregroundColor(self.getColor(isAbsent: student.isAbsent, isBathroom: student.isBathroom)).overlay(Text(student.name))
}
}
}
}
Spacer()
ScrollView{
ForEach(students.datas,id: \.id){student in
VStack(spacing: 15){
if student.isBathroom{
RoundedRectangle(cornerRadius: 20).frame(width:200,height: 100).foregroundColor(self.getColor(isAbsent: student.isAbsent, isBathroom: student.isBathroom)).overlay(Text(student.name))
}
}
}
}
Spacer()
ScrollView{
ForEach(students.datas,id: \.id){student in
VStack(spacing: 15){
if !student.isAbsent && !student.isBathroom{
RoundedRectangle(cornerRadius: 20).frame(width:200,height: 100).foregroundColor(self.getColor(isAbsent: student.isAbsent, isBathroom: student.isBathroom)).overlay(Text(student.name))
}
}
}
}
Spacer()
}
}
}.navigationBarTitle("Classe 5SA",displayMode: .inline)
}.navigationViewStyle(StackNavigationViewStyle())
}
func getColor(isAbsent: Bool, isBathroom : Bool) -> Color {
if isAbsent{
return Color.red
}
if isBathroom{
return Color.blue
}
return Color.green
}
}
struct Menu : View {
var body : some View{
HStack{
NavigationLink(destination: Studenti()){
RoundedRectangle(cornerRadius: 30).frame(width: 150,height: 150).foregroundColor(.red).opacity(0.5).overlay(Text("Studenti").foregroundColor(.black))
}
NavigationLink(destination: Text("Overview")){
RoundedRectangle(cornerRadius: 30).frame(width: 150,height: 150).foregroundColor(.green).opacity(0.5).overlay(Text("Overview").foregroundColor(.black))
}
NavigationLink(destination: Text("Orario")){
RoundedRectangle(cornerRadius: 30).frame(width: 150,height: 150).foregroundColor(.blue).opacity(0.5).overlay(Text("Orario").foregroundColor(.black))
}
NavigationLink(destination: Text("Calendario")){
RoundedRectangle(cornerRadius: 30).frame(width: 150,height: 150).foregroundColor(.orange).opacity(0.5).overlay(Text("Calendario").foregroundColor(.black))
}
}
}
}
struct Studenti : View {
#ObservedObject var students = getStudents()
var body : some View {
ForEach(students.datas,id: \.id){student in
VStack{
Rectangle().frame(width:300,height: 150).foregroundColor(self.getColor(isAbsent: student.isAbsent, isBathroom: student.isBathroom)).overlay(Text(student.name))
}
}
}
func getColor(isAbsent: Bool, isBathroom : Bool) -> Color {
if isAbsent{
return Color.red
}
if isBathroom{
return Color.blue
}
return Color.green
}
}