using SwiftUI, when scrolling, VStack inside Section shrinks and expands - ios

I'm trying to make simple testing app with Buttons and Options for Button action.
target is iOS 13.0 for backward compatibility.
expected smooth scroll just like other iOS app does, but it stutters.
some VStack is continuously shrink and expand, keep changing scrolls height so it stutters I think.
here's video: https://imgur.com/a/QguC4g0
if I stop just right at stutter-bottom point, there's shrunken VStack.
you can see that shrunken VStack at end of video.
here's the code of main View.
(shrunken Section is SectionH.)
import SwiftUI
struct ContentView: View {
#Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass?
#Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass?
var body: some View {
if(verticalSizeClass == .regular && horizontalSizeClass == .compact) {
VStack {
List {
SectionA()
SectionB()
SectionC()
SectionD()
SectionE()
SectionF()
SectionG()
SectionH()
}
OtherView()
}
} else {
HStack {
List {
SectionA()
SectionB()
SectionC()
SectionD()
SectionE()
SectionF()
SectionG()
SectionH()
}
OtherView()
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
and heres simple examples of other Sections(A to G are similar.)
import SwiftUI
struct SectionF: View {
#State var bool1 : Bool = true;
#State var text1: String = "text"
#State var text2: String = "text"
#State var text3: String = "text"
#State var text4: String = "text"
var body: some View {
Section(header: Text("Section F")) {
Button{
} label: {
Text("button")
}
VStack(alignment: .leading) {
Button{
} label: {
Text("button: \(bool1 ? "true" : "false")")
}
Toggle("bool", isOn: $bool1)
}
VStack(alignment: .leading) {
Button{
} label: {
Text("button")
}
HStack {
Text("text: ")
TextField("text", text: $text1)
}
HStack {
Text("text: ")
TextField("text", text: $text2)
}
HStack {
Text("text: ")
TextField("text", text: $text3)
}
HStack {
Text("text: ")
TextField("text", text: $text4)
}
}
VStack(alignment: .leading) {
Button{
} label: {
Text("button")
}
HStack {
Text("text: ")
TextField("text", text: $text1)
}
}
}
}
}
struct SectionF_Previews: PreviewProvider {
static var previews: some View {
Form {
SectionF()
}
}
}
and here's dreaded Section H.
import SwiftUI
struct SectionH: View {
#State var text1: String = "text"
#State var text2: String = "text"
#State var picker1: String = "1"
var picker1s = ["1", "2"]
#State var text3: String = "text"
#State var text4: String = "text"
#State var bool1 : Bool = true;
#State var text5: String = "text"
#State var bool2 : Bool = true;
#State var bool3 : Bool = true;
#State var number1: String = "0"
var body: some View {
Section(header: Text("Section H")) {
VStack(alignment: .leading) {
Button{
} label: {
Text("button")
}
HStack {
Text("text: ")
TextField("text", text: $text1)
}
HStack {
Text("text: ")
TextField("text", text: $text2)
}
VStack(alignment: .leading) {
Text("picker: \(picker1)")
Picker("picker", selection: $picker1) {
ForEach(picker1s, id: \.self) {
Text($0)
}
}.pickerStyle(.segmented)
}
HStack {
Text("text: ")
TextField("text", text: $text3)
}
HStack {
Text("text: ")
TextField("text", text: $text4)
}
}
Button {
} label: {
Text("Button")
}
VStack(alignment: .leading) {
Button {
} label: {
Text("Button")
}
VStack {
HStack {
Text("text: ")
TextField("text", text: $text1)
}
HStack {
Text("text: ")
TextField("text", text: $text2)
}
VStack(alignment: .leading) {
Text("picker: \(picker1)")
Picker("picker", selection: $picker1) {
ForEach(picker1s, id: \.self) {
Text($0)
}
}.pickerStyle(.segmented)
}
}
//**this is shrinking VStack!**
VStack {
Toggle("bool", isOn: $bool1)
HStack {
Text("text: ")
TextField("text", text: $text3)
}
HStack {
Text("text: ")
TextField("text", text: $text5)
}
Toggle("bool", isOn: $bool2)
Toggle("bool", isOn: $bool3)
HStack {
Text("number: ")
TextField("number", text: $number1)
}
}
}
}
}
}
struct SectionH_Previews: PreviewProvider {
static var previews: some View {
Form {
SectionH()
}
}
}
here's what I tried:
changing List to Form
changing Sections order
removing V/HStack changing by orientation <- this one removes stutter!
reducing options from SectionH
divide SectionHs options into 2 VStack <- is applied to code above.
I'm new to Swift and SwiftUI, I don't know why this happens and can't get any clues to solve this problem.
thanks in advice.
UPDATE
removed SectionH, not fixed.
removed OtherView, not fixed.
I think theres a clue on if state with orientation.

Related

Animation not working inside sheet for swiftui

I am using a sheet to present a list of options and on click of the option I want to change the view with the animation of sliding from trailing. As per my understanding and what I have read on various sites I have written this code but I am not sure why it is not working the way intended. I just want to know where exactly this code went wrong.
struct XYZ: App {
let persistenceController = PersistenceController.shared
#State var isPresented : Bool = false
#State var isSwiped : Bool = false
var body: some Scene {
WindowGroup {
optionList(isPresented: $isPresented)
.sheet(isPresented: $isPresented, content: {
Text("This is from modal view!")
.onTapGesture {
withAnimation(Animation.easeIn(duration: 10)){
isSwiped.toggle()
}
}
if isSwiped {
checkedList()
.transition(.move(edge: .trailing))
}
})
}
}
}
struct optionList : View {
#Binding var isPresented : Bool
var body: some View {
Text("This is a testView")
.onTapGesture {
withAnimation{
isPresented.toggle()
}
}
}
}
struct checkedList : View {
#State var name : String = "WatchList"
var arr = ["First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh"]
#State var mp : [Int:Int] = [:]
var body: some View {
VStack{
HStack{
TextField("WatchlistName", text: $name)
.padding(.all)
Image(systemName: "trash.fill")
.padding(.all)
.onTapGesture {
print("Delete watchList!!")
}
}
ScrollView{
ForEach(arr.indices) { item in
HStack (spacing: 0) {
Image(systemName: mp.keys.contains(item) ? "checkmark.square" : "square")
.padding(.horizontal)
Text(arr[item])
}
.padding(.bottom)
.frame(width: UIScreen.main.bounds.width, alignment: .leading)
.onTapGesture {
if mp.keys.contains(item) {
mp[item] = nil
} else {
mp[item] = 1
}
}
}
}
Button {
print("Remove Ticked Elements!")
deleteWatchListItem(arr: Array(mp.keys))
} label: {
Text("Save")
}
}
}
func deleteWatchList(ind: Int){
print(ind)
}
func deleteWatchListItem(arr : [Int]) {
print(arr)
}
}
I tried to create a view and with the animation using withanimation with a bool variable tried to change the view.
It sounds like what you want is to push the checkedList on to a NavigationStack…
struct ContentView: View {
#State var isPresented : Bool = false
var body: some View {
Text("This is a testView")
.onTapGesture {
isPresented.toggle()
}
.sheet(isPresented: $isPresented, content: {
NavigationStack {
NavigationLink("This is from modal view!") {
checkedList()
}
}
})
}
}

SwiftUI Limit Scope of EditButton() by Platform

I am building an app for iOS and Mac Catalyst and have been able to code most of the
experience that I want except for functions that use swipe to delete in iOS.
The view includes multiple sections, each with a List and ForEach closure. I want to
be able to add the EditButton() function to the header of each section and have it
apply only to that section's List.
I can
add an EditButton() function to gain this functionality, however,
so far I have only been able to make that work for the entire screen, not for the
individual sections.
I have tried refactoring the code for each section into functions and into structs
(as shown below). In all cases the EditButton() activates the delete icons for ALL
list rows, not just the section with the button.
I have also tried placing the EditButton() inside the section in the VStack. No difference.
Here's a simple example with the latest code attempt:
struct ContentView: View {
#State private var selectedItem: String?
#State private var items = ["One", "Two", "Three", "Four", "Five"]
#State private var fruits = ["Apple", "Orange", "Pear", "Lemon", "Grape"]
var body: some View {
NavigationSplitView {
ItemSection(selectedItem: $selectedItem, items: $items)
FruitSection(selectedItem: $selectedItem, fruits: $fruits)
} detail: {
if let selectedItem {
ItemDetailView(selectedItem: selectedItem)
} else {
EmptyView()
}
}//nav
}//body
}//struct
struct ItemSection: View {
#Binding var selectedItem: String?
#Binding var items: [String]
var body: some View {
Section {
VStack {
List(selection: $selectedItem) {
ForEach(items, id: \.self) { item in
NavigationLink(value: item) {
Text(item)
}
}
.onDelete { items.remove(atOffsets: $0) }
}
.listStyle(PlainListStyle())
}//v
.padding()
} header: {
HStack {
Text("Section for Items")
Spacer()
//uncomment when you have it working
//#if targetEnvironment(macCatalyst)
EditButton()
//#endif
}//h
.padding(.horizontal, 10)
}//section and header
}//body
}//item section
struct FruitSection: View {
#Binding var selectedItem: String?
#Binding var fruits: [String]
var body: some View {
Section {
VStack {
List(selection: $selectedItem) {
ForEach(fruits, id: \.self) { fruit in
NavigationLink(value: fruit) {
Text(fruit)
}
}
.onDelete { fruits.remove(atOffsets: $0) }
}
.listStyle(PlainListStyle())
}//v
.padding()
} header: {
HStack {
Text("Section for Fruits")
Spacer()
}//h
.padding(.horizontal, 10)
}//section fruit
}//body
}//fruit section
struct ItemDetailView: View {
var selectedItem: String
var body: some View {
VStack {
Text(selectedItem)
Text("This is the DetailView")
}
}
}
Any guidance would be appreciated. Xcode 14.0.1 iOS 16
import SwiftUI
struct ContentView: View {
#State private var selectedItem: String?
#State private var items = ["One", "Two", "Three", "Four", "Five"]
#State private var fruits = ["Apple", "Orange", "Pear", "Lemon", "Grape"]
var body: some View {
NavigationSplitView {
ItemSection(selectedItem: $selectedItem, items: $items)
FruitSection(selectedItem: $selectedItem, fruits: $fruits)
} detail: {
if let selectedItem {
ItemDetailView(selectedItem: selectedItem)
} else {
EmptyView()
}
}//nav
}//body
}//struct
struct ItemSection: View {
#Binding var selectedItem: String?
#Binding var items: [String]
#State var isEditMode = false // this is what you need
var body: some View {
Section {
VStack {
List(selection: $selectedItem) {
ForEach(items, id: \.self) { item in
NavigationLink(value: item) {
Text(item)
}
}
.onDelete { items.remove(atOffsets: $0) }
}
.environment(\.editMode, isEditMode ? .constant(.active) : .constant(.inactive)) // and set this
.listStyle(PlainListStyle())
}//v
.padding()
} header: {
HStack {
Text("Section for Items")
Spacer()
//uncomment when you have it working
//#if targetEnvironment(macCatalyst)
Button { // you also need to set EditButton() -> Button()
withAnimation {
isEditMode.toggle()
}
} label: {
Text(isEditMode ? "Done" : "Edit")
}
//#endif
}//h
.padding(.horizontal, 10)
}//section and header
}//body
}//item section
struct FruitSection: View {
#Binding var selectedItem: String?
#Binding var fruits: [String]
#State var isEditMode = false // same as this section
var body: some View {
Section {
VStack {
List(selection: $selectedItem) {
ForEach(fruits, id: \.self) { fruit in
NavigationLink(value: fruit) {
Text(fruit)
}
}
.onDelete { fruits.remove(atOffsets: $0) }
}
.environment(\.editMode, isEditMode ? .constant(.active) : .constant(.inactive))
.listStyle(PlainListStyle())
}//v
.padding()
} header: {
HStack {
Text("Section for Fruits")
Spacer()
Button {
withAnimation {
isEditMode.toggle()
}
} label: {
Text(isEditMode ? "Done" : "Edit")
}
}//h
.padding(.horizontal, 10)
}//section fruit
}//body
}//fruit section
struct ItemDetailView: View {
var selectedItem: String
var body: some View {
VStack {
Text(selectedItem)
Text("This is the DetailView")
}
}
}
Here's a more general & simplified approach using PreferenceKey:
struct EditModeViewModifier: ViewModifier {
var forceEditing: Bool?
#State var isEditing = false
func body(content: Content) -> some View {
content
.onPreferenceChange(IsEditingPrefrenceKey.self) { newValue in
withAnimation {
isEditing = newValue
}
}.environment(\.editMode, .constant((forceEditing ?? isEditing) ? .active: .inactive))
}
}
extension View {
func editMode(_ editing: Bool? = nil) -> some View {
modifier(EditModeViewModifier(forceEditing: editing))
}
}
struct EditingButton: View {
#State var isEditing = false
var body: some View {
Button(action: {
isEditing.toggle()
}) {
Text(isEditing ? "Done" : "Edit")
}.preference(key: IsEditingPrefrenceKey.self, value: isEditing)
}
}
struct IsEditingPrefrenceKey: PreferenceKey {
static var defaultValue = false
static func reduce(value: inout Bool, nextValue: () -> Bool) {
value = nextValue()
}
}
You use EditingButton instead of EditButton, & use .editMode() at then end of your View. Then your sections become something like this:
struct ItemSection: View {
#Binding var selectedItem: String?
#Binding var items: [String]
var body: some View {
Section {
VStack {
List(selection: $selectedItem) {
ForEach(items, id: \.self) { item in
NavigationLink(value: item) {
Text(item)
}
}.onDelete { items.remove(atOffsets: $0) }
}.listStyle(PlainListStyle())
}.padding()
} header: {
HStack {
Text("Section for Items")
Spacer()
//uncomment when you have it working
//#if targetEnvironment(macCatalyst)
EditingButton()
//#endif
}.padding(.horizontal, 10)
}.editMode()
}
}
struct FruitSection: View {
#Binding var selectedItem: String?
#Binding var fruits: [String]
var body: some View {
Section {
VStack {
List(selection: $selectedItem) {
ForEach(fruits, id: \.self) { fruit in
NavigationLink(value: fruit) {
Text(fruit)
}
}.onDelete { fruits.remove(atOffsets: $0) }
}.listStyle(PlainListStyle())
}.padding()
} header: {
HStack {
Text("Section for Fruits")
Spacer()
EditingButton()
}.padding(.horizontal, 10)
}.editMode()
}
}
A more concise version of Timmy's answer uses this reusable code:
import SwiftUI
struct EditingButton: View {
#Binding var isEditing: Bool
var body: some View {
Button(isEditing ? "Done" : "Edit", action: changeEditing)
.preference(key: IsEditingPrefrenceKey.self, value: isEditing)
}
func changeEditing() { withAnimation { isEditing.toggle() } }
struct IsEditingPrefrenceKey: PreferenceKey {
static var defaultValue = false
static func reduce(value: inout Bool, nextValue: () -> Bool) { value = nextValue() }
}
}// EditingButton
extension View {
func editMode(_ editing: Bool) -> some View {
environment(\.editMode, editing ? .constant(.active) : .constant(.inactive))
}
}
Then the ItemSection is like this:
struct ItemSection: View {
#Binding var selectedItem: String?
#Binding var items: [String]
#State var isEditMode = false // this is what you need
var body: some View {
Section {
VStack {
List(selection: $selectedItem) {
ForEach(items, id: \.self) { item in
NavigationLink(value: item) {
Text(item)
}
}
.onDelete { items.remove(atOffsets: $0) }
.onMove(perform: move)
}
.editMode(isEditMode)
.listStyle(PlainListStyle())
}//v
.padding()
} header: {
HStack {
Text("Section for Items")
Spacer()
EditingButton(isEditing: $isEditMode)
}//h
.padding(.horizontal, 10)
}//section and header
}// body
private func move(indexes: IndexSet, dest: Int) {
print("Move item from indexset \(indexSetList(indexes)) to index \(dest)")
items.move(fromOffsets: indexes, toOffset: dest)
}// move
}//item section
func indexSetList(_ indexes: IndexSet) -> String {
guard !indexes.isEmpty else { return "none"}
return Array(indexes).map(String.init).joined(separator: " ")
}
I have allowed drag and drop reordering to make it do what I was interested in as well.

SwiftUI - List that does not display

I don't know how to explain it, I think a video will be more explicit ..
https://www.youtube.com/watch?v=UyuGaNA6NCo
I only want to display the information according to my choices, and I don't know where I was wrong, I am in the problem since some hours
My code :
HOME VIEW
struct HomeView: View {
#ObservedObject var travelLibrary: TravelLibrary
#State private var isShowingSurveyCreation = false
var body: some View {
ZStack(alignment: Alignment(horizontal: .trailing, vertical: .bottom)) {
NavigationView {
ScrollView {
VStack {
ForEach(travelLibrary.testTravels) { travel in
ZStack {
RoundedRectangle(cornerRadius: 15, style: .continuous)
.fill(Color.white)
.shadow(color: /*#START_MENU_TOKEN#*/.black/*#END_MENU_TOKEN#*/, radius: 7)
TravelCellView(travel: travel)
}
}
}.padding(12)
}.navigationTitle(Text("Mes Voyages"))
}
PlusButtonView(action: {
isShowingSurveyCreation.toggle()
}).sheet(isPresented: $isShowingSurveyCreation, content: {
TravelCreationView(travelLibrary: travelLibrary)
})
.padding()
}
}
}
struct HomeView_Previews: PreviewProvider {
#StateObject static var travelLibrary = TravelLibrary()
static var previews: some View {
HomeView(travelLibrary: travelLibrary)
}
}
TRAVEL CREATION VIEW
import SwiftUI
struct TravelCreationView: View {
#ObservedObject var travelLibrary: TravelLibrary
#ObservedObject var travelConfig = NewTravelConfig()
#Environment(\.presentationMode) var presentationMode
var body: some View {
NavigationView {
ZStack {
Form {
Section {
Picker(selection: $travelConfig.index1, label: Text("Pays de destination : ")) {
ForEach(0 ..< travelConfig.nameCountry.count) {
Text(travelConfig.nameCountry[$0]).tag($0)
.foregroundColor(.black)
}
}
Picker(selection: $travelConfig.index2, label: Text("Type de voyage : ")) {
ForEach(0 ..< travelConfig.typeOfTravel.count) {
Text(travelConfig.typeOfTravel[$0]).tag($0)
.foregroundColor(.black)
}
}
}
Section {
Stepper(value: $travelConfig.day, in: 1...365) {
Text("Durée du séjour : \(travelConfig.day) jours")
}
}
}
}.navigationTitle("Formulaire")
.navigationBarItems(trailing: Button(action: {
let newTravel = Travel(config: travelConfig)
travelLibrary.testTravels.append(newTravel)
presentationMode.wrappedValue.dismiss()
}, label: {
Text("Valider")
}))
}
}
}
struct TravelCreationView_Previews: PreviewProvider {
#StateObject static var travelLibrary = TravelLibrary()
static var previews: some View {
TravelCreationView(travelLibrary: travelLibrary)
}
}
//
// TravelCreationView.swift
// Final Travel Project Logistics
//
// Created by Sefkan on 04/05/2021.
//
import SwiftUI
struct TravelCreationView: View {
#ObservedObject var travelLibrary: TravelLibrary
#ObservedObject var travelConfig = NewTravelConfig()
#Environment(\.presentationMode) var presentationMode
var body: some View {
NavigationView {
ZStack {
Form {
Section {
Picker(selection: $travelConfig.index1, label: Text("Pays de destination : ")) {
ForEach(0 ..< travelConfig.nameCountry.count) {
Text(travelConfig.nameCountry[$0]).tag($0)
.foregroundColor(.black)
}
}
Picker(selection: $travelConfig.index2, label: Text("Type de voyage : ")) {
ForEach(0 ..< travelConfig.typeOfTravel.count) {
Text(travelConfig.typeOfTravel[$0]).tag($0)
.foregroundColor(.black)
}
}
}
Section {
Stepper(value: $travelConfig.day, in: 1...365) {
Text("Durée du séjour : \(travelConfig.day) jours")
}
}
}
}.navigationTitle("Formulaire")
.navigationBarItems(trailing: Button(action: {
let newTravel = Travel(config: travelConfig)
travelLibrary.testTravels.append(newTravel)
presentationMode.wrappedValue.dismiss()
}, label: {
Text("Valider")
}))
}
}
}
struct TravelCreationView_Previews: PreviewProvider {
#StateObject static var travelLibrary = TravelLibrary()
static var previews: some View {
TravelCreationView(travelLibrary: travelLibrary)
}
}//
// TravelCreationView.swift
// Final Travel Project Logistics
//
// Created by Sefkan on 04/05/2021.
//
import SwiftUI
struct TravelCreationView: View {
#ObservedObject var travelLibrary: TravelLibrary
#ObservedObject var travelConfig = NewTravelConfig()
#Environment(\.presentationMode) var presentationMode
var body: some View {
NavigationView {
ZStack {
Form {
Section {
Picker(selection: $travelConfig.index1, label: Text("Pays de destination : ")) {
ForEach(0 ..< travelConfig.nameCountry.count) {
Text(travelConfig.nameCountry[$0]).tag($0)
.foregroundColor(.black)
}
}
Picker(selection: $travelConfig.index2, label: Text("Type de voyage : ")) {
ForEach(0 ..< travelConfig.typeOfTravel.count) {
Text(travelConfig.typeOfTravel[$0]).tag($0)
.foregroundColor(.black)
}
}
}
Section {
Stepper(value: $travelConfig.day, in: 1...365) {
Text("Durée du séjour : \(travelConfig.day) jours")
}
}
}
}.navigationTitle("Formulaire")
.navigationBarItems(trailing: Button(action: {
let newTravel = Travel(config: travelConfig)
travelLibrary.testTravels.append(newTravel)
presentationMode.wrappedValue.dismiss()
}, label: {
Text("Valider")
}))
}
}
}
struct TravelCreationView_Previews: PreviewProvider {
#StateObject static var travelLibrary = TravelLibrary()
static var previews: some View {
TravelCreationView(travelLibrary: travelLibrary)
}
}
TRAVELCELLVIEW
//
// TravelCellView.swift
// Final Travel Project Logistics
//
// Created by Sefkan on 04/05/2021.
//
import SwiftUI
struct TravelCellView: View {
let travel : Travel
var body: some View {
HStack {
Image(travel.flagCountry)
.resizable()
.aspectRatio(contentMode: /*#START_MENU_TOKEN#*/.fill/*#END_MENU_TOKEN#*/)
.frame(width: 80, height: 80)
.clipShape(/*#START_MENU_TOKEN#*/Circle()/*#END_MENU_TOKEN#*/)
.overlay(Circle().stroke(Color.white, lineWidth: 4))
.shadow(radius: 10)
.padding(.trailing, 8)
VStack(alignment : .leading) {
Text(travel.nameCountry)
.font(.title3)
.fontWeight(.bold)
.foregroundColor(Color.black)
.padding(.bottom, -4)
Text("Durée du voyage: \(travel.day) jours")
.font(.subheadline)
.fontWeight(.semibold)
.foregroundColor(Color.black)
.padding(.bottom, -4)
Text(travel.typeOfTravel)
.font(.subheadline)
.foregroundColor(Color.black)
}
Spacer()
FavouriteButtonView()
}.padding()
}
}
struct TravelCellView_Previews: PreviewProvider {
private static let testTravel = Travel(flagCountry: "USAflag", nameCountry: "États-Unis", typeOfTravel: "Tourisme", day: 0, isFavourite: false)
static var previews: some View {
TravelCellView(travel: testTravel)
.previewLayout(.sizeThatFits)
}
}
DATA VIEW
import Foundation
struct Travel: Identifiable {
let id = UUID().uuidString
let flagCountry: String
let nameCountry: String
let typeOfTravel: String
let day: Int
var isFavourite: Bool
init(flagCountry: String, nameCountry: String, typeOfTravel: String, day: Int, isFavourite: Bool) {
self.flagCountry = flagCountry
self.nameCountry = nameCountry
self.typeOfTravel = typeOfTravel
self.day = day
self.isFavourite = isFavourite
}
init(config: NewTravelConfig) {
flagCountry = ""
nameCountry = ""
typeOfTravel = ""
day = 0
isFavourite = false
}
}
class TravelLibrary: ObservableObject {
#Published var testTravels = [
Travel(flagCountry: "USAflag", nameCountry: "États-Unis", typeOfTravel: "Tourisme", day: 0, isFavourite: false),
Travel(flagCountry: "Japanflag", nameCountry: "Japon", typeOfTravel: "Tourisme", day: 0, isFavourite: true),
Travel(flagCountry: "Germanflag", nameCountry: "Allemagne", typeOfTravel: "Tourisme", day: 0, isFavourite: false)
]
}
class NewTravelConfig: ObservableObject {
#Published var nameCountry = ["États-Unis", "Japon", "Allemagne"]
#Published var typeOfTravel = ["Tourisme", "Business"]
#Published var day = 0
#Published var index1 = 0
#Published var index2 = 0
}
Thanks!
Your issue is inside the Travel struct. You have two init() methods, and the issue is in the one where you pass your config.
You are passing the config to it, but not using any of these values from the config.
Here is the correct one:
init(config: NewTravelConfig) {
flagCountry = ""
nameCountry = config.nameCountry[config.index1]
typeOfTravel = config.typeOfTravel[config.index2]
day = config.day
isFavourite = false
}

Swift - Update List from different View

I have 2 Views in my Swift Project and when I click on the Button on the secondView, I want to update the List in the First View. I don't know how to do it! If I use a static variable in my MainView and then edit this variable from the secondView, it works, but it won't update. And if I don't use static and instead use #State, it would update, but I can't access it from my secondView.
Here is the Code below:
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
MainView()
.tabItem() {
VStack {
Image(systemName: "circle.fill")
Text("MainView")
}
}.tag(0)
UpdateOtherViewFromHere()
.tabItem() {
VStack {
Image(systemName: "circle.fill")
Text("SecondView")
}
}.tag(1)
}
}
}
struct MainView: View {
var arrayList: [CreateListItems] = []
init() {
let a = CreateListItems(name: "First Name!")
let b = CreateListItems(name: "Second Name!")
let c = CreateListItems(name: "Third Name!")
arrayList.append(a)
arrayList.append(b)
arrayList.append(c)
}
var body: some View {
return VStack {
ZStack {
NavigationView {
List {
ForEach(arrayList) { x in
Text("\(x.name)")
}
}.navigationBarTitle("Main View")
}
}
}
}
}
struct UpdateOtherViewFromHere: View {
func updateList() {
//Code that should remove "FirstName" from the List in MainView
}
var body: some View {
return VStack {
Button(action: {
updateList()
}) {
Image(systemName: "heart.slash")
.font(.largeTitle)
Text("Click Me!")
}
}
}
}
struct CreateListItems: Identifiable {
var id: UUID = UUID()
var name: String
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
You can share it using #State and #Binding if you put
struct ContentView: View {
#State var arrayList: [CreateListItems] = []
struct MainView: View {
#Binding var arrayList: [CreateListItems]
struct UpdateOtherViewFromHere: View {
#Binding var arrayList: [CreateListItems]
or you use the MVVM pattern and store the list in an ObservableObject and use #StateObject/#ObservedObject (source) and use #EnvironmentObject(connection) to share it between your Views.
https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app
class ParentViewModel: ObservableObject{
#Published var arrayList: [CreateListItems] = []
init(){
addSamples()
}
func addSamples() {
let a = CreateListItems(name: "First Name!")
let b = CreateListItems(name: "Second Name!")
let c = CreateListItems(name: "Third Name!")
arrayList.append(a)
arrayList.append(b)
arrayList.append(c)
}
func updateList() {
let a = CreateListItems(name: "\(arrayList.count + 1) Name!")
arrayList.append(a)
}
}
struct ParentView: View {
#StateObject var vm: ParentViewModel = ParentViewModel()
var body: some View {
TabView {
MainView().environmentObject(vm)
.tabItem() {
VStack {
Image(systemName: "circle.fill")
Text("MainView")
}
}.tag(0)
UpdateOtherViewFromHere().environmentObject(vm)
.tabItem() {
VStack {
Image(systemName: "circle.fill")
Text("SecondView")
}
}.tag(1)
}
}
}
struct MainView: View {
#EnvironmentObject var vm: ParentViewModel
var body: some View {
return VStack {
ZStack {
NavigationView {
List {
ForEach(vm.arrayList) { x in
Text(x.name)
}
}.navigationBarTitle("Main View")
}
}
}
}
}
struct UpdateOtherViewFromHere: View {
#EnvironmentObject var vm: ParentViewModel
var body: some View {
return VStack {
Button(action: {
vm.updateList()
}) {
Image(systemName: "heart.slash")
.font(.largeTitle)
Text("Click Me!")
}
}
}
}

How to push many views into a NavigationView in SwiftUI [duplicate]

In my navigation, I want to be able to go from ContentView -> ModelListView -> ModelEditView OR ModelAddView.
Got this working, my issue now being that when I hit the Back button from ModelAddView, the intermediate view is omitted and it pops back to ContentView; a behaviour that
ModelEditView does not have.
There's a reason for that I guess – how can I get back to ModelListView when dismissing ModelAddView?
Here's the code:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
List{
NavigationLink(
destination: ModelListView(),
label: {
Text("1. Model")
})
Text("2. Model")
Text("3. Model")
}
.padding()
.navigationTitle("Test App")
}
}
}
struct ModelListView: View {
#State var modelViewModel = ModelViewModel()
var body: some View {
List(modelViewModel.modelValues.indices) { index in
NavigationLink(
destination: ModelEditView(model: $modelViewModel.modelValues[index]),
label: {
Text(modelViewModel.modelValues[index].titel)
})
}
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(
trailing:
NavigationLink(
destination: ModelAddView(modelViewModel: $modelViewModel), label: {
Image(systemName: "plus")
})
)
}
}
struct ModelEditView: View {
#Binding var model: Model
var body: some View {
TextField("Titel", text: $model.titel)
}
}
struct ModelAddView: View {
#Binding var modelViewModel: ModelViewModel
#State var model = Model(id: UUID(), titel: "")
var body: some View {
TextField("Titel", text: $model.titel)
}
}
struct ModelViewModel {
var modelValues: [Model]
init() {
self.modelValues = [ //mock data
Model(id: UUID(), titel: "Foo"),
Model(id: UUID(), titel: "Bar"),
Model(id: UUID(), titel: "Buzz")
]
}
}
struct Model: Identifiable, Equatable {
let id: UUID
var titel: String
}
Currently placing a NavigationLink in the .navigationBarItems may cause some issues.
A possible solution is to move the NavigationLink to the view body and only toggle a variable in the navigation bar button:
struct ModelListView: View {
#State var modelViewModel = ModelViewModel()
#State var isAddLinkActive = false // add a `#State` variable
var body: some View {
List(modelViewModel.modelValues.indices) { index in
NavigationLink(
destination: ModelEditView(model: $modelViewModel.modelValues[index]),
label: {
Text(modelViewModel.modelValues[index].titel)
}
)
}
.background( // move the `NavigationLink` to the `body`
NavigationLink(destination: ModelAddView(modelViewModel: $modelViewModel), isActive: $isAddLinkActive) {
EmptyView()
}
.hidden()
)
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(trailing: trailingButton)
}
// use a Button to activate the `NavigationLink`
var trailingButton: some View {
Button(action: {
self.isAddLinkActive = true
}) {
Image(systemName: "plus")
}
}
}

Resources