How to transfer FetchedResults data to an array of object in SWIFTUI - ios

I have a list of data from CoreData using fetch results. And I wanted to put that coredata data to a list of array so that I can save the checkbox state in memory not in the database.
Example:
#FetchRequest(
entity: ParticipantEntity.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \ParticipantEntity.first_name, ascending: true)]
) var participantItems:FetchedResults<ParticipantEntity>
**// is there a way to transfer participantItems to a simple list of object?like:
var newItems : participantItems **
var body: some View {
List {
ForEach(self.participantItems) { participant in
HStack{
AsyncImage(
url: URL(string: url+(participant.profile_picture ?? "")),
content: { image in
image.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 45, maxHeight: 45)
.cornerRadius(5)
},
placeholder: {
Image("no_profile_pic")
.resizable()
.frame(maxWidth: 45, maxHeight: 45)
}
)
VStack(alignment: .leading){
let fname = participant.first_name ?? ""
let lname = participant.last_name ?? ""
let name = fname + " " + lname
Text(name)
Text(participant.group_name ?? "No Group Name")
}.padding(.leading, 5.0)
Spacer()
**Image(systemName: participant.isChecked ? "checkmark.square" : "square")
.onTapGesture {
participant.isChecked.toggle()
}**
}
}
}.listStyle(PlainListStyle())
}
#FetchRequest(
entity: ParticipantEntity.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \ParticipantEntity.first_name, ascending: true)]
) var participantItems:FetchedResults<ParticipantEntity>
var newItems = newItems[participantItems]

You don't need to copy the values to a separate array. Have a #State property to hold the selected items:
#State private var selectedParticipants = Set<Participant>()
Then a couple of functions on your view to get and set selection:
private func isSelected(_ participant: Participant) -> Bool {
selectedParticipants.contains(participant)
}
private func toggleSelection(of participant: Participant) {
if isSelected(participant) {
selectedParticipants.remove(participant)
} else {
selectedParticipants.insert(participant)
}
}
So your checkbox would then be:
Image(systemName: isSelected(participant) ? "checkmark.square" : "square")
.onTapGesture {
toggleSelection(of: participant)
}

Related

Swiftui items get duplicated in all views when added to a single custom view

I'm struggling with the following issue: I'm trying to build a very simple app that lets you add items in a dedicated view that can be collapsed. I managed to write a simple function that lets me add multiple of these custom collapsable views. It's my first app so I wanted to follow the MVVM protocol. I think I got confused along the way because now every item I add gets automatically added to all the custom collapsable views I made. Is there any way to fix this? I thought using the UUID would solve this issue.. I'm guessing that I have to customise the "saveButtonPressed" function, but I don't know how to tell it to only add the item to the view where I pressed the "plus" button..
Here are the Models for the individual items and the collapsable view:
struct ItemModel: Identifiable, Equatable {
let id: String
let title: String
init(id: String = UUID().uuidString, title: String) {
self.id = id
self.title = title
}
func updateCompletion() -> ItemModel {
return ItemModel(id: id, title: title)
}
}
--
import Foundation
struct CollapsableItem: Equatable, Identifiable, Hashable {
let id: String
var title: String
init(id: String = UUID().uuidString, title: String) {
self.id = id
self.title = title
}
func updateCompletion() -> CollapsableItem {
return CollapsableItem(id: id, title: title)
}
}
These are my two ViewModels:
class ListViewModel: ObservableObject {
#Published var items: [ItemModel] = []
init() {
getItems()
}
func getItems() {
let newItems = [
ItemModel(title: "List Item1"),
ItemModel(title: "List Item2"),
ItemModel(title: "List Item3"),
]
items.append(contentsOf: newItems)
}
func addItem(title: String) {
let newItem = ItemModel(title: title)
items.append(newItem)
}
func updateItem(item: ItemModel) {
if let index = items.firstIndex(where: { $0.id == item.id}) {
items[index] = item.updateCompletion()
}
}
}
--
class CollapsedViewModel: ObservableObject {
#Published var collapsableItems: [CollapsableItem] = []
#Published var id = UUID().uuidString
init() {
getCollapsableItems()
}
func getCollapsableItems() {
let newCollapsableItems = [
CollapsableItem(title: "Wake up")
]
collapsableItems.append(contentsOf: newCollapsableItems)
}
func addCollapsableItem(title: String) {
let newCollapsableItem = CollapsableItem(title: title)
collapsableItems.append(newCollapsableItem)
}
func updateCollapsableItem(collapsableItem: CollapsableItem) {
if let index = collapsableItems.firstIndex(where: { $0.id ==
collapsableItem.id}) {
collapsableItems[index] =
collapsableItem.updateCompletion()
}
}
}
The item view:
struct ListRowView: View {
#EnvironmentObject var listViewModel: ListViewModel
let item: ItemModel
var body: some View {
HStack() {
Text(item.title)
.font(.body)
.fontWeight(.bold)
.foregroundColor(.white)
.multilineTextAlignment(.center)
.lineLimit(1)
.frame(width: 232, height: 16)
}
.padding( )
.frame(width: 396, height: 56)
.background(.gray)
.cornerRadius(12.0)
}
}
The collapsable view:
struct CollapsedView2<Content: View>: View {
#State var collapsableItem: CollapsableItem
#EnvironmentObject var collapsedViewModel: CollapsedViewModel
#State private var collapsed: Bool = true
#EnvironmentObject var listViewModel: ListViewModel
#State var label: () -> Text
#State var content: () -> Content
#State private var show = true
var body: some View {
ZStack{
VStack {
HStack{
Button(
action: { self.collapsed.toggle() },
label: {
HStack() {
Text("Hello")
.font(.title2.bold())
Spacer()
Image(systemName: self.collapsed ? "chevron.down" :
"chevron.up")
}
.padding(.bottom, 1)
.background(Color.white.opacity(0.01))
}
)
.buttonStyle(PlainButtonStyle())
Button(action: saveButtonPressed, label: {
Image(systemName: "plus")
.font(.title2)
.foregroundColor(.white)
})
}
VStack {
self.content()
}
ForEach(listViewModel.items) { item in ListRowView (item: item)
}
.lineLimit(1)
.fixedSize(horizontal: true, vertical: true)
.frame(minWidth: 396, maxWidth: 396, minHeight: 0, maxHeight: collapsed ?
0 : .none)
.animation(.easeInOut(duration: 1.0), value: show)
.clipped()
.transition(.slide)
}
}
}
func saveButtonPressed() {
listViewModel.addItem(title: "Hello")
}
}
and finally the main view:
struct ListView: View {
#EnvironmentObject var listViewModel: ListViewModel
#EnvironmentObject var collapsedViewModel: CollapsedViewModel
var body: some View {
ZStack{
ScrollView{
VStack{
HStack{
Text("MyFirstApp")
.font(.largeTitle.bold())
Button(action: newCollapsablePressed, label: {
Image(systemName: "plus")
.font(.title2)
.foregroundColor(.white)
})
}
.padding()
.padding(.leading)
ForEach(collapsedViewModel.collapsableItems) { collapsableItem in
CollapsedView2 (collapsableItem: collapsableItem,
label: { Text("") .font(.title2.bold()) },
content: {
HStack {
Text("")
Spacer() }
.frame(maxWidth: .infinity)
})
}
.padding()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.statusBar(hidden: false)
.navigationBarHidden(true)
}
}
func newCollapsablePressed() {
collapsedViewModel.addCollapsableItem(title: "hello2")
}
}
Would love to understand how I could fix this!
There is the anwser for your comment about add item in each CollapsedView2.
Because ListViewModel is not ObservableObject (ListViewModel is difference from each CollapsableItem). You should use "#State var items: [ItemModel]".
struct CollapsedView2<Content: View>: View {
#State var collapsableItem: CollapsableItem
// #State var listViewModel = ListViewModel()
#State var collapsed: Bool = true
#State var label: () -> Text
#State var content: () -> Content
#State private var show = true
#State var items: [ItemModel] = []
#State var count = 1
var body: some View {
VStack {
HStack{
Text("Hello")
.font(.title2.bold())
Spacer()
Button( action: { self.collapsed.toggle() },
label: {
Image(systemName: self.collapsed ? "chevron.down" : "chevron.up")
}
)
.buttonStyle(PlainButtonStyle())
Button(action: saveButtonPressed, label: {
Image(systemName: "plus")
.font(.title2)
// .foregroundColor(.white)
})
}
VStack {
self.content()
}
ForEach(items) { item in
ListRowView (item: item)
}
.lineLimit(1)
.fixedSize(horizontal: true, vertical: true)
.frame(minHeight: 0, maxHeight: collapsed ? 0 : .none)
.animation(.easeInOut(duration: 1.0), value: show)
.clipped()
.transition(.slide)
}
}
func saveButtonPressed() {
addItem(title: "Hello \(count)")
count += 1
}
func addItem(title: String) {
let newItem = ItemModel(title: title)
items.append(newItem)
}
func updateItem(item: ItemModel) {
if let index = items.firstIndex(where: { $0.id == item.id}) {
items[index] = item.updateCompletion()
}
}
}
There is the anwser. Ask me if you have some questions
struct ListView: View {
#StateObject var collapsedViewModel = CollapsedViewModel()
var body: some View {
ScrollView{
VStack{
HStack{
Text("MyFirstApp")
.font(.largeTitle.bold())
Button(action: newCollapsablePressed, label: {
Image(systemName: "plus")
.font(.title2)
// .foregroundColor(.white)
})
}
ForEach(collapsedViewModel.collapsableItems) { collapsableItem in
CollapsedView2 (collapsableItem: collapsableItem,
label: { Text("") .font(.title2.bold()) },
content: {
HStack {
Text("")
Spacer()
}
})
}
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.statusBar(hidden: false)
.navigationBarHidden(true)
}
func newCollapsablePressed() {
collapsedViewModel.addCollapsableItem(title: "hello2")
}
}
struct CollapsedView2<Content: View>: View {
#State var collapsableItem: CollapsableItem
#State var listViewModel = ListViewModel()
#State var collapsed: Bool = true
#State var label: () -> Text
#State var content: () -> Content
#State private var show = true
var body: some View {
VStack {
HStack{
Button( action: { self.collapsed.toggle() },
label: {
HStack() {
Text("Hello")
.font(.title2.bold())
Spacer()
Image(systemName: self.collapsed ? "chevron.down" : "chevron.up")
}
.padding(.bottom, 1)
.background(Color.white.opacity(0.01))
}
)
.buttonStyle(PlainButtonStyle())
Button(action: saveButtonPressed, label: {
Image(systemName: "plus")
.font(.title2)
.foregroundColor(.white)
})
}
VStack {
self.content()
}
ForEach(listViewModel.items) { item in
ListRowView (item: item)
}
.lineLimit(1)
.fixedSize(horizontal: true, vertical: true)
.frame(minHeight: 0, maxHeight: collapsed ? 0 : .none)
.animation(.easeInOut(duration: 1.0), value: show)
.clipped()
.transition(.slide)
}
}
func saveButtonPressed() {
listViewModel.addItem(title: "Hello")
}
}
struct ListRowView: View {
let item: ItemModel
var body: some View {
HStack() {
Text(item.title)
.font(.body)
.fontWeight(.bold)
.foregroundColor(.white)
.multilineTextAlignment(.center)
.lineLimit(1)
.frame(width: 232, height: 16)
}
.padding( )
.frame(width: 396, height: 56)
.background(.gray)
.cornerRadius(12.0)
}
}
class ListViewModel {
var items: [ItemModel] = []
init() {
getItems()
}
func getItems() {
let newItems = [
ItemModel(title: "List Item1"),
ItemModel(title: "List Item2"),
ItemModel(title: "List Item3"),
]
items.append(contentsOf: newItems)
}
func addItem(title: String) {
let newItem = ItemModel(title: title)
items.append(newItem)
}
func updateItem(item: ItemModel) {
if let index = items.firstIndex(where: { $0.id == item.id}) {
items[index] = item.updateCompletion()
}
}
}
class CollapsedViewModel: ObservableObject {
#Published var collapsableItems: [CollapsableItem] = []
#Published var id = UUID().uuidString
init() {
getCollapsableItems()
}
func getCollapsableItems() {
let newCollapsableItems = [
CollapsableItem(title: "Wake up")
]
collapsableItems.append(contentsOf: newCollapsableItems)
}
func addCollapsableItem(title: String) {
let newCollapsableItem = CollapsableItem(title: title)
collapsableItems.append(newCollapsableItem)
}
func updateCollapsableItem(collapsableItem: CollapsableItem) {
if let index = collapsableItems.firstIndex(where: { $0.id ==
collapsableItem.id}) {
collapsableItems[index] =
collapsableItem.updateCompletion()
}
}
}
struct CollapsableItem: Equatable, Identifiable, Hashable {
let id: String
var title: String
init(id: String = UUID().uuidString, title: String) {
self.id = id
self.title = title
}
func updateCompletion() -> CollapsableItem {
return CollapsableItem(id: id, title: title)
}
}
struct ItemModel: Identifiable, Equatable {
let id: String
let title: String
init(id: String = UUID().uuidString, title: String) {
self.id = id
self.title = title
}
func updateCompletion() -> ItemModel {
return ItemModel(id: id, title: title)
}
}

Swift 2.0 .contextMenu Multiple Delete From Core Data

First time post in here and new to coding... so I hope I am following proper protocol. I am putting together a view in Xcode 12.2 (SwiftUI 2) that outputs a list of data from Core Data and have a context menu to provide the user the option to edit, delete, and delete multiple. The context menu is working properly for edit and delete, however, I am facing a road block in how to implement the functionality to delete multiple list items. I am imagining the user would hard press one of the list items, the context menus pops open and if they press the "Delete Multiple" option, the view activates something similar to an edit mode that populates little circle on the left of each item which the user can select and delete more than one item at a time. I can see other article on how to do this, however, I cannot find guidance on how to implement this through Core Data. I have pasted my code below.
Please let me know if I am missing any other information that would make my question more clear.
I really appreciate the forums expertise and guidance.
Struct List : View {
#StateObject var appData = AppViewModel()
#Environment(\.managedObjectContext) var context
//Fetch Data...
#FetchRequest(entity: EntryData.entity(), sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)], animation: .spring()) var results : FetchedResults<EntryData>
var body : some View {
ZStack(alignment: Alignment(horizontal: .trailing, vertical: .bottom), content: {
VStack{
ScrollView(.vertical, showsIndicators: false, content: {
LazyVStack(alignment: .leading, spacing: 20){
ForEach(results){task in
VStack(alignment: .leading, spacing: 5, content: {
Text(task.category ?? "")
.font(.title)
.fontWeight(.bold)
Text(task.date ?? Date(), style:. date)
.fontWeight(.bold)
Text("\(task.number.formattedCurrencyText)")
})
.padding(.horizontal, 14)
.padding(.top, 10)
.foregroundColor(Color("ColorTextList"))
.contextMenu{
Button(action: {appData.editItem(item: task)}, label: {
Text("Edit")
})
Button(action: {
context.delete(task)
try! context.save()
}, label: {
Text("Delete")
})
Button(action: {}, label: {
Text("Delete Mutiple")
})
}
}
}
})
}
VStack(){
VisualEffectView(effect: UIBlurEffect(style: .regular))
.frame(width: UIScreen.main.bounds.width, height: 50, alignment: .top)
.edgesIgnoringSafeArea(.all)
.background(Color.clear)
Spacer()
}
})
.background(Color.clear)
.sheet(isPresented: $appData.isNewData, content: {
AddDataView(appData: appData)
.environment(\.managedObjectContext, self.context)
})
}
}
Adding the viewModel of the app. How do I tap into into this and delete each of the attributes in a multi-list selection?
import SwiftUI
import CoreData
class AppViewModel : ObservableObject, Identifiable{
#Published var cateogry = ""
#Published var date = Date()
#Published var number : Double? = nil
#Published var notes = ""
#Published var id = UUID()
}
And adding the actual Core Data model screenshot.
You can implement a Selection Set for your List. This will contain all elements that are selected. Then you can dynamically show the contextMenu for delete or deleteAll based on the count of the set. Here is a full example with the implementation of deleteAll
struct SelectionDemo : View {
#State var demoData = ["Dave", "Tom", "Phillip", "Steve"]
#State var selected = Set<String>()
var body: some View {
HStack {
List(demoData, id: \.self, selection: $selected){ name in
Text(name)
.contextMenu {
Button(action: {
//Delete only one item
}){
Text("Delete")
}
if (selected.count > 1) {
Button(action: {
//Delete all
deleteAll()
})
{
Text("Delete all")
}
}
}
}.frame(width: 500, height: 460)
}
}
func deleteAll() {
for element in selected {
self.demoData.removeAll(where: {
$0 == element
})
}
}
}

SwiftUI: How to select multi items(image) with ForEach?

I'm working on my project with the feature of select multiple blocks of thumbnails. Only selected thumbnail(s)/image will be highlighted.
For the ChildView, The binding activeBlock should be turned true/false if a use taps on the image.
However, when I select a thumbnail, all thumbnails will be highlighted.I have come up with some ideas like
#State var selectedBlocks:[Bool]
// which should contain wether or not a certain block is selected.
But I don't know how to implement it.
Here are my codes:
ChildView
#Binding var activeBlock:Bool
var thumbnail: String
var body: some View {
VStack {
ZStack {
Image(thumbnail)
.resizable()
.frame(width: 80, height: 80)
.background(Color.black)
.cornerRadius(10)
if activeBlock {
RoundedRectangle(cornerRadius: 10)
.stroke(style: StrokeStyle(lineWidth: 2))
.frame(width: 80, height: 80)
.foregroundColor(Color("orange"))
}
}
}
BlockBView
struct VideoData: Identifiable{
var id = UUID()
var thumbnails: String
}
struct BlockView: View {
var videos:[VideoData] = [
VideoData(thumbnails: "test"), VideoData(thumbnails: "test2"), VideoData(thumbnails: "test1")
]
#State var activeBlock = false
var body: some View {
ScrollView(.horizontal){
HStack {
ForEach(0..<videos.count) { _ in
Button(action: {
self.activeBlock.toggle()
}, label: {
ChildView(activeBlock: $activeBlock, thumbnail: "test")
})
}
}
}
}
Thank you for your help!
Here is a demo of possible approach - we initialize array of Bool by videos count and pass activated flag by index into child view.
Tested with Xcode 12.1 / iOS 14.1 (with some replicated code)
struct BlockView: View {
var videos:[VideoData] = [
VideoData(thumbnails: "flag-1"), VideoData(thumbnails: "flag-2"), VideoData(thumbnails: "flag-3")
]
#State private var activeBlocks: [Bool] // << declare
init() {
// initialize state with needed count of bools
self._activeBlocks = State(initialValue: Array(repeating: false, count: videos.count))
}
var body: some View {
ScrollView(.horizontal){
HStack {
ForEach(videos.indices, id: \.self) { i in
Button(action: {
self.activeBlocks[i].toggle() // << here !!
}, label: {
ChildView(activeBlock: activeBlocks[i], // << here !!
thumbnail: videos[i].thumbnails)
})
}
}
}
}
}
struct ChildView: View {
var activeBlock:Bool // << value, no binding needed
var thumbnail: String
var body: some View {
VStack {
ZStack {
Image(thumbnail)
.resizable()
.frame(width: 80, height: 80)
.background(Color.black)
.cornerRadius(10)
if activeBlock {
RoundedRectangle(cornerRadius: 10)
.stroke(style: StrokeStyle(lineWidth: 2))
.frame(width: 80, height: 80)
.foregroundColor(Color.orange)
}
}
}
}
}
Final result
Build your element and it's model first. I'm using MVVM,
class RowModel : ObservableObject, Identifiable {
#Published var isSelected = false
#Published var thumnailIcon: String
#Published var name: String
var id : String
var cancellables = Set<AnyCancellable>()
init(id: String, name: String, icon: String) {
self.id = id
self.name = name
self.thumnailIcon = icon
}
}
//Equivalent to your BlockView
struct Row : View {
#ObservedObject var model: RowModel
var body: some View {
GroupBox(label:
Label(model.name, systemImage: model.thumnailIcon)
.foregroundColor(model.isSelected ? Color.orange : .gray)
) {
HStack {
Capsule()
.fill(model.isSelected ? Color.orange : .gray)
.onTapGesture {
model.isSelected = !model.isSelected
}
//Two way binding
Toggle("", isOn: $model.isSelected)
}
}.animation(.spring())
}
}
Prepare data and handle action in your parent view
struct ContentView: View {
private let layout = [GridItem(.flexible()),GridItem(.flexible())]
#ObservedObject var model = ContentModel()
var body: some View {
VStack {
ScrollView {
LazyVGrid(columns: layout) {
ForEach(model.rowModels) { model in
Row(model: model)
}
}
}
if model.selected.count > 0 {
HStack {
Text(model.selected.joined(separator: ", "))
Spacer()
Button(action: {
model.clearSelection()
}, label: {
Text("Clear")
})
}
}
}
.padding()
.onAppear(perform: prepare)
}
func prepare() {
model.prepare()
}
}
class ContentModel: ObservableObject {
#Published var rowModels = [RowModel]()
//I'm handling by ID for futher use
//But you can convert to your Array of Boolean
#Published var selected = Set<String>()
func prepare() {
for i in 0..<20 {
let row = RowModel(id: "\(i)", name: "Block \(i)", icon: "heart.fill")
row.$isSelected
.removeDuplicates()
.receive(on: RunLoop.main)
.sink(receiveValue: { [weak self] selected in
guard let `self` = self else { return }
print(selected)
if selected {
self.selected.insert(row.name)
}else{
self.selected.remove(row.name)
}
}).store(in: &row.cancellables)
rowModels.append(row)
}
}
func clearSelection() {
for r in rowModels {
r.isSelected = false
}
}
}
Don't forget to import Combine framework.

Cannot compile SwiftUI ForEach Table

I'm trying to make a table view from dynamic array, but compiler cannot build it with unrelated error:
The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
struct RecentView: View {
#State var recentRooms: [[String: String]] = []
#Binding var recentIsPressed: Bool
#Binding var nextIsPressed: Bool
#Binding var conferenceName: String
let screenWidth = UIScreen.main.bounds.size.width
var body: some View {
if let recent = UserDefaults.standard.array(forKey: "recentRooms") as? [[String: String]] {
recentRooms = recent
}
return VStack {
...
VStack(alignment: .leading) {
ForEach(0 ..< recentRooms.count) { i in
Button(action: { self.meetingSelected(name: self.recentRooms[i]["name"] ?? "") }) {
HStack {
ZStack {
Circle()
.foregroundColor(Color("ProjectBlue"))
.frame(width: 76, height: 76)
Text(self.recentRooms[i]["short_name"] ?? "")
.font(.custom("Roboto-Bold", size: 36))
.foregroundColor(Color("AlmostWhite"))
}
VStack(alignment: .leading) {
Text(self.recentRooms[i]["name"] ?? "")
.font(.custom("OpenSans-Bold", size: 17))
.foregroundColor(Color("AlmostWhite"))
Text(self.recentRooms[i]["date"] ?? "")
.font(.custom("OpenSans-Regular", size: 17))
.foregroundColor(Color("AlmostWhite"))
Text(self.recentRooms[i]["duration"] ?? "")
.font(.custom("OpenSans-Regular", size: 17))
.foregroundColor(Color("AlmostWhite"))
}
}
}
.frame(width: self.screenWidth, alignment: .leading)
.padding(.leading, 20)
}
}
}
}
private func meetingSelected(name: String) {
conferenceName = name
nextIsPressed = true
recentIsPressed = false
}
}
Any suggestions on what is wrong with the ForEach part?
This variant of ForEach shouldn't be used with a dynamic content.
ForEach(0 ..< recentRooms.count)
If the recentRooms variable can be changed, use this instead:
ForEach(recentRooms, id: \.self)

Update view bugs when data in firebase changes

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

Resources