How can I pass data through a Navigation Link with FetchedResults and CoreData? - swift

I'm trying to pass FetchResult Data from one screen to another, through a Navigation Link in a ScrollView.
When I try to call the passed data in the DetailView screen, I get the following error:
Instance member "" cannoth be used on type "SavedPoem"
Code below - Saved Poem List
import SwiftUI
struct SavedPoemList: View {
#Environment(\.managedObjectContext) var moc
#FetchRequest(entity: SavedPoem.entity(), sortDescriptors: []) var savedpoems : FetchedResults<SavedPoem>
var body: some View {
VStack (alignment: .leading, spacing: 0) {
HStack{
Text("Your Saved Poems")
.font(.title)
.fontWeight(.black)
.foregroundColor(.black)
Spacer()
}.padding(.bottom)
.padding(.trailing)
.padding(.leading)
ScrollView {
ForEach(savedpoems, id:\.title) {SavedPoem in
NavigationLink (destination: DetailViewSaved()){
ZStack {
Rectangle()
.fill(Color.white)
.frame(width: UIScreen.screenWidth - 40, height: 70)
.cornerRadius(5)
.padding([.horizontal], 20)
// .shadow(color: .gray, radius: 10)
HStack {
VStack (alignment: .leading){
Text("\(SavedPoem.title ?? "")")
.font(.headline)
.foregroundColor(.black)
.lineLimit(1)
.padding(.bottom, 3)
Text("\(SavedPoem.author ?? "")")
.font(.subheadline)
.foregroundColor(.secondary)
}
.padding(.trailing)
Spacer()
}
.padding()
}.padding(.bottom,10)
}
}.onDelete(perform: self.remove)
}
.navigationTitle("My Saved Poems")
.navigationBarHidden(true)
.edgesIgnoringSafeArea(.top)
.padding(.bottom, 30)
}
.padding(.horizontal, 30)
.edgesIgnoringSafeArea(.bottom)
}
func remove(at offsets : IndexSet) {
for index in offsets {
let delete = savedpoems[index]
self.moc.delete(delete)
}
try? self.moc.save()
}
}
and Code for the Detail View
import SwiftUI
struct DetailViewSaved: View {
#ObservedObject var fetch = FetchPoem()
#Environment(\.managedObjectContext) var moc
#FetchRequest(entity: SavedPoem.entity(), sortDescriptors: []) var savedpoems : FetchedResults<SavedPoem>
#State private var saved : Bool = false
var currentDate = Text(Date().addingTimeInterval(600), style: .date)
var body: some View {
VStack {
HStack{
// NavigationLink( destination: HomeView())
// {
//
// Image(systemName: "arrow.backward")
// .font(.system(size: 25, weight: .heavy))
// .foregroundColor(.black)
// }
Spacer(minLength: 0)
Button(action:
{
// self.moc.delete(delete)
try? self.moc.save()
}) {
Image(systemName: saved ? "bookmark.fill": "bookmark")
.font(.system(size: 25, weight: .heavy))
.foregroundColor(.black)
}
}
ScrollView {
VStack {
HStack{
VStack (alignment: .leading) {
Text("Today's Poem, \(currentDate)")
.font(.subheadline)
.foregroundColor(Color.gray)
.padding(.bottom, 20)
.padding(.top, 10)
Text("\(SavedPoem.title ?? "")")
// .font(.largeTitle)
// .fontWeight(.heavy)
// .foregroundColor(.black)
// .padding(.bottom, 20)
// .lineSpacing(0)
// Text("BY "+poem.author.uppercased())
Text("BY \(SavedPoem.author ?? "")")
// .font(.subheadline)
// .foregroundColor(Color.gray)
// .padding(.bottom, 20)
HStack {
Text("\(SavedPoem.lines ?? "")")
// .font(.body)
// .foregroundColor(.black)
// .padding(.bottom)
// .lineSpacing(5)
//
Spacer()
}
Spacer()
}
.padding()
}
}
}
}
}
}
Am I missing anything? How can I resolve this issue, and pass the data from the Fetchedresults in the list view to the detailed screen?
Thanks in advance.
EDITED CODE FOR REPLY
SavedPoemList:
import SwiftUI
struct SavedPoemList: View {
#Environment(\.managedObjectContext) var moc
#FetchRequest(entity: SavedPoem.entity(), sortDescriptors: []) var savedpoems : FetchedResults<SavedPoem>
var body: some View {
VStack (alignment: .leading, spacing: 0) {
HStack{
Text("Your Saved Poems")
.font(.title)
.fontWeight(.black)
.foregroundColor(.black)
Spacer()
Text("Edit")
.font(.subheadline)
.fontWeight(.black)
.foregroundColor(.gray)
}.padding(.bottom)
.padding(.trailing)
.padding(.leading)
ScrollView {
ForEach(savedpoems, id:\.title) {SavedPoem in
NavigationLink (destination: DetailViewSaved(savedPoem: SavedPoem)){
ZStack {
Rectangle()
.fill(Color.white)
.frame(width: UIScreen.screenWidth - 40, height: 70)
.cornerRadius(5)
.padding([.horizontal], 20)
// .shadow(color: .gray, radius: 10)
HStack {
VStack (alignment: .leading){
Text("\(SavedPoem.title ?? "")")
.font(.headline)
.foregroundColor(.black)
.lineLimit(1)
.padding(.bottom, 3)
Text("\(SavedPoem.author ?? "")")
.font(.subheadline)
.foregroundColor(.secondary)
}
.padding(.trailing)
Spacer()
}
.padding()
}.padding(.bottom,10)
}
}.onDelete(perform: self.remove)
}
.navigationTitle("My Saved Poems")
.navigationBarHidden(true)
.edgesIgnoringSafeArea(.top)
.padding(.bottom, 30)
}
.padding(.horizontal, 30)
.edgesIgnoringSafeArea(.bottom)
}
func remove(at offsets : IndexSet) {
for index in offsets {
let delete = savedpoems[index]
self.moc.delete(delete)
}
try? self.moc.save()
}
}
DetailView
import SwiftUI
struct DetailViewSaved: View {
#ObservedObject var fetch = FetchPoem()
#Environment(\.managedObjectContext) var moc
#FetchRequest(entity: SavedPoem.entity(), sortDescriptors: []) var savedpoems : FetchedResults<SavedPoem>
#State private var saved : Bool = false
var savedPoem : SavedPoem
var currentDate = Text(Date().addingTimeInterval(600), style: .date)
var body: some View {
VStack {
HStack{
// NavigationLink( destination: HomeView())
// {
//
// Image(systemName: "arrow.backward")
// .font(.system(size: 25, weight: .heavy))
// .foregroundColor(.black)
// }
Spacer(minLength: 0)
Button(action:
{
// self.moc.delete(delete)
try? self.moc.save()
}) {
Image(systemName: saved ? "bookmark.fill": "bookmark")
.font(.system(size: 25, weight: .heavy))
.foregroundColor(.black)
}
}
ScrollView {
VStack {
HStack{
VStack (alignment: .leading) {
// Text("Today's Poem, \(currentDate)")
// .font(.subheadline)
// .foregroundColor(Color.gray)
// .padding(.bottom, 20)
// .padding(.top, 10)
Text("\(savedPoem.title ?? "")")
// .font(.largeTitle)
// .fontWeight(.heavy)
// .foregroundColor(.black)
// .padding(.bottom, 20)
// .lineSpacing(0)
// Text("BY "+poem.author.uppercased())
// Text("BY \(SavedPoem.author ?? "")")
// .font(.subheadline)
// .foregroundColor(Color.gray)
// .padding(.bottom, 20)
// HStack {
// Text("\(SavedPoem.lines ?? "")")
// .font(.body)
// .foregroundColor(.black)
// .padding(.bottom)
// .lineSpacing(5)
//
Spacer()
}
Spacer()
}
.padding()
}
}
}
}
}
}
struct DetailViewSaved_Previews: PreviewProvider {
static var previews: some View {
DetailViewSaved(savedPoem: SavedPoem)
}
}

I made an example with your case :
SavedPoemList : Pass an item from your array to your Detail View like that :
NavigationLink (destination: DetailViewSaved(savedPoem: SavedPoem)){
VStack{
Text("Your row view")
}
}
and to receive it in your Detail View :
struct DetailViewSaved: View {
var savedPoem : SavedPoem
var body: some View {
Text("Your view")
}
}

Related

Can’t get the sum of ForEach items

I'm trying to display the total of the Depot Values. One depot can have many stocks.
My Overview Page has a List of Depots and should have a Text with the total value of all depots.
I get the value of one depot as following:
(connected in One-To-Many-Relationship)
let sum = depot.aktienArray.map { $0.a_purchValue }.reduce(0, +)
Portfolio View:
struct Portfolio: View {
#Environment(\.managedObjectContext) private var viewContext
#FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Depot.d_name, ascending: true)],
animation: .default)
private var depots: FetchedResults<Depot>
// General States
#State private var showAddDepotView: Bool = false
// #State private var //left header button
#State private var isShowingConfirmation: Bool = false
#State private var isShowingMenu: Bool = false
#State private var navigateTo: AnyView?
#State private var isNavActive = false
#State private var depotToDelete: Depot?
#State var PortVal: Double = 0
#State var sum: Double = 0
var body: some View {
NavigationView {
GeometryReader { g in
VStack(spacing: 0) {
//MARK: DEPOT OVERVIEW
VStack {
VStack(alignment: .leading) {
HStack {
Text("Portfolio Value")
.font(.system(size: g.size.width / 22))
.foregroundColor(Color.gray)
Spacer()
Text("Details")
.foregroundColor(Color.white)
.font(.system(size: g.size.width / 22))
Image(systemName: "chevron.right")
.foregroundColor(Color.white)
.padding(.leading, g.size.width / -100)
}
.padding(.horizontal, g.size.width / 40)
.padding(.bottom, g.size.height / -45)
.padding(.top, g.size.height / 100)
HStack(alignment: .top) {
Text("\(PortVal as NSNumber, formatter: formatter) €")
.bold()
.foregroundColor(Color.blue)
.minimumScaleFactor(0.4)
.font(.system(size: g.size.width / 10))
.lineLimit(1)
Text("+22,3 %")
.bold()
.foregroundColor(Color.green)
.font(.system(size: g.size.width / 20))
}
.padding(.horizontal, g.size.width / 40)
.padding(.bottom, g.size.height / 800)
HStack {
VStack(alignment: .leading) {
Text("Liquidität")
.font(.system(size: g.size.width / 22))
.foregroundColor(Color.gray)
.padding(.bottom, g.size.height / -75)
Text("339.830,87 €")
.bold()
.foregroundColor(Color.blue)
.minimumScaleFactor(0.4)
.font(.system(size: g.size.width / 14))
.lineLimit(1)
}
Spacer()
VStack(alignment: .center) {
Text("Yield")
.font(.system(size: g.size.width / 22))
.foregroundColor(Color.gray)
.padding(.bottom, g.size.height / -75)
Text("5,80 %")
.bold()
.foregroundColor(Color.blue)
.font(.system(size: g.size.width / 14))
}
Spacer()
}
.padding(.leading, g.size.width / 40)
.padding(.bottom, g.size.height / 120)
}
.frame(height: UIScreen.main.bounds.height / 5.5)
.frame(maxWidth: .infinity)
.background(RoundedRectangle(cornerRadius: 15).fill(Color.white.opacity(0.1)))
}
.padding(.horizontal, g.size.width / 30)
.padding(.vertical, g.size.height / 65)
.padding(.bottom, g.size.height / 150)
//
//MARK: DEPOT LIST
// DEPOT LIST TITLE
VStack(spacing: 0) {
Divider()
HStack {
Text("Depots")
.bold()
.font(.system(size: g.size.width / 12))
.foregroundColor(.white)
Spacer()
}
.padding(.horizontal, g.size.width / 30)
.padding(.top, g.size.width / 40)
.padding(.bottom, g.size.width / 100)
Divider()
.overlay(.white)
.padding(.horizontal, g.size.width / 30)
}
//DEPOT LIST
VStack(alignment: .leading) {
ScrollView(showsIndicators: false) {
Color.clear
.padding(.bottom, g.size.height / -200)
ForEach(depots) { depot in
let sum = depot.aktienArray.map { $0.a_purchValue }.reduce(0, +)
ZStack {
NavigationLink(destination: DepotDetail(depot: depot)) {
VStack(alignment: .leading, spacing: 6) {
HStack {
VStack (alignment: .leading){
HStack (alignment: .bottom) {
Text(depot.d_name ?? "")
.font(.system(size: 22))
.foregroundColor(Color.GrayD)
.bold()
Text("Depot-Nr.: \(depot.d_nr as NSNumber, formatter: formatter2)")
.foregroundColor(.gray)
.font(.system(size: 16))
Spacer()
}
Text("Eröffnet: \(depot.d_createDate ?? Date(), formatter: formatterD)")
.font(.system(size: 15))
.foregroundColor(.GrayD)
}
}.padding(.bottom, -0.5)
.padding(.top, 40)
HStack {
VStack (alignment: .leading) {
Text("Depot Value")
.font(.system(size: 18))
.foregroundColor(Color.gray)
if sum == 0 {
Text("0,00 €")
.bold()
.foregroundColor(Color.GrayD)
.minimumScaleFactor(0.32)
.font(.system(size: g.size.width / 4))
.lineLimit(1)
.padding(.bottom, -5)
} else {
Text("\(sum as NSNumber, formatter: formatter) €")
.bold()
.foregroundColor(Color.GrayD)
.minimumScaleFactor(0.32)
.font(.system(size: g.size.width / 4))
.lineLimit(1)
.padding(.bottom, -5)
}
Text("Performance")
.font(.system(size: 18))
.foregroundColor(Color.gray)
HStack {
Text("360,00 €")
.bold()
.foregroundColor(Color.green)
.font(.system(size: 25))
.lineLimit(1)
Text("+3,6 %")
.bold()
.foregroundColor(Color.green)
.font(.system(size: 18))
}
}
Spacer()
VStack(alignment: .leading) {
Text("Yield")
.font(.system(size: 18))
.foregroundColor(Color.gray)
Text("5,80 %")
.bold()
.foregroundColor(Color.GrayD)
.font(.system(size: 25))
Spacer()
Text("FSA")
.font(.system(size: 18))
.foregroundColor(Color.gray)
Text("\(depot.d_fsa as NSNumber, formatter: formatter) €")
.bold()
.foregroundColor(Color.GrayD)
.font(.system(size: 25))
}
}
Spacer()
Spacer()
Spacer()
}
.padding(.horizontal, g.size.width / 40)
.frame(height: UIScreen.main.bounds.height / 4.7)
.frame(maxWidth: .infinity)
.background(RoundedRectangle(cornerRadius: 15).fill(Color.white))
}.buttonStyle(FlatLinkStyle())
Menu {
Button {
self.navigateTo = AnyView(EditDepot(depot: depot))
self.isNavActive = true
} label: {
Label("Bearbeiten", systemImage: "pencil")
}
Button(role: .destructive) {
depotToDelete = depot
isShowingConfirmation = true
} label: {
Label("Löschen", systemImage: "trash")
}
} label: {
Image(systemName: "ellipsis.circle").foregroundColor(.GrayD)
}
.padding(.bottom, g.size.height / 5.75)
.padding(.leading, g.size.width / 1.25)
.confirmationDialog("Depot", isPresented: $isShowingConfirmation, titleVisibility: .visible) {
if let depot = depotToDelete {
Text("Depot \"\(depot.d_name ?? "")\" wirklich löschen?")
}
Button(role: .destructive) {
if let depot = depotToDelete {
deleteDepot(depot: depot)
}
} label: {
if let depot = depotToDelete {
Text("\"\(depot.d_name ?? "")\" wirklich löschen?")
}
}
}
.background(
NavigationLink(destination: self.navigateTo, isActive: $isNavActive) {
EmptyView()
}
)
}
.onAppear {
PortVal = 0
PortVal + sum
}
// .onChange(of: sum, perform: { _ in
// PortVal = 0
// PortVal += sum
//
// })
}
// DEPOT ADD BUTTON
VStack {
HStack {
Button {
showAddDepotView.toggle()
} label: {
HStack {
Image(systemName: "plus")
Text("Depot hinzufügen")
.font(.headline)
}
.foregroundColor(.white)
}
}
.frame(height: UIScreen.main.bounds.height / 20)
.frame(maxWidth: .infinity)
.background(RoundedRectangle(cornerRadius: 15).fill(Color.white.opacity(0.1)))
}
.padding(.top, g.size.height / 50)
}
}
.padding(.horizontal, g.size.width / 30)
}
.background(Color.GrayD.ignoresSafeArea())
.fullScreenCover(isPresented: $showAddDepotView, content: AddDepot.init)
.preferredColorScheme(.dark)
}
.navigationTitle("")
.navigationBarHidden(true)
}
}
private func deleteDepot(depot: Depot) {
withAnimation {
viewContext.delete(depot)
do {
try viewContext.save()
} catch {
print(error)
}
}
}
}
DepotDetail View:
import SwiftUI
struct DepotDetail: View {
#Environment(\.managedObjectContext) private var viewContext
#Environment(\.presentationMode) var presentationMode
// CoreData States
#StateObject var depot: Depot
#State private var aktieToDelete: AktieKauf?
#State private var a_name: String = ""
#State private var a_industry: String = ""
#State private var a_segment: String = ""
#State private var a_shares: Double = 0
#State private var a_purchPrice: Double = 0
#State private var a_purchValue: Double = 0
#State private var a_expDividend: Double = 0
#State private var a_fees: Double = 0
#State private var a_ertrag: Double = 0
#State private var a_purchDate: Date = Date()
// General States
#State private var showAddStockView: Bool = false
#State private var isShowingConfirmation: Bool = false
#State private var navigateTo: AnyView?
#State private var isNavActive = false
var body: some View {
GeometryReader { g in
VStack(spacing: 0) {
Text("\(PortfolioValue() as NSNumber, formatter: formatter) €")
// Image(systemName: "ellipsis")
//MARK: BESTAND
VStack(alignment: .leading) {
ScrollView(showsIndicators: false) {
Color.clear
.padding(.bottom, g.size.height / -200)
ForEach(depot.aktienArray) { aktie in
ZStack {
//NavigationLink(destination: StockDetail(depot: depot)) {
VStack(alignment: .leading, spacing: 6) {
HStack {
VStack (alignment: .leading){
HStack (alignment: .bottom) {
Text(aktie.unwrappedName)
.font(.system(size: 22))
.foregroundColor(Color.GrayD)
.bold()
Text("\(aktie.a_purchValue)")
.foregroundColor(.black)
Text(aktie.a_industry ?? "").foregroundColor(.black)
Spacer()
}
Text("Gekauft: \(aktie.a_purchDate ?? Date(), style: .date)")
.foregroundColor(.GrayD)
}
}
.padding(.bottom, -0.5)
.padding(.top, 40)
HStack {
VStack (alignment: .leading) {
Text("Depot Value")
.foregroundColor(Color.gray)
Text("360.000,00 €")
.bold()
.font(.system(size: g.size.width / 4))
.lineLimit(1)
}
Spacer()
VStack(alignment: .center) {
Text("Yield")
.font(.system(size: 18))
.foregroundColor(Color.gray)
Text("5,80 %")
.bold()
.foregroundColor(Color.GrayD)
}
}
HStack {
VStack(alignment: .leading) {
Text("Performance")
.font(.system(size: 18))
HStack {
Text("360,00 €")
.bold()
.font(.system(size: 25))
.lineLimit(1)
Text("+3,6 %")
.bold()
.foregroundColor(Color.green)
}
}
}
Spacer()
Spacer()
Spacer()
}
.padding(.horizontal, g.size.width / 40)
.frame(height: UIScreen.main.bounds.height / 4.7)
.frame(maxWidth: .infinity)
.background(RoundedRectangle(cornerRadius: 15).fill(Color.white))
//}
//.buttonStyle(FlatLinkStyle())
Menu {
Button {
self.navigateTo = AnyView(EditDepot(depot: depot))
self.isNavActive = true
} label: {
Label("Bearbeiten", systemImage: "pencil")
}
Button(role: .destructive) {
aktieToDelete = aktie
isShowingConfirmation = true
} label: {
Label("Löschen", systemImage: "trash")
}
} label: {
Image(systemName: "ellipsis.circle").foregroundColor(.GrayD)
}
.padding(.bottom, g.size.height / 5.75)
.padding(.leading, g.size.width / 1.25)
.confirmationDialog("Aktie", isPresented: $isShowingConfirmation, titleVisibility: .visible) {
if let aktie = aktieToDelete {
Text("Aktie \"\(aktie.a_name ?? "?")\" wirklich löschen?")
}
Button(role: .destructive) {
if let aktie = aktieToDelete {
deleteStock(aktieKauf: aktie)
}
} label: {
if let aktie = aktieToDelete {
Text("\"\(aktie.a_name ?? "?")\" wirklich löschen?")
}
}
}
//.background(
//NavigationLink(destination: self.navigateTo, //isActive: $isNavActive) {
// EmptyView()
// }
//)
}
}
}
}
.padding(.horizontal, g.size.width / 30)
//
Spacer()
}
.background(Color.GrayD.ignoresSafeArea())
.fullScreenCover(isPresented: $showAddStockView) {
AddStock(depot: depot)
}
.preferredColorScheme(.dark)
}
.navigationBarTitle("")
.navigationBarHidden(true)
}
private func addStock() {
withAnimation {
let newAktie = AktieKauf(context: viewContext)
newAktie.a_name = a_name
newAktie.a_industry = a_industry
newAktie.a_segment = a_segment
newAktie.a_shares = a_shares
newAktie.a_purchPrice = a_purchPrice
newAktie.a_purchValue = a_purchValue
newAktie.a_expDividend = a_expDividend
newAktie.a_fees = a_fees
newAktie.a_ertrag = a_ertrag
newAktie.a_purchDate = a_purchDate
depot.addToAktieKaufRel(newAktie)
PersistenceController.shared.saveContext()
}
}
private func deleteStock(aktieKauf: AktieKauf) {
withAnimation {
viewContext.delete(aktieKauf)
do {
try viewContext.save()
} catch {
print(error)
}
}
}
func deleteAktie(at offsets: IndexSet) {
withAnimation {
for index in offsets {
let aktie = depot.aktienArray[index]
viewContext.delete(aktie)
PersistenceController.shared.saveContext()
}
}
}
private func PortfolioValue() -> Double {
var portfolioValue: Double = 0
for item in depot.aktienArray {
portfolioValue += item.a_purchValue
}
return portfolioValue
}
}
With SwiftUI you want to create a ViewModel that represents something that's the formatted version of your model. Something that's as easy as possible to map to views. You don't want to be doing calculation and logic within a View's body.
So for example:
extension Depot {
func sumOfStuff() -> Double {
aktienArray.map(\.a_purchValue).reduce(0, +)
}
}
That would be your model, then your view model might be that value stringified for presentation.
extension DepotViewModel {
var presentableSumOfStuff: String {
if depot.sumOfStuff() == 0 { return "0" } else { ... }
The problem is this "normal swift code" inside your ViewBuilder block:
ForEach(depots) { depot in
let sum = depot.aktienArray.map { $0.a_purchValue }.reduce(0, +) // This should not be here
ZStack {
Within a ViewBuilder context, you can't put normal swift code, it should just be a list of Views.
If you set up your ViewModel right, then you don't have complex logic in the view like instead of this:
if sum == 0 {
Text("0,00 €")
.bold()
.foregroundColor(Color.GrayD)
.minimumScaleFactor(0.32)
.font(.system(size: g.size.width / 4))
.lineLimit(1)
.padding(.bottom, -5)
} else {
Text("\(sum as NSNumber, formatter: formatter) €")
.bold()
.foregroundColor(Color.GrayD)
.minimumScaleFactor(0.32)
.font(.system(size: g.size.width / 4))
.lineLimit(1)
.padding(.bottom, -5)
}
You would have
Text($0.presentableSumOfStuff)
.bold()
.foregroundColor(Color.GrayD)
.minimumScaleFactor(0.32)
.font(.system(size: g.size.width / 4))
.lineLimit(1)
.padding(.bottom, -5)

How to remove blank space between two text views in SwiftUI

I have a problem with a Text view. I have a List of menu products, and the products contains a description. But, when a product don't have a description, there is a blank space. I do not want the blank space. How can I remove the blank space ?
This is what I Have : [First Picture]
This is what I want : Second Picture
I will share the code Below :
struct SectionVieww: View {
#EnvironmentObject var syncViewModel : SyncViewModel
#StateObject var coreDataViewModel = CoreDataViewModel()
#Environment(\.managedObjectContext) private var viewContext
#FetchRequest(sortDescriptors: [])
private var menus : FetchedResults<LocalMenu>
var menuType : MenuType
var body: some View {
let filteredMenus = syncViewModel.menu
.filter({$0.type == menuType.id})
if !filteredMenus.isEmpty {
Section(header:
Text("\(menuType.text.capitalized)")
.font(.system(size: 20, weight: .bold, design: .rounded))
.foregroundColor(.black)
) {
ForEach(filteredMenus) { men in
HStack {
WebImage(url: men.image.getThumbnailUrl(), options: .refreshCached)
.onFailure(perform: { (error) in
} )
.resizable()
.frame(width: 80, height: 80)
.cornerRadius(10)
VStack(alignment: .leading, spacing: 10) {
Spacer()
Text(men.name)
.font(.system(size: 16))
.foregroundColor(.black)
.fontWeight(.semibold)
Text(men.welcomeDescription ?? "")
.font(.caption)
.foregroundColor(.gray)
HStack {
Text("\(men.price) lei")
.fontWeight(.bold)
Text("\(men.ingredients.map({$0.grams}).reduce(0, +), specifier: "%.0f")g")
.font(.system(size: 16))
.fontWeight(.medium)
.foregroundColor(.gray)
}
Spacer()
}
Spacer()
Button {
coreDataViewModel.addTask(name: men.name, grams: men.ingredients.map({$0.grams}).reduce(0, +), price: Int((men.price)), id: men.id)
} label: {
Image(systemName: "plus")
.font(.title)
.foregroundColor(Color.onboardingColor)
}
.buttonStyle(PlainButtonStyle())
}.background(NavigationLink("", destination:
ItemMenuView(meniu: men)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
ToolbarButtons(numberOfProducts: menus.count)
}
}
).opacity(0)
)
}
}
} else {
EmptyView()
}
}
}
struct MeniuriView: View {
#EnvironmentObject var syncViewModel : SyncViewModel
var body: some View {
List {
ForEach(syncViewModel.menuType) { type in
SectionVieww(menuType: type)
}
}
.listStyle(PlainListStyle())
}
}
Put it conditionally, like
if let description = men.welcomeDescription {
Text(description)
.font(.caption)
.foregroundColor(.gray)
}

Using TextField hides the ScrollView beneath it in VStack

This view hold a list of pdf names which when tapped open webviews of pdf links.
The view has a search bar above the list which when tapped causes the scrollview to disappear.
struct AllPdfListView: View {
#Environment(\.presentationMode) var mode: Binding<PresentationMode>
#ObservedObject var pdfsFetcher = PDFsFetcher()
#State var searchString = ""
#State var backButtonHidden: Bool = false
#State private var width: CGFloat?
var body: some View {
GeometryReader { geo in
VStack(alignment: .leading, spacing: 1) {
HStack(alignment: .center) {
Image(systemName: "chevron.left")
Text("All PDFs")
.font(.largeTitle)
Spacer()
}
.padding(.leading)
.frame(width: geo.size.width, height: geo.size.height / 10, alignment: .leading)
.background(Color(uiColor: UIColor.systemGray4))
.onTapGesture {
self.mode.wrappedValue.dismiss()
}
HStack(alignment: .center) {
Image(systemName: "magnifyingglass")
.padding([.leading, .top, .bottom])
TextField ("Search All Documents", text: $searchString)
.textFieldStyle(PlainTextFieldStyle())
.autocapitalization(.none)
Image(systemName: "slider.horizontal.3")
.padding(.trailing)
}
.overlay(RoundedRectangle(cornerRadius: 10).stroke(.black, lineWidth: 1))
.padding([.leading, .top, .bottom])
.frame(width: geo.size.width / 1.05 )
ScrollView {
ForEach($searchString.wrappedValue == "" ? pdfsFetcher.pdfs :
pdfsFetcher.pdfs.filter({ pdf in
pdf.internalName.contains($searchString.wrappedValue.lowercased())
})
, id: \._id) { pdf in
if let parsedString = pdf.file?.split(separator: "-") {
let request = URLRequest(url: URL(string: "https://mylink/\(parsedString[1]).pdf")!)
NavigationLink(destination: WebView(request: request)
.navigationBarBackButtonHidden(backButtonHidden)
.navigationBarHidden(backButtonHidden)
.onTapGesture(perform: {
backButtonHidden.toggle()
})) {
HStack(alignment: .center) {
Image(systemName: "doc")
.padding()
.frame(width: width, alignment: .leading)
.lineLimit(1)
.alignmentGuide(.leading, computeValue: { dimension in
self.width = max(self.width ?? 0, dimension.width)
return dimension[.leading]
})
Text(pdf.internalName)
.padding()
.multilineTextAlignment(.leading)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
}
.padding(.leading)
}
}
}
.navigationBarHidden(true)
}
.accentColor(Color.black)
.onAppear{
pdfsFetcher.pdfs == [] ? pdfsFetcher.fetchPDFs() : nil
}
}
}
}
}
Pdf list and Searchbar.
The same view on Searchbar focus.
I would like the search string to filter the list of pdfs while maintaining the visibility of the list.
I was able to fix this by making my #ObservableObject an #EnvironmentObject in my App :
#main
struct MyApp: App {
#ObservedObject var pdfsFetcher = PDFsFetcher()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(pdfsFetcher)
}
}
}
struct AllPdfListView: View {
#EnvironmentObject var pdfsFetcher: PDFsFetcher
}

Adding a "Hamburger" Menu to IOS application

I am trying to create a hamburger menu that when you click the "hamburger" (three horizontal lines) button, the menu will slide out. I am following the tutorial found here, but the only thing that isn't working is the lines for the Hamburger image is not showing up on my application. Everything else works, but for some reason this is the one thing that is not working.
Here is my code for the ContentView, where it hosts the problem code
struct ContentView: View {
#State var showMenu = false
var body: some View {
let drag = DragGesture()
.onEnded {
if $0.translation.width < -100 {
withAnimation {
self.showMenu = false
}
}
}
return NavigationView {
GeometryReader { geometry in
ZStack(alignment: .leading) {
MainView(showMenu: self.$showMenu)
.frame(width: geometry.size.width, height: geometry.size.height)
.offset(x: self.showMenu ? geometry.size.width/2 : 0)
.disabled(self.showMenu ? true : false)
if self.showMenu {
MenuView()
.frame(width: geometry.size.width/2)
.transition(.move(edge: .leading))
}
}
.gesture(drag)
}
.navigationBarTitle("Side Menu", displayMode: .inline) // this works
//somewhere below here is the problem
.navigationBarItems(leading: (
Button(action: {
withAnimation {
self.showMenu.toggle()
}
}) {
Image(systemName: "three_horizontal_lines")
.imageScale(.large)
}
))
}
}
}
Here is the MainView:
struct MainView: View{
#Binding var showMenu: Bool
var body: some View{
Button(action: {
withAnimation{
self.showMenu = true
}
}){
Text("Show Menu")
}
}
}
Lastly, this is the MenuView:
struct MenuView: View{
var body: some View{
VStack(alignment: .leading){
HStack{
Image(systemName: "person")
.foregroundColor(.gray)
.imageScale(.large)
Text("Profile")
.foregroundColor(.gray)
.font(.headline)
}
.padding(.top, 100)
HStack{
Image(systemName: "envelope")
.foregroundColor(.gray)
.imageScale(.large)
Text("Messages")
.foregroundColor(.gray)
.font(.headline)
}
.padding(.top, 30)
HStack{
Image(systemName: "gear")
.foregroundColor(.gray)
.imageScale(.large)
Text("Settings")
.foregroundColor(.gray)
.font(.headline)
}
.padding(.top, 30)
Spacer()
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color(red: 32/255, green: 32/255, blue: 32/255))
.edgesIgnoringSafeArea(/*#START_MENU_TOKEN#*/.all/*#END_MENU_TOKEN#*/)
}
}
I have checked for any misspellings and even copied and pasted the code from the original tutorial, but it seems nothing allows me to see the burger image like is shown on the tutorial. Any thoughts on what else I could try?

How to expand Detail View to full screen with SwiftUI?

I have a list view embedded in a Navigation View, however, this Navigation View is only about the screen height. This list links to a detailed view, and when a row is tapped, the Detail View only takes up half of the screen. I would like it to open a completely new window.
Screenshots:
The code is used is the following:
import SwiftUI
import CoreData
extension UIScreen{
static let screenWidth = UIScreen.main.bounds.size.width
static let screenHeight = UIScreen.main.bounds.size.height
static let screenSize = UIScreen.main.bounds.size
}
let topCardHeight: CGFloat = 350
struct HomeView: View {
#Environment(\.managedObjectContext) var moc
#FetchRequest(entity: SavedPoem.entity(), sortDescriptors: []) var savedpoems : FetchedResults<SavedPoem>
var body: some View {
VStack {
VStack (alignment: .center){
Text("Today's Poem, November 18th...")
.font(.subheadline)
.foregroundColor(.white)
.padding(.bottom)
.padding(.top, 75)
Text("No Man Is An Island")
.font(.largeTitle)
.fontWeight(.heavy)
.foregroundColor(.white)
.padding(.bottom,1)
Text("by John Donne")
.font(.largeTitle)
.fontWeight(.heavy)
.foregroundColor(.white)
.padding(.bottom, 35)
Button(action: {}) {
Text("Read Now")
.fontWeight(/*#START_MENU_TOKEN#*/.bold/*#END_MENU_TOKEN#*/)
.font(.subheadline)
.foregroundColor(.white)
.padding(15)
.border(Color.white, width: 3)
}
}
.frame(width: UIScreen.screenWidth, height: topCardHeight, alignment: .top)
.background(Color.black)
.edgesIgnoringSafeArea(.top)
.edgesIgnoringSafeArea(.bottom)
.padding(.bottom, 0)
NavigationView{
List{
ForEach(savedpoems, id:\.title) {SavedPoem in
NavigationLink (destination: ContentView()){
ZStack {
Rectangle().fill(Color.white)
.frame(width: UIScreen.main.bounds.width - 32, height: 70)
.cornerRadius(10).shadow(color: .gray, radius: 4)
HStack {
VStack (alignment: .leading){
Text("\(SavedPoem.title ?? "")").font(.headline)
.lineLimit(1)
Text("\(SavedPoem.author ?? "")").font(.subheadline)
.foregroundColor(.secondary)
}
Spacer()
}.padding()
}
// }.onDelete(perform: remove)
}
}
}
.navigationTitle("My Saved Poems")
.navigationBarHidden(true)
.edgesIgnoringSafeArea(.top)
.padding(.top, 0)
}
}
// func remove(at offsets : IndexSet) {
// for index in offsets {
// let delete = SavedPoem[index]
// self.moc.delete(delete)
// }
// try? self.moc.save()
// }
}
Any ideas? Thanks in advance.
If you need the same UI:
(The navigation view at the bottom of your top view) , here is a solution for it .
var body: some View {
#EnvironmentObject var sharedViewModel : SharedViewModel
VStack {
VStack (alignment: .center){
if sharedViewModel.currentPageIsHome {
// your top view body here ..
}
}
NavigationView{\*....*\}
}
}.onAppear {
sharedViewModel.currentPageIsHome = true
}.onDisappear {
sharedViewModel.currentPageIsHome = false
}
And you need to create an Observable object
class SharedViewModel: ObservableObject {
#Published var currentPageIsHome = false
}
And don't forget to initialize it in your SceneDelegate
ContentView().environmentObject(SharedViewModel())
Or
Clear version :
change your view hierarchy to :
NavigationView {
List{
Section(header: YourTopView()) {
// ... your list content
}
}
}