SwiftUI Textfield not actually making updates to data - swift

I am trying to make an application where the user can see a list of foods and recipes and make changes etc. In the DetailView for each food, there is an EditView where the values can be changed. I am trying to use a double binding on the value by using #State to define the value and $food.name for example to make the changes happen.
When I click 'Done' and exit this view back to the DetailView, the changes are not made at all, leaving me confused?
Any help on this problem would be greatly appreciated, thank you :)
My EditView.swift:
import SwiftUI
struct EditView: View {
#State var food: Food
var body: some View {
List {
Section(header: Text("Name")) {
TextField("Name", text: $food.name)
}
Section(header: Text("Image")) {
TextField("Image", text: $food.image)
}
Section(header: Text("Desc")) {
TextField("Desc", text: $food.desc)
}
Section(header: Text("Story")) {
TextField("Story", text: $food.story)
}
}.listStyle(InsetGroupedListStyle())
}
}
struct EditView_Previews: PreviewProvider {
static var previews: some View {
EditView(food: cottonCandy)
}
}
My FoodDetail.swift:
import SwiftUI
struct FoodDetail: View {
#State var food: Food
#State private var isPresented = false
var body: some View {
VStack {
Image(food.image)
.resizable()
.frame(width: 300.0,height:300.0)
.aspectRatio(contentMode: .fill)
.shadow(radius: 6)
.padding(.bottom)
ScrollView {
VStack(alignment: .leading) {
Text(food.name)
.font(.largeTitle)
.fontWeight(.bold)
.padding(.leading)
.multilineTextAlignment(.leading)
Text(food.desc)
.italic()
.fontWeight(.ultraLight)
.padding(.horizontal)
.multilineTextAlignment(.leading)
Text(food.story)
.padding(.horizontal)
.padding(.top)
Text("Ingredients")
.bold()
.padding(.horizontal)
.padding(.vertical)
ForEach(food.ingredients, id: \.self) { ingredient in
Text(ingredient)
Divider()
}.padding(.horizontal)
Text("Recipe")
.bold()
.padding(.horizontal)
.padding(.vertical)
ForEach(food.recipe, id: \.self) { step in
Text(step)
Divider()
}.padding(.horizontal)
}.frame(maxWidth: .infinity)
}.frame(minWidth: 0,
maxWidth: .infinity,
maxHeight: .infinity,
alignment: .center
)
}
.navigationBarItems(trailing: Button("Edit") {
isPresented = true
})
.fullScreenCover(isPresented: $isPresented) {
NavigationView {
EditView(food: food)
.navigationTitle(food.name)
.navigationBarItems(leading: Button("Cancel") {
isPresented = false
}, trailing: Button("Done") {
isPresented = false
})
}
}
}
}
struct FoodDetail_Previews: PreviewProvider {
static var previews: some View {
Group {
FoodDetail(food: cottonCandy)
}
}
}

Inside EditView, you want to have a Binding. Replace
#State var food: Food
with
#Binding var food: Food
... then, you'll need to pass it in:
.fullScreenCover(isPresented: $isPresented) {
NavigationView {
EditView(food: $food) /// make sure to have dollar sign
.navigationTitle(food.name)
.navigationBarItems(leading: Button("Cancel") {
isPresented = false
}, trailing: Button("Done") {
isPresented = false
})
}
}

Related

Utilize buttons as pickers to pick between map views

So I want to be able to utilize a picker so that I can choose between different views, but the only option that I found was a picker, which outputs the following output:
I want to have a custom solution where I can have three buttons, and choose between those similar to how a picker is chosen, here is the look that I'm attempting to achieve:
The code below renders the buttons perfectly, but the Picker don't render the contents of the button nor the modifiers, how would I be able to achieve clicking on each individual button similar to how a picker is selected?
The Picker also have a .tag(0), .tag(1) that can be utilized, how would this work on buttons?
Here is the code:
struct MapDisplaySheetView: View {
#ObservedObject var mapSettings = MapSettings()
#State var mapType = 0
#State var allItems: [String] = [
"Standard",
"Hybrid",
"Image",
]
var body: some View {
VStack(spacing: 0) {
// MARK: Map Type
HStack {
Picker(
"Map Type",
selection: $mapType,
content: {
ForEach(allItems, id: \.self) { item in
VStack {
HStack {
VStack {
Text(item)
.font(.footnote.weight(.bold))
.frame(maxWidth: .infinity, alignment: .leading)
.clipped()
.foregroundColor(.primary)
}
}
.padding(8)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.clipped()
}
.border(.red)
.background {
RoundedRectangle(cornerRadius: 4, style: .continuous)
.foregroundColor(Color(.systemFill))
}
.foregroundColor(Color(.quaternaryLabel))
.cornerRadius(8)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.clipped()
}
})
.pickerStyle(SegmentedPickerStyle())
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
.clipped()
.padding(.bottom, 4)
}
.padding(16)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.clipped()
}
}
Here is what I attempted, but it's not working, I am not getting a log.info() output when clicking buttons:
struct MapDisplaySheetView: View {
#ObservedObject var mapSettings = MapSettings()
#State var mapType = 0
#State var allItems: [String] = [
"Standard",
"Hybrid",
"Image",
]
var body: some View {
VStack(spacing: 0) {
HStack {
ForEach(allItems, id: \.self) { item in
VStack {
HStack {
VStack {
Button(action: {
// Action
}, label: {
Text(item)
})
.tag(mapSettings.mapType)
.onChange(of: mapType) { newValue in
mapSettings.mapType = newValue
log.info("We have selected \(newValue)")
}
}
}
}
}
}
}
}
}
you could try something like this:
struct MapDisplaySheetView: View {
#ObservedObject var mapSettings = MapSettings()
#State var allItems: [String] = ["Standard", "Hybrid", "Image"]
var body: some View {
HStack {
ForEach(allItems, id: \.self) { item in
Button(action: {
mapSettings.mapType = item
print("We have selected \(item)")
}, label: {
Text(item)
})
.buttonStyle(.bordered)
}
}
}
}
EDIT-1:
if the mapType is an Int, then use:
struct MapDisplaySheetView: View {
#ObservedObject var mapSettings = MapSettings()
#State var allItems: [String] = ["Standard", "Hybrid", "Image"]
var body: some View {
HStack {
ForEach(allItems, id: \.self) { item in
Button(action: {
switch item {
case "Standard": mapSettings.mapType = 0
case "Hybrid": mapSettings.mapType = 1
case "Image": mapSettings.mapType = 2
default: mapSettings.mapType = 0
}
print("We have selected \(item) mapSettings: \(mapSettings.mapType) ")
}, label: {
Text(item)
})
.buttonStyle(.bordered)
}
}
}
}

How debug my code in List with NavigationView

On the Canvas my code start correctly, but simulator send message about bug
import SwiftUI
struct ContentView: View {
//1
var persons: [Member] = []
var body: some View {
NavigationView() {
break point stopped here
List(persons) { person in
NavigationLink(destination: {
MemberDetail(name: person.name, headline: person.headline, bio: person.bio)
}, label: {
HStack {
Image(person.imageName).cornerRadius(40.0)
VStack(alignment: .leading) {
Text(person.name)
Text(person.headline)
.font(.subheadline)
.foregroundColor(.gray)
}
}
})
}
.navigationTitle(Text("Persons"))
}
}
}
expectation
reality
Code struct of MemberDetails
struct MemberDetail: View {
var name: String
var headline: String
var bio: String
var body: some View {
VStack {
Image(name)
.clipShape(Circle())
.overlay(Circle().stroke(Color.orange, lineWidth: 4))
.shadow(radius: 10)
Text(name)
.font(.title)
Text(headline)
.font(.subheadline)
Divider()
Text(bio)
.font(.headline)
.multilineTextAlignment(.center)
.lineLimit(50)
}
.padding(20)
.navigationBarTitle(Text(name), displayMode: .inline)
}
}
break points get error. I run my project on iphone 7 and simulator, but this not give solution
I completely rewrote the code according to the guides from apple and it all worked
struct LandmarkList: View {
var body: some View {
NavigationView {
List(landmarks) { landmark in
NavigationLink {
LandmarkDetail(landmark: landmark)
} label: {
LandmarkRow(landmark: landmark)
}
}
.navigationTitle("Landmarks")
}
}

navigationLink to tag in another view

Here's an edited version of the question. I'm working in Swiftui and I have two views. The first has two NavigationLinks. The second link is where I get stuck. I would like that link to go to 'tabTwo' on PageTwo. Getting to PageTwo isn't an issue... it's getting to tabTwo as a view on that page.
Here's an edited down contentView file:
import SwiftUI
struct ContentView: View {
#State var isNavigationBarHidden: Bool = true
#State private var selectedTab = ""
var body: some View {
NavigationView {
ZStack {
VStack(spacing: 0) {
VStack(spacing: 0) {
NavigationLink("Link One", destination: PageTwo()).padding(10)
.foregroundColor(Color.gray)
.background(Color.white)
Divider().frame(width: 300,height: 1).background(Color.black)
}
VStack(spacing: 0) {
NavigationLink("Link Two", destination: PageTwo())
.padding(10)
.foregroundColor(Color.gray)
.background(Color.white)
}
// If you remove the below VStack everything works.
VStack(spacing: 0) {
NavigationLink("Page Two tabTwo", destination: PageTwo(), tag: "tabTwo", selection: $selectedTab?)
.padding(10)
.foregroundColor(Color.gray)
.background(Color.white)
}
}
}
}
.navigationBarTitle("Hidden Title")
.navigationBarHidden(self.isNavigationBarHidden)
.onAppear {self.isNavigationBarHidden = true}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
and here's an edited down PageTwo:
import SwiftUI
struct PageTwo: View {
#Environment(\.presentationMode) var mode: Binding<PresentationMode>
#State var isNavigationBarHidden: Bool = true
#State private var selectedTab = ""
var body: some View {
ZStack() {
TabView(selection: $selectedTab) {
//---> Tab One
VStack(spacing: 0) {
VStack{
Text("PAGE ONE")
.padding(.bottom, 20)
}
Button(action: {
self.mode.wrappedValue.dismiss()})
{ Text("BACK") }
.font(Font.system(size:14, weight: .bold, design: .default))
}
.navigationBarTitle("Hidden Title")
.navigationBarHidden(self.isNavigationBarHidden)
.onAppear {self.isNavigationBarHidden = true}
.tabItem {Label("ONE", systemImage: "circle.fill")}
.tag("tabOne")
//---> Tab Two
VStack(spacing: 0) {
VStack{
Text("PAGE TWO")
.padding(.bottom, 20)
}
Button(action: {
self.mode.wrappedValue.dismiss()})
{ Text("BACK") }
.font(Font.system(size:14, weight: .bold, design: .default))
}
.navigationBarTitle("Hidden Title")
.navigationBarHidden(self.isNavigationBarHidden)
.onAppear {self.isNavigationBarHidden = true}
.tabItem {Label("TWO", systemImage: "circle.fill")}
.tag("tabTwo")
}
.tabViewStyle(PageTabViewStyle())
.indexViewStyle(.page(backgroundDisplayMode: .never))
.onAppear {self.isNavigationBarHidden = true}
}
.navigationBarTitle("Hidden Title")
.navigationBarHidden(self.isNavigationBarHidden)
.onAppear {self.isNavigationBarHidden = true}
.background(Color.gray)
.opacity(0.75)
.indexViewStyle(.page(backgroundDisplayMode: .never))
}
}
struct PageTwo_Previews: PreviewProvider {
static var previews: some View {
PageTwo()
}
}
This throws an error at the 'some view' level of:
Failed to produce diagnostic for expression; please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the project
I've submitted the report, but can't figure out how to do what I'd like. Any help is much appreciated.

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

SwiftUI passing a saved value to parent view

I am fairly new to SwiftUI I am trying to figure out the best way to pass data from a child view to parent?
Thanks for the help I come from a Javascript (React) background so this is a little different for me
The way my child view works is the user clicks on an image to select that image.
I have #State binding that saves the imgUrl which is a String referring to name in Assets.
I am just not sure about the best way to pass that value to the parent component.
Here is the child view (imageSelector)
struct ImageSelector: View {
#State private var windowImgs = ["1", "2", "3","4","5","6","7","8","9","10","11","12","13", "14","15","16","17","18"]
#State private var imgPicked = ""
var body: some View{
ScrollView(Axis.Set.horizontal, showsIndicators: true){
HStack{
ForEach(0..<18){num in
Button(action:{
self.imgPicked = self.windowImgs[num]
print(self.imgPicked)
}){
Image("\(self.windowImgs[num])")
.renderingMode(.original)
.resizable()
.cornerRadius(4)
.frame(width: 100, height: 100)
}
}
}
}
}
}
Here is the parent view (AddCounterForm)
struct AddCounterForm: View {
#Environment(\.presentationMode) var presentationMode
#State private var pickedImg: String = "defaultImg"
#State private var price: String = "0.0"
#State private var qty: String = "0"
var body: some View {
VStack (spacing: 40){
HStack {
Button("Cancel"){
self.presentationMode.wrappedValue.dismiss()
}
.foregroundColor(.red)
Spacer()
Button("Save"){
}
}
HStack {
VStack (spacing: 20){
TextField("Window type", text: /*#START_MENU_TOKEN#*//*#PLACEHOLDER=Value#*/.constant("")/*#END_MENU_TOKEN#*/)
TextField("Window location", text: /*#START_MENU_TOKEN#*//*#PLACEHOLDER=Value#*/.constant("")/*#END_MENU_TOKEN#*/)
}
.textFieldStyle(RoundedBorderTextFieldStyle())
Image(pickedImg)
.resizable()
.cornerRadius(4)
.frame(width: 90, height: 90)
.padding(.leading)
}
HStack {
Text("Price")
TextField("", text:$price)
.frame(width: 70)
.textFieldStyle(RoundedBorderTextFieldStyle())
.keyboardType(.numberPad)
Spacer()
Text("Qty")
TextField("", text:$qty)
.frame(width: 70)
.textFieldStyle(RoundedBorderTextFieldStyle())
.keyboardType(.numberPad)
}
VStack {
Text("Select an image")
.foregroundColor(.blue)
ImageSelector()
.padding(.bottom)
Button("Use your own image"){
//method
}
.frame(width: 180, height: 40)
.background(Color.blue)
.clipShape(Capsule())
.foregroundColor(.white)
.padding(.top)
}
}
.padding()
}
}
Solution for preview thanks for the help from #Asperi & #neverwinterMoon
struct ImageSelector_Previews: PreviewProvider {
static var previews: some View {
PreviewWrapper()
}
}
struct PreviewWrapper: View {
#State(initialValue: "") var imgPicked: String
var body: some View {
ImageSelector(imgPicked: $imgPicked)
}
}
In this case Binding is most appropriate
struct ImageSelector: View {
#Binding var imgPicked: String
and use
ImageSelector(imgPicked: $pickedImg)
.padding(.bottom)