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)
Related
How can I create a text field like the one shown in the figure? with background of that color and white line?
import SwiftUI
#available(iOS 16.0, *)
struct SecondView: View {
#State var nome = ""
#State var cognome = ""
var body: some View {
GeometryReader { geo in
ZStack {
Color(red: 57 / 255, green: 63 / 255, blue: 147 / 255)
Text("Ora tocca ai tuoi dati")
.font(Font.custom("Rubik", size: 22)).foregroundColor(Color.white).font(Font.body.bold()).padding(.bottom, 300).font(Font.headline.weight(.light))
Text("Per iniziare ad usare MyEntZo completa i campi qui sotto").multilineTextAlignment(.center)
.font(Font.custom("Rubik", size: 18)).foregroundColor(Color.white).font(Font.body.bold()).padding(.bottom, 80).font(Font.headline.weight(.light))
TextField("Nome", text: $nome, axis: .vertical)
.textFieldStyle(.roundedBorder)
.background(.ultraThinMaterial)
.padding().padding(.bottom, 20).padding(.top, 110).scaledToFill()
.underline()
TextField("Cognome", text: $nome, axis: .vertical)
.textFieldStyle(.roundedBorder)
.background(.ultraThinMaterial)
.padding().padding(.bottom, 20).padding(.top, 190).scaledToFill()
.underline()
Button(action: {
print("sign up bin tapped")
}){
Text("Continua")
.frame(minWidth: 0, maxWidth: .infinity)
.font(.system(size: 18))
.padding()
.foregroundColor(.white)
.overlay(
RoundedRectangle(cornerRadius: 25)
.stroke(Color.green, lineWidth: 2)
)
}
.background(Color.green)
.cornerRadius(25).frame(width: geo.size.width - 50, height: 100)
.padding(.top, 300)
}
}
.ignoresSafeArea()
}
}
struct SecondView_Previews: PreviewProvider {
static var previews: some View {
SecondView()
}
}
You can create a custom text field that wraps a regular TextField, passing in the #State as a #Binding, e.g.
struct CustomTextField: View {
let placeholder: String
#Binding var text: String
var body: some View {
VStack {
TextField(placeholder, text: $text)
.padding(.bottom, 4)
.foregroundColor(.white)
Color.white
.frame(height: 2)
}
.padding([.top, .bottom, .trailing])
.background(.blue)
}
}
which you can use like:
struct ContentView: View {
#State private var nome: String = ""
var body: some View {
VStack {
// etc
CustomTextField(placeholder: "Nome", text: $nome)
// etc
}
}
}
struct SecondView: View {
#State var nome = ""
#State var cognome = ""
var body: some View {
GeometryReader { geo in
ZStack {
Color(red: 57 / 255, green: 63 / 255, blue: 147 / 255)
VStack {
Spacer()
Text("Ora tocca ai tuoi dati")
.font(Font.custom("Rubik", size: 22))
.foregroundColor(.white)
.bold()
.padding()
.font(Font.headline.weight(.light))
Text("Per iniziare ad usare MyEntZo completa i campi qui sotto").multilineTextAlignment(.center)
.font(Font.custom("Rubik", size: 18)).foregroundColor(Color.white).font(Font.body.bold()).padding(.bottom, 80).font(Font.headline.weight(.light))
TextField("Nome", text: $nome, prompt: Text("Nome").foregroundColor(.white.opacity(0.7)))
.foregroundColor(.white)
.padding(.horizontal)
Rectangle()
.foregroundColor(.white)
.background(.white)
.frame(maxWidth: .infinity, maxHeight: 2)
.padding(.horizontal)
TextField("Cognome", text: $cognome, prompt: Text("Cognome").foregroundColor(.white.opacity(0.7)))
.foregroundColor(.white)
.padding([.horizontal, .top])
Rectangle()
.foregroundColor(.white)
.background(.white)
.frame(maxWidth: .infinity, maxHeight: 2)
.padding(.horizontal)
Button(action: {
print("sign up bin tapped")
}){
Text("Continua")
.frame(minWidth: 0, maxWidth: .infinity)
.font(.system(size: 18))
.padding()
.foregroundColor(.white)
.overlay(
RoundedRectangle(cornerRadius: 25)
.stroke(Color.green, lineWidth: 2)
)
}
.background(Color.green)
.cornerRadius(25).frame(width: geo.size.width - 50, height: 100)
.padding(.top)
Spacer()
}
}
}
.ignoresSafeArea()
}
}
I want to display a pop-up in my view, where I will be able to display a menu where I can choose how I fell, then it will show what I have chosen and close itself. If I am adding it to the view and it's presenting It shows wrong and Tab Bar does not disappear. Can someone provide a better way to show a pop-up over Tab Bar? The logic is something like this: button pressed -> shows pop-up -> choose status -> shows another pop-up -> disappears.
Code's provided below:
// Smile face that used in question
struct SmileFace: View {
var text: String
var image: String
#Binding var current: String
var body: some View {
Button {
withAnimation {
current = text
}
} label: {
VStack {
Image(image)
.resizable()
.scaledToFill()
.frame(width: 46, height: 46)
Text(text)
.foregroundColor(.white)
}
}
}
}
// Check in answer view
import SwiftUI
struct CheckInAskView: View {
let didClose: () -> Void
var didChose: Bool = false
#State var current: String
var emotions = [
"GREAT" : "good",
"GOOD" : "happy",
"OK" : "moderate",
"BAD" : "sad",
"TERRIBLE" : "verysad"
]
var body: some View {
VStack {
ZStack(alignment: .topLeading) {
Rectangle()
.fill(Color("navigation"))
.cornerRadius(15)
.frame(height: 601)
HStack(alignment: .top) {
ZStack(alignment: .top) {
Circle()
.fill(Color(red: 0.682, green: 0.384, blue: 0.486).opacity(0.10))
.frame(width: 173)
.offset(x: -173/2, y: -90/2)
.clipped()
}
Spacer()
Button {
didClose()
} label: {
ZStack {
Circle()
.fill(Color(red: 0.933, green: 0.933, blue: 0.933).opacity(0.30))
.frame(width: 24)
.clipped()
Image(systemName: "xmark")
.font(.system(size: 15))
.foregroundColor(Color(red: 0.762, green: 0.762, blue: 0.762))
}
.padding(10)
}
}
VStack(alignment: .center) {
Spacer()
Text("How do you feel now?")
.foregroundColor(.white)
.font(.custom("Manrope-Bold", size: 16))
HStack(spacing: 35) {
SmileFace(text: "GOOD", image: "good", current: $current)
}
.padding(.horizontal)
DoubleTextView(topText: "Recommendation for you", buttomText: "We have selected courses based on your goals and \nexperience", topTextSize: 16, buttomTextSize: 14)
BigFrameScrollViewHorizontal()
Spacer()
}
}
.frame(height: 601)
}
.frame(height: 601)
.transition(.move(edge: .bottom))
}
}
// View with all views
struct CheckInView: View {
#StateObject var sheetManager: SheetManager
var body: some View {
VStack {
HStack {
Text("How do you fell now?")
.foregroundColor(.white)
.font(.custom("Manrope-Bold", size: 16))
Spacer()
Button {
} label: {
ZStack {
HStack {
Text("Pass check in")
.padding([.top, .leading, .bottom])
.foregroundColor(.white)
.font(.custom("Manrope-Medium", size: 12))
Image(systemName: "chevron.right")
.foregroundColor(.white)
.font(.system(size: 15))
.padding(.trailing)
}
}
.background(Rectangle()
.fill(Color("active"))
.cornerRadius(100)
.frame(height: 26))
}
}
VStack {
Divider()
.background(Color("inactive"))
.padding(.vertical)
Divider()
.background(Color("inactive"))
.padding(.vertical)
Divider()
.background(Color("inactive"))
.padding(.vertical)
Divider()
.background(Color("inactive"))
.padding(.vertical)
Divider()
.background(Color("inactive"))
.padding(.vertical)
Divider()
.background(Color("inactive"))
.padding(.vertical)
}
HStack {
Button {
} label: {
ZStack {
Circle()
.fill(Color("navigation"))
.frame(width: 26)
Image(systemName: "chevron.left")
.foregroundColor(.white)
.font(.system(size: 14))
}
}
Button {
} label: {
ZStack {
Circle()
.fill(Color("navigation"))
.frame(width: 26)
Image(systemName: "chevron.right")
.foregroundColor(Color("inactive"))
.font(.system(size: 14))
}
}
}
.padding(.top)
}
.padding(.horizontal, 15.0)
}
}
struct CheckInView_Previews: PreviewProvider {
static var previews: some View {
CheckInView(sheetManager: SheetManager())
.background(.yellow)
}
}
If I have all of this, how can I create a pop-up over Tab View? Maybe a link to a similar problem?
I am new to coding and am trying to make a notes app come to life, the issue is I have no idea how to make separate text editors in my for loops.
This is my code. When you run the project and create a green sticky note and type on it, it works fine, but if you do a second one of the same color, they have the same text. How do I fix this in a way that doesn't take hours of tedious work?
I have tried to use different ways to make a for loop. I have made one with normal lists and with a struct list that has different ids, both come up with the same text editor.
(Text editors don't copy over color, which makes me think it's the for loop because when I tried to use different variables for the bindings it didn't work either.)
//
// ContentView.swift
// Notesapp
//
//
import SwiftUI
import Foundation
struct newBoy: Identifiable {
let id = UUID()
let number: Int
}
class NewBoys: ObservableObject {
#Published var numbs = [newBoy]()
}
struct ContentView: View {
#StateObject var numbers1 = NewBoys()
#State var buttonoff = true
#State var recaddor: [GridItem] = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible()),
]
#State var glist = [Int]()
#State var i = -1
#State var gtext = "This be me"
#State var blist = [Int]()
#State var bman = -1000
#State var btext = "Enter your notes here!"
#State var bklist = [Int]()
#State var bkman = -2000
#State var bktext = "Enter your notes here!"
#State var ylist = [Int]()
#State var yman = -3000
#State var ytext = "Enter your notes here!"
#State var olist = [Int]()
#State var oman = -4000
#State var otext = "Enter your notes here!"
#State var plist = [Int]()
#State var pman = -5000
#State var ptext = "Enter your notes here!"
var body: some View {
HStack{
VStack {
Text("Make new note!").padding().foregroundColor(Color.black)
Button {
withAnimation(.easeIn) {
self.buttonoff.toggle()
}
} label: {
Image(systemName: "plus")
.font(.title2)
.foregroundColor(.white)
.rotationEffect(.init(degrees: buttonoff ? 0 : 45))
.scaleEffect(buttonoff ? 1 : 1.3)
.padding()
}.buttonStyle(PlainButtonStyle())
.background(Color.black)
.clipShape(Circle())
.padding()
if buttonoff {
}
else {
Group {
Button {
recGreen()
} label: {
RoundedRectangle(cornerRadius: 25)
.fill(Color.green)
.frame(width: 30, height: 30)
}.buttonStyle(PlainButtonStyle())
Button {
recBlue()
} label: {
RoundedRectangle(cornerRadius: 25)
.fill(Color.blue)
.frame(width: 30, height: 30)
}.buttonStyle(PlainButtonStyle())
Button {
recBlack()
} label: {
RoundedRectangle(cornerRadius: 25)
.fill(Color.black)
.frame(width: 30, height: 30)
}.buttonStyle(PlainButtonStyle())
Button {
recYellow()
} label: {
RoundedRectangle(cornerRadius: 25)
.fill(Color.yellow)
.frame(width: 30, height: 30)
}.buttonStyle(PlainButtonStyle())
Button {
recOrange()
} label: {
RoundedRectangle(cornerRadius: 25)
.fill(Color.orange)
.frame(width: 30, height: 30)
}.buttonStyle(PlainButtonStyle())
Button {
recPurple()
} label: {
RoundedRectangle(cornerRadius: 25)
.fill(Color.purple)
.frame(width: 30, height: 30)
}.buttonStyle(PlainButtonStyle())
}.padding(20)
.scaleEffect(1.5)
}
}.frame(width: 100, height: 700, alignment: .top)
.background(Color.white)
.border(Color.gray, width: 2)
VStack {
ScrollView{
LazyVGrid(columns: recaddor){
ForEach(numbers1.numbs, id: \.id) {o in
ZStack{
RoundedRectangle(cornerRadius: 25)
.fill(Color.green)
.frame(width: 250, height: 200)
.padding()
VStack{
HStack{
Spacer()
Button {
print("hi")
} label: {
Image(systemName: "minus")
}
.padding(.top, 25.0)
.padding(.trailing, 30.0)
.frame(alignment:.trailing)
}
TextEditor(text: $gtext)
.frame(width: 225, height: 150, alignment: .center)
.cornerRadius(3.0)
.colorMultiply(.green)
Spacer()
}
}
}
ForEach(blist, id: \.self) {blueman in
ZStack{
RoundedRectangle(cornerRadius: 25)
.fill(Color.blue)
.frame(width: 250, height: 200)
.padding()
VStack{
HStack{
Spacer()
Button {
blist = blist.filter({ Int in
return Int != blueman
})
} label: {
Image(systemName: "minus")
}
.padding(.top, 25.0)
.padding(.trailing, 30.0)
.frame(alignment:.trailing)
}
TextEditor(text: $btext)
Spacer()
}
}
}
ForEach(bklist, id: \.self) {bklueman in
ZStack{
RoundedRectangle(cornerRadius: 25)
.fill(Color.black)
.frame(width: 250, height: 200)
.padding()
VStack{
HStack{
Spacer()
Button {
bklist = bklist.filter({ Int in
return Int != bklueman
})
} label: {
Image(systemName: "minus")
}
.padding(.top, 25.0)
.padding(.trailing, 30.0)
.frame(alignment:.trailing)
}
TextEditor(text: $bktext)
Spacer()
}
}
}
ForEach(ylist, id: \.self) {ylueman in
ZStack{
RoundedRectangle(cornerRadius: 25)
.fill(Color.yellow)
.frame(width: 250, height: 200)
.padding()
VStack{
HStack{
Spacer()
Button {
ylist = ylist.filter({ Int in
return Int != ylueman
})
} label: {
Image(systemName: "minus")
}
.padding(.top, 25.0)
.padding(.trailing, 30.0)
.frame(alignment:.trailing)
}
TextEditor(text: $ytext)
Spacer()
}
}
}
ForEach(olist, id: \.self) {olueman in
ZStack{
RoundedRectangle(cornerRadius: 25)
.fill(Color.orange)
.frame(width: 250, height: 200)
.padding()
VStack{
HStack{
Spacer()
Button {
olist = olist.filter({ Int in
return Int != olueman
})
} label: {
Image(systemName: "minus")
}
.padding(.top, 25.0)
.padding(.trailing, 30.0)
.frame(alignment:.trailing)
}
TextEditor(text: $otext)
Spacer()
}
}
}
ForEach(plist, id: \.self) {plueman in
ZStack{
RoundedRectangle(cornerRadius: 25)
.fill(Color.purple)
.frame(width: 250, height: 200)
.padding()
VStack{
HStack{
Spacer()
Button {
plist = plist.filter({ Int in
return Int != plueman
})
} label: {
Image(systemName: "minus")
}
.padding(.top, 25.0)
.padding(.trailing, 30.0)
.frame(alignment:.trailing)
}
TextEditor(text: $ptext)
Spacer()
}
}
}
}
}
}.frame(width: 1100)
}
}
func recGreen() {
let i = newBoy(number: 0)
numbers1.numbs.append(i)
print(numbers1.numbs)
}
func recBlue() {
bman += 1
blist.append(bman)
}
func recBlack() {
bkman += 1
bklist.append(bkman)
}
func recYellow() {
yman += 1
ylist.append(yman)
}
func recOrange() {
oman += 1
olist.append(oman)
}
func recPurple() {
pman += 1
plist.append(pman)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
It's because you are using the same binding.
In your ForEach, you are creating multiple TextEditors but they are editing the same variable like $otext. You need to create a struct that holds the text, and the array you use in the ForEach should be of type YourStruct. Then you pass the text to TextEditors.
struct Note: Identifiable {
var id: Int //your current array type, i.e if your array is [Int] use Int.
var text: String //this is the text to pass to the editors
}
Hello I apologize for my ignorance if this is an overdone or overly simple question. I am using SwiftUI and want my page to have multiple buttons that all send emails with different subject and message bodies. Everything currently works although only after the second time I click a button. The first time the subject is my initialized var ("start") regardless of button.
Another small thing, when I click the same button repeatedly it never updates until I click a different one.
Thank you very much, attached is code, I left out my mailcompose swift file as it all works as expected.
import SwiftUI
import MessageUI
import Foundation
struct ContentView: View {
#State var result: Result<MFMailComposeResult, Error>? = nil
#State private var showSheet = false
#State private var subject: String = "start"
#State private var msgBody: String = ""
let numberString = "201-228-0752"
var body: some View {
ZStack{
Color.white.ignoresSafeArea()
VStack{
Image("Icon-1024")
.resizable()
.aspectRatio(contentMode: .fit)
.clipShape(Circle())
.shadow(radius: 10)
.overlay(Circle().stroke(Color.black, lineWidth: 3))
.frame(width: 50, height: 50, alignment: .center)
.padding(.top, 10)
Text("Contact Us")
.foregroundColor(.black)
.fontWeight(.heavy)
.font(.title)
.padding()
Text("Be part of something bigger.")
.foregroundColor(.black)
.fontWeight(.medium)
.font(.system(size: 20))
.padding()
.padding(.bottom, 5)
.multilineTextAlignment(.center)
Spacer()
VStack(spacing: 0){
Button(action: {
self.subject = "General"
self.msgBody = "Hello, I need help."
self.suggestFeature()
}){
HStack {
Spacer()
Text("General help")
.font(.system(size: 25))
.fontWeight(.medium)
.foregroundColor(.white)
.padding(.top, 15)
.padding(.bottom, 15)
Spacer()
}
}
.background(Color(red: 1.00, green: 0.49, blue: 0.51))
Button(action: {
self.subject = "Technical"
self.msgBody = "Hello, I am having technical difficulties"
self.suggestFeature()
}){
HStack {
Spacer()
Text("Technical issues")
.font(.system(size: 25))
.fontWeight(.medium)
.foregroundColor(.white)
.padding(.top, 15)
.padding(.bottom, 15)
Spacer()
}
}
.background(Color(red: 0.81, green: 0.39, blue: 0.40))
Button(action: {
self.subject = "Gender"
self.msgBody = "Hello, I would like to make a gender or sexuality request."
self.suggestFeature()
}){
HStack {
Spacer()
Text("Gender & Sexuality")
.font(.system(size: 25))
.fontWeight(.medium)
.foregroundColor(.white)
.padding(.top, 15)
.padding(.bottom, 15)
Spacer()
}
}
.background(Color(red: 0.62, green: 0.29, blue: 0.30))
Button(action: {
self.subject = "Delete"
self.msgBody = "Hello, I would like to request a delete of my info."
self.suggestFeature()
}){
HStack {
Spacer()
Text("Delete info")
.font(.system(size: 25))
.fontWeight(.medium)
.foregroundColor(.white)
.padding(.top, 15)
.padding(.bottom, 15)
Spacer()
}
}
.background(Color(red: 0.45, green: 0.19, blue: 0.20))
Button(action: {
let telephone = "tel://"
let formattedString = telephone + numberString
guard let url = URL(string: formattedString) else { return }
UIApplication.shared.open(url)
}){
HStack {
Spacer()
(Text("Call ") + Text(Image(systemName: "phone.fill")))
.font(.system(size: 25))
.fontWeight(.medium)
.foregroundColor(.white)
.padding(.top, 15)
.padding(.bottom, 15)
Spacer()
}
}
.background(Color(.black))
Spacer()
Text("© Copyright Sixish, Inc.")
.padding(.bottom, 30)
.foregroundColor(.gray)
}.sheet(isPresented: $showSheet) {
MailView(result: self.$result, newSubject: self.subject, newMsgBody: self.msgBody)
}
}
}
}
func suggestFeature() {
print("You've got mail")
if MFMailComposeViewController.canSendMail() {
self.showSheet = true
} else {
print("Error sending mail")
// Alert : Unable to send the mail
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
This is a classic issue that gets run into a lot with sheet() on SwiftUI. The gist is that your sheet's content view can get rendered before it actually appears and before the #State variables' changes propagate to it.
The fix is to use sheet(item:):
struct Message : Identifiable, Hashable {
var id = UUID()
var subject : String
var body : String
}
struct ContentView: View {
//#State var result: Result<MFMailComposeResult, Error>? = nil
#State private var showSheet = false
#State private var message : Message?
let numberString = "201-228-0752"
var body: some View {
ZStack{
Color.white.ignoresSafeArea()
VStack{
Image("Icon-1024")
.resizable()
.aspectRatio(contentMode: .fit)
.clipShape(Circle())
.shadow(radius: 10)
.overlay(Circle().stroke(Color.black, lineWidth: 3))
.frame(width: 50, height: 50, alignment: .center)
.padding(.top, 10)
Text("Contact Us")
.foregroundColor(.black)
.fontWeight(.heavy)
.font(.title)
.padding()
Text("Be part of something bigger.")
.foregroundColor(.black)
.fontWeight(.medium)
.font(.system(size: 20))
.padding()
.padding(.bottom, 5)
.multilineTextAlignment(.center)
Spacer()
VStack(spacing: 0){
Button(action: {
self.message = Message(subject: "General", body: "Hello, I need help.")
self.suggestFeature()
}){
HStack {
Spacer()
Text("General help")
.font(.system(size: 25))
.fontWeight(.medium)
.foregroundColor(.white)
.padding(.top, 15)
.padding(.bottom, 15)
Spacer()
}
}
.background(Color(red: 1.00, green: 0.49, blue: 0.51))
Button(action: {
self.message = Message(subject: "Technical", body: "Hello, I am having technical difficulties")
self.suggestFeature()
}){
HStack {
Spacer()
Text("Technical issues")
.font(.system(size: 25))
.fontWeight(.medium)
.foregroundColor(.white)
.padding(.top, 15)
.padding(.bottom, 15)
Spacer()
}
}
.background(Color(red: 0.81, green: 0.39, blue: 0.40))
Button(action: {
self.message = Message(subject: "Gender", body: "Hello, I would like to make a gender or sexuality request.")
self.suggestFeature()
}){
HStack {
Spacer()
Text("Gender & Sexuality")
.font(.system(size: 25))
.fontWeight(.medium)
.foregroundColor(.white)
.padding(.top, 15)
.padding(.bottom, 15)
Spacer()
}
}
.background(Color(red: 0.62, green: 0.29, blue: 0.30))
Button(action: {
self.message = Message(subject: "Delete", body: "Hello, I would like to request a delete of my info.")
self.suggestFeature()
}){
HStack {
Spacer()
Text("Delete info")
.font(.system(size: 25))
.fontWeight(.medium)
.foregroundColor(.white)
.padding(.top, 15)
.padding(.bottom, 15)
Spacer()
}
}
.background(Color(red: 0.45, green: 0.19, blue: 0.20))
Button(action: {
let telephone = "tel://"
let formattedString = telephone + numberString
guard let url = URL(string: formattedString) else { return }
UIApplication.shared.open(url)
}){
HStack {
Spacer()
(Text("Call ") + Text(Image(systemName: "phone.fill")))
.font(.system(size: 25))
.fontWeight(.medium)
.foregroundColor(.white)
.padding(.top, 15)
.padding(.bottom, 15)
Spacer()
}
}
.background(Color(.black))
Spacer()
Text("© Copyright Sixish, Inc.")
.padding(.bottom, 30)
.foregroundColor(.gray)
}.sheet(item: $message) { item in
MailView(message: item)
//your code would probably need to be more like MailView(result: self.$result, newSubject: item.subject, newMsgBody: item.body)
}
}
}
}
func suggestFeature() {
print("You've got mail")
if MFMailComposeViewController.canSendMail() {
self.showSheet = true
} else {
print("Error sending mail")
// Alert : Unable to send the mail
}
}
}
struct MailView : View {
var message : Message
var body: some View {
Text(message.subject)
Text(message.body)
}
}
Note that now, your sheet is displayed if message has an item it it. Message is now a struct.
I simplified things a little bit since I didn't have your MailView container (and I temporarily commented out your result property), but the concept presented here should get you started doing what you need to do.
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")
}
}