#State Variable Not Updating When Passed Through Binding Variable SwiftUI - binding

So I am trying to pass a variable from one view to another using Bindings. When I tap on the ExpenseRow, it sets the index to the expenseArrayIndex State variable and prints the proper index. However when I print the variable in the view I am passing it to, it shows up as the default value of -1 (or if I initialize it as an Int? it is nil in the other view).
Also if I have multiple object in my m_expensesArray, once I click on the second object, all the indices will be transferred to the second view there on out fine. So I'm fairly certain it is something with the initialization but not sure exactly what and I am all out of ideas.
My Main ExpenseView struct where I set the expenseArrayIndex = to the index of the expense object I tapped on
struct ExpenseView: View {
#ObservedObject var gloablBudgetData: BudgetData = BudgetData.shared
#State var isModalA: Bool = false
#State var isModalE: Bool = false
#State var expenseArrayIndex: Int = -1
#State var activeSheet: ActiveSheet? = nil
var body: some View {
VStack (spacing: 0)
{
Text("ADD EXPENSE")
.font(.title3)
.fontWeight(.bold)
.foregroundColor(Color.white)
.multilineTextAlignment(.center)
.padding(5.0)
.background(darkGreen)
.onTapGesture(count: 1, perform: {
activeSheet = .addExpense
})
Spacer()
List
{
Section(header: ListHeader())
{
ForEach(gloablBudgetData.m_expensesArray.indices, id: \.self) { i in
VStack(spacing: 0){
ExpenseRow(expense: gloablBudgetData.m_expensesArray[i])
.padding(.all, 5.0)
.onTapGesture(count: 1, perform: {
print("Expense Index: " + String(i))
expenseArrayIndex = i
activeSheet = .editExpense
print("Var index: ", expenseArrayIndex)
})
Divider()
.frame(height: 1)
.background(Color.black)
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
.listRowInsets(EdgeInsets())
.background(Color.white)
}
}
}
.frame(width: 300, height: 450, alignment: .center)
.cornerRadius(30)
.shadow(radius: 50)
}
.fullScreenCover(item: $activeSheet) { item in
switch item {
case .addExpense:
AddExpenseView(activeSheet: $activeSheet)
case .editExpense:
EditExpenseView(activeSheet: $activeSheet, expenseArrayIndex: $expenseArrayIndex)
}
}
}
}
My EditExpenseView that I am trying to pass the expenseArrayIndex into. Here is where the value comes out as -1 or nil
struct EditExpenseView: View {
#State private var expenseName: String = ""
#State private var expenseDate: String = ""
#State private var expenseCategory: String = ""
#State private var expenseAmount: String = ""
#State private var validExpenseName: Bool = true
#State private var validExpenseDate: Bool = true
#State private var validExpenseCategory: Bool = true
#State private var validExpenseAmount: Bool = true
#ObservedObject var gloablBudgetData: BudgetData = BudgetData.shared
#Binding var m_activeSheet: ActiveSheet?
#Binding var m_expenseArrayIndex: Int
init(activeSheet: Binding<ActiveSheet?>, expenseArrayIndex: Binding<Int>)
{
self._m_activeSheet = activeSheet
self._m_expenseArrayIndex = expenseArrayIndex
print("Index: ", m_expenseArrayIndex)
}
I am still fairly new to SwiftUI and new to posting on stack overflow so sorry if It isn't formatted entirely right

Related

Swift List row background color not changing

I am working on minimizable side menu with collapsable sub menus in swift. Currently both the minimizing and collapsable submenu components are working. I have spent all evening trying to set the background color of the menu items. As it stands listRowBackground does nothing. I have come across other stack overflow (and other websites) posts talking about this. Some say to use for each loops. Below I have another SideMenu struct using a for each loop. This loop moves the side menu to the center of the screen. Can someone help me with either of these approaches to keep the menu on the left hand side of the screen and change the row background color?
Any help is appreciated!
Edit: I have also updated Xcode to version 14, but it did not help.
import SwiftUI
struct SideMenu: View {
let width: CGFloat
#Binding var menuOpened: Bool
var items: [Menu] = [.item0, .a, .b, .c]
var body: some View {
ZStack {
VStack(alignment: .leading){
HStack{
List(items, children: \.items){ row in
HStack{
if row.imageName != ""{
let image = Image(systemName: row.imageName)
image.resizable()
.aspectRatio(contentMode: .fit)
.foregroundColor(.black)
.frame(width: 32, height: 32, alignment: .center)
}else{
Text(row.name).listRowBackground(Color.orange)
}
}.listRowBackground(Color.orange)
.onTapGesture {
if row.imageName == "line.horizontal.3"{
menuOpened.toggle()
}
}
}
.background(Color.red)
.listStyle(.plain)
.frame(width: menuOpened ? 275 : 80)
Spacer()
}
}
}
}
}
struct Menu: Identifiable{
let id = UUID()
var name = ""
var imageName = ""
var items: [Menu]?
}
struct ContentView: View {
#State var menuOpened = true
var body: some View {
SideMenu(width: UIScreen.main.bounds.width/1.6, menuOpened: $menuOpened)
}
}
extension Menu{
static var item1 = Menu(name: "a1")
static var item2 = Menu(name: "a2")
static var item3 = Menu(name: "a3")
static var item0 = Menu(imageName: "line.horizontal.3")
static var a = Menu(name: "a", items: [.item1, .item2, item3])
static var b = Menu(name: "b")
static var c = Menu(name: "Settings")
}
Here is a struct with a for each loop
struct SideMenu: View {
let width: CGFloat
#Binding var menuOpened: Bool
var items: [Menu] = [.item0, .a, .b, .c]
var body: some View {
ZStack {
VStack(alignment: .leading){
HStack{
List {
ForEach(items) { row in
HStack{
if row.imageName != ""{
let image = Image(systemName: row.imageName)
image.resizable()
.aspectRatio(contentMode: .fit)
.foregroundColor(.black)
.frame(width: 32, height: 32, alignment: .center)
}else{
Text(row.name).listRowBackground(Color.orange)
}
}.listRowBackground(Color.orange)
.onTapGesture {
if row.imageName == "line.horizontal.3"{
menuOpened.toggle()
}
}
}
.background(Color.red)
.listStyle(.plain)
.frame(width: menuOpened ? 275 : 80)
Spacer()
}
}
}
}
}
}

SwiftUI - MagnificationGesture sometimes work most of the time it doesn't work on a view

So I have made a crossword puzzel. That works fine.
But I want to zoom in and out on the crossword puzzel.
Now I am using a MagnificationGesture on the complete view. That works only sometimes.
But most of the times it doesn't work on both simulator as a real iPhone.
I tried using the MagnificationGesture on the scrollview, but that zooms out on the whole scrollview (so you get a small screen) and the text on the left shouldn't zoom in or out, but even if I put it on the scrollview, it still works only sometimes.
Is there something I am doing wrong?
Can someone help me point me in the right direction to fix it?
The strange thing is dat is does work sometimes.
struct CrosswordPuzzelView: View {
#State var currentScale: CGFloat = 0.7
#State var finalScale: CGFloat = 1
#FocusState private var nameIsFocused: Bool
#State var currentAmount: CGFloat = 0
#State var lastAmount: CGFloat = 0
#Binding var Dader11: Bool
#Binding var Dader22: Bool
#Binding var Dader3: Bool
#Binding var Dader4: Bool
#Binding var Dader5: Bool
#Binding var Wapen1: Bool
#Binding var Wapen2: Bool
#Binding var Wapen3: Bool
#Binding var Wapen4: Bool
#Binding var Wapen5: Bool
#Binding var Handlanger1: Bool
#Binding var Handlanger2: Bool
#Binding var Handlanger3: Bool
#Binding var Handlanger4: Bool
#Binding var Handlanger5: Bool
#Binding var Schuilplaats1: Bool
#Binding var Schuilplaats2: Bool
#Binding var Schuilplaats3: Bool
#Binding var Schuilplaats4: Bool
#Binding var Schuilplaats5: Bool
#Binding var Antwoord: String
#State var isLinkActive = false
var body: some View {
ZStack {
Image("Achtergrond")
.resizable()
.ignoresSafeArea()
VStack {
Text("")
.frame(height: 50)
VStack{
ScrollViewReader { sp in
ScrollView([.horizontal,.vertical], showsIndicators: false) {
VStack (alignment: .leading, spacing: 1){
VStack(alignment: .leading) {
Text("")
.frame(height:10)
Text("Verticaal:")
.font(.title)
.foregroundColor(.black)
.padding(.horizontal)
Text("1. Iemand die zijn godsdienst verspreidt\n2. Een kronkelende rivier\n3. Stroomversnelling die zuurstof toevoegt aan het water\n4. Witte grazers op de hei\n5. Bijzondere oude boom\n10. Watertje waar reptielen leven\n11. Roofdier dat weer terug is in Nederland")
.font(.headline)
.foregroundColor(.black)
// .frame(width: 350)
.padding(.horizontal)
.padding(.bottom)
Text("Horizontaal:")
.font(.title)
.foregroundColor(.black)
.padding(.horizontal)
Text("4. Waterstroompje afkomstig uit grondwater\n6. Wegovergang voor fauna\n7. Slecht waterdoorlatende bestrating\n8. Heeft sponsfunctie en ligt op de grond\n9. Toren die water opslaat\n10. Natuurgebied in Gelderland\n12. Bloeiend paars struikje ")
.font(.headline)
.foregroundColor(.black)
//.frame(width: 350)
.padding(.horizontal)
NavigationLink {
Raadsel3(Dader11: $Dader11, Dader22: $Dader22, Dader3: $Dader3, Dader4: $Dader4, Dader5: $Dader5, Wapen1: $Wapen1, Wapen2: $Wapen2, Wapen3: $Wapen3, Wapen4: $Wapen4, Wapen5: $Wapen5, Handlanger1: $Handlanger1, Handlanger2: $Handlanger2, Handlanger3: $Handlanger3, Handlanger4: $Handlanger4, Handlanger5: $Handlanger5, Schuilplaats1: $Schuilplaats1, Schuilplaats2: $Schuilplaats2, Schuilplaats3: $Schuilplaats3, Schuilplaats4: $Schuilplaats4, Schuilplaats5: $Schuilplaats5, Antwoord: $Antwoord)
} label: {
Text("Beantwoord het raadsel")
.padding()
.background(Color(red: 0.13, green: 0.505, blue: 0.261))
.foregroundColor(.white)
.cornerRadius(10)
.padding()
}.buttonStyle(ThemeAnimationStyle())
Text("Scroll om de puzzel te zien")
.foregroundColor(.black)
.font(.headline)
.italic()
.bold()
.padding(.horizontal)
}
CrossWordBlock()
.focused($nameIsFocused)
.scaleEffect(0.6 + currentAmount + lastAmount)
.gesture(
MagnificationGesture()
.onChanged{ value in
currentAmount = value - 1
}
.onEnded{ value in
lastAmount += currentAmount
currentAmount = 0
}
)
}
.id("root")
}
.onAppear {
sp.scrollTo("root", anchor: .topLeading)
}
}
Button {
nameIsFocused = false
} label: {
Text("Minimalizeer Toetsenbord")
.padding()
.font(.subheadline)
.background(Color(red: 0.13, green: 0.505, blue: 0.261))
.foregroundColor(Color.white)
.cornerRadius(10)
}.buttonStyle(ThemeAnimationStyle())
Text("")
}
} //.navigationBarTitle(
// Text("")
// , displayMode: .inline)
.navigationBarItems(trailing:
Image("Logo")
.resizable()
.foregroundColor(.white)
.aspectRatio(contentMode: .fit)
.frame(width: 60, height: 60, alignment: .center)
)
}
}
}
And the Crosswordblock view. I tried to delete the onTabGesture but that didn't solve the issue.
import SwiftUI
func generateRepeatStringArray(size: Int, string: String) -> [String] {
var array = [String]()
for _ in 0 ..< size {
array.append(string)
}
return array
}
struct CrossWordBlock: View {
#State var blockText = generateRepeatStringArray(size: 100, string: " ")
#State var selectedIndex = 0
#State var currentAmount: CGFloat = 0
#State var lastAmount: CGFloat = 0
var selectedBinding: Binding<String> {
Binding<String>(
get: {
blockText[selectedIndex]
},
set: {
blockText[selectedIndex] = $0
}
)
}
var body: some View {
ZStack{
CrosswordKeyboard(text: selectedBinding)
VStack{
// Here is the code for the crossword, createBlock(0) - createBlock(100) but it was to long for this textfield,
}
}
}
func txtBind(index: Int) -> Binding<String> {
Binding<String>(
get: {
blockText[index]
},
set: {
blockText[index] = $0
}
)
}
func createBlock(_ index: Int, tip: Int = -1) -> CrosswordBlock {
CrosswordBlock(index: index, selectedIndex: $selectedIndex, text: txtBind(index: index))
}
func createEmptyBlock(){
}
}
struct CrosswordBlock: View {
var index: Int
var tip = -1
#Binding var selectedIndex: Int
#Binding var text: String
#FocusState private var nameIsFocused: Bool
var body: some View {
VStack {
HStack{
Text(tip == -1 ? " " : String(tip))
.font(.caption)
.position()
Spacer()
}
Text(text)
.font(.headline)
.fontWeight(.bold)
.focused($nameIsFocused)
}
.padding()
.foregroundColor(.black)
.frame(width: 50, height: 50)
.background(selectedIndex == index ? Color(red: 0.737, green: 0.822, blue: 0.015) : Color.white)
.onTapGesture {
withAnimation{
selectedIndex = index
}
}
}
}

SwiftUI Get Coordinates of TextField or any View

I'm struggling to find a way in SwiftUI to get the x,y coordinates of the origin of a
TextField (or any view). I can certainly provide a position or offset to move the view
but I can't seem to find a way to get a reference to the TextField to get its
coordinates.
In UIKit, I believe I would use myTextField.frame.origin.x
This is a very simple example:
struct ContentView: View {
#State private var someNumber1 = "1000"
#State private var someNumber2 = "2000"
//bunch more
#State private var enteredNumber = "Some Text at the Top"
#State var value: CGFloat = 0
var body: some View {
ScrollView {
VStack {
Spacer()
Text("\(enteredNumber)")
Spacer()
Group { //1
TextField("Placeholder", text: $someNumber1)
.keyboardType(.default)
.textFieldStyle(RoundedBorderTextFieldStyle())
//this does not work
.onTapGesture {
let aY = (textfieldreferencehere).frame.origin.y
}
TextField("Placeholder", text: $someNumber2)
.keyboardType(.default)
.textFieldStyle(RoundedBorderTextFieldStyle())
}//group 1
//bunch more
Button(action: {
self.enteredNumber = self.someNumber1
self.someNumber1 = ""
UIApplication.shared.endEditing()
}) {
Text("Submit")
}
.padding(.bottom, 50)
}//outer v
.padding(.horizontal, 16)
.padding(.top, 44)
}//Scrollview or Form
.modifier(AdaptsToSoftwareKeyboard())
}
}
Any Guidance would be appreciated. Xcode 11.4.1
Here is a demo of how specific view coordinates can be read (see helpful comments inline)
struct DemoReadViewOrigin: View {
#State private var someNumber1 = "1000"
#State private var someNumber2 = "2000"
//bunch more
#State private var enteredNumber = "Some Text at the Top"
#State var value: CGFloat = 0
var body: some View {
ScrollView {
VStack {
Spacer()
Text("\(enteredNumber)")
Spacer()
Group { //1
TextField("Placeholder", text: $someNumber1)
.keyboardType(.default)
.textFieldStyle(RoundedBorderTextFieldStyle())
.background(GeometryReader { gp -> Color in
let rect = gp.frame(in: .named("OuterV")) // < in specific container
// let rect = gp.frame(in: .global) // < in window
// let rect = gp.frame(in: .local) // < own bounds
print("Origin: \(rect.origin)")
return Color.clear
})
//this does not work
.onTapGesture {
}
TextField("Placeholder", text: $someNumber2)
.keyboardType(.default)
.textFieldStyle(RoundedBorderTextFieldStyle())
}//group 1
//bunch more
Button(action: {
self.enteredNumber = self.someNumber1
self.someNumber1 = ""
// UIApplication.shared.endEditing()
}) {
Text("Submit")
}
.padding(.bottom, 50)
}//outer v
.coordinateSpace(name: "OuterV") // << declare coord space
.padding(.horizontal, 16)
.padding(.top, 44)
}//Scrollview or Form
// .modifier(AdaptsToSoftwareKeyboard())
}
}

How to make VStack fill the width of screen in SwiftUI

I'm trying to make VStack Sample app which visualize VStack Properties
but my VStack couldn't fill the width of screen
I tried many solutions on internet (frame modifier, HStack with Spacer, GeometryReader) but they were not work
This is my code
Parent View
struct LayoutView: View {
#State private var spacing: CGFloat = 0.0
#State private var alignmentIndex = 0
#State private var elementsAmount = 0
private let layout: StackLayout
private let alignments: [String] = [".leading", ".center", ".trailing"]
private let minValue: CGFloat = 0.0
private let maxValue: CGFloat = 100.0
init(_ layout: StackLayout) {
self.layout = layout
}
var body: some View {
NavigationView {
Form {
Section(header: Text("Controls")) {
VStack(alignment: .leading) {
Text("Spacing: \(Int(spacing))").font(.caption)
HStack {
Text("\(Int(minValue))")
Slider(value: $spacing, in: minValue...maxValue, step: 1)
Text("\(Int(maxValue))")
}
}
Picker("alignments", selection: self.$alignmentIndex) {
ForEach(0..<alignments.count) {
Text(self.alignments[$0])
}
}
.pickerStyle(SegmentedPickerStyle())
Stepper("Element's amount: \(elementsAmount)", value: self.$elementsAmount, in: 0...10)
}
Section(header: Text("Canvas")) {
VStackView(spacing: $spacing, alignmentIndex: $alignmentIndex, elementsCount: $elementsAmount)
}
}
.navigationBarTitle(Text(layout.rawValue), displayMode: .inline)
}
}
}
Child View
struct VStackView: View {
#Binding var spacing: CGFloat
#Binding var alignmentIndex: Int
#Binding var elementsCount: Int
private let alignments: [HorizontalAlignment] = [.leading, .center, .trailing]
var body: some View {
VStack(alignment: self.alignments[alignmentIndex], spacing: CGFloat(spacing)) {
ForEach(0..<elementsCount, id: \.self) {
Text("\($0)th View")
}
}
}
}
and this is result
I guess you can update code like this
struct VStackView: View {
#Binding var spacing: CGFloat
#Binding var alignmentIndex: Int
#Binding var elementsCount: Int
private let alignments: [HorizontalAlignment] = [.leading, .center, .trailing]
var body: some View {
VStack(alignment: self.alignments[alignmentIndex], spacing: CGFloat(spacing)) {
ForEach(0..<elementsCount, id: \.self) {
Text("\($0)th View").frame(maxWidth: .infinity, alignment: Alignment(horizontal: self.alignments[self.alignmentIndex], vertical: .center))
}
}
}
}
You can set maxWidth of the frame to infinity - it is a way of telling the parent view that this view wants all the width it can get:
VStack {
//...
} .frame(maxWidth: .infinity)

How do I update the list when I add an item?

I am trying to update the list to reflect the new item when it is added. Currently, it isn't showing once I press the "paperplane" button. I have left out the repeating blocks of code if you see the references.
I have a suspicion that the problem comes in the button from MergedView but couldn't find a solution. It compiles but doesn't execute the intended action.
import Foundation
class One: Identifiable, ObservableObject { // ObservableObject requires class
let id: UUID
var item: String = ""
#Published var completed: Bool = false // this will affect the UI
init(item: String, completed: Bool) {
id = UUID()
self.item = item
self.completed = completed
}
}
class OneList: ObservableObject {
#Published var items = [One(item:"",completed:false)]
}
struct VitalRow: View {
#EnvironmentObject var item: One
var body: some View {
HStack{
Image(systemName: item.completed ? "checkmark.circle" : "circle")
Text(item.item)
}
}
}
struct ButtonView: View {
#EnvironmentObject var itemss1: OneList
#EnvironmentObject var itemss2: TwoList
#EnvironmentObject var itemss3: ThreeList
#EnvironmentObject var itemss4: FourList
#State private var showingAdditem: Bool = false
var body: some View {
VStack{
Spacer()
HStack{
Spacer()
Button(action:{
self.showingAdditem = true
}){
Image(systemName: "plus")
.font(Font.system(size: 25,weight: .regular))
.font(.largeTitle)
.frame( width: 50, height: 50)
.foregroundColor(.white)
}.sheet(isPresented: $showingAdditem)
{
MergedView()
.environmentObject(OneList())
.environmentObject(TwoList())
.environmentObject(ThreeList())
.environmentObject(FourList())
//items1: self.itemss1, items2: self.itemss2, items3: self.itemss3, items4: self.itemss4)
}.background(Color.blue)
.cornerRadius(25)
.shadow(color: Color.black.opacity(0.3), radius: 3,
x: 3,
y: 3)
.padding()
.offset(x: -5,
y: 15)
}
}
}
}
struct MergedView: View {
#Environment(\.presentationMode) var presentationMode
#EnvironmentObject var items1: OneList
#EnvironmentObject var items2: TwoList
#EnvironmentObject var items3: ThreeList
#EnvironmentObject var items4: FourList
/*#ObservedObject var items1: OneList
#ObservedObject var items2: TwoList
#ObservedObject var items3: ThreeList
#ObservedObject var items4: FourList*/
#State private var item = ""
#State var blockcolor1 = Color.red.opacity(0.90)
#State var blockcolor2 = Color.red.opacity(0.70)
#State var blockcolor3 = Color.red.opacity(0.45)
#State var blockcolor4 = Color.red.opacity(0.20)
#State var itemvalue: Int = 0
var body: some View {
GeometryReader { geo in
ZStack{
NavigationView{
HStack{
TextField("Item", text: self.$item)
.padding()
Button(action:{
if self.itemvalue == 1{
let things1 = One(item: self.item, completed: false)
self.items1.items.append(things1)
self.presentationMode.wrappedValue.dismiss()
}
{
Image(systemName:"paperplane")
.font(.headline)
.padding()
}
HStack(spacing: 10) {
Rectangle()
.fill(self.blockcolor1)
//.fill(Color.red.opacity(0.90))
//.frame(width: 140,height: 180)
.frame(width: geo.size.width/3, height: geo.size.height/4.6)
.cornerRadius(15)
.shadow(radius: 6)
.onTapGesture{
self.blockcolor1 = Color.red.opacity(0.1)
self.itemvalue = 1
}
struct CheckboxList: View {
#EnvironmentObject var item1List: OneList
#EnvironmentObject var item2List: TwoList
#EnvironmentObject var item3List: ThreeList
#EnvironmentObject var item4List: FourList
var body: some View {
NavigationView{
List {
Section(header: Text("Vital")) {
ForEach(item1List.items.indices) { index in
VitalRow()
.environmentObject(self.item1List.items[index])
.onTapGesture {
self.item1List.items[index].completed.toggle()
}
}
}
}
Thank you in advance.

Resources