When using TabView, the "AddItemView" shows itself as multiple buttons, but should just be one button. Each button takes the user to a different piece of the view. For example, one button shows the title, another button shows a list etc.
The TabView code:
struct MainTabView: View {
var body: some View {
TabView {
ContentView()
.tabItem {
Label("Recipes", systemImage: "list.dash")
}
AddItemView(imageToUpload: Data.init(), recipeStep: [String].init(), stepNumber: [Int].init())
.tabItem {
Label("Add", systemImage: "plus")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gearshape.2")
}
}
}
}
What is actually displayed:
As you can see, the "Add" is repeated multiple times.
Any thoughts on what could be causing this?
EDIT — AddItemView code
struct AddItemView: View {
@StateObject var viewModel = ViewModel()
@State var imageToUpload: Data
@StateObject var vm = CoreDataRelationshipViewModel()
@Environment(.presentationMode) var presentationMode
@State var recipeStep: [String]
@State var stepInfo: String = ""
@State var textFieldCount: Int = 1
@State var stepNumber: [Int]
@State var recipeName: String = ""
@State var errorText = ""
@State var stepErrorColor = Color.white.opacity(0)
@State var nameErrorColor = Color.white.opacity(0)
@State var recipesToBind = [StepsEntity].init()
@State var showImage = true
@StateObject var pubRecArray = PublishedRecipeStepAddItemView()
@State private var showingAlert = false
var body: some View {
ZStack {
HStack {
Spacer()
Button(action: {
showingAlert = true
}) {
Image(systemName: viewModel.buttonImage)
}
.alert(isPresented: $showingAlert) { () -> Alert in
Alert(title: Text("Upload Image"), primaryButton: .default(Text("Choose Photo"), action: {
viewModel.choosePhoto()
}), secondaryButton: .default(Text("Take Photo"), action: {
viewModel.takePhoto()
}))
}
//placing the imageview directly in the image section of the button allows for both the camera icon and the uploaded image to be a button
Button(action: {
showingAlert = true
}) {
imageView(for: viewModel.selectedImage)
.frame(width: viewModel.frameDimensionsPublished, height: viewModel.frameDimensionsPublished, alignment: .trailing)
.cornerRadius(10)
}
.alert(isPresented: $showingAlert) { () -> Alert in
Alert(title: Text("Upload Image"), primaryButton: .default(Text("Choose Image"), action: {
viewModel.choosePhoto()
}), secondaryButton: .default(Text("Take Photo"), action: {
viewModel.takePhoto()
}))
}
.buttonStyle(PlainButtonStyle())
Spacer()
}
.fullScreenCover(isPresented: $viewModel.isPresentingImagePicker, content: {
ImagePicker(sourceType: viewModel.sourceType, completionHandler: viewModel.didSelectImage)
})
}
//this is the error popup//
.errorView(text: $errorText)
VStack {
TextField("Recipe Name", text: $recipeName).padding(.bottom).multilineTextAlignment(.leading).font(.title).padding(.leading).overlay(RoundedRectangle(cornerRadius: 10).stroke(nameErrorColor, lineWidth: 1).textFieldStyle(PlainTextFieldStyle()).frame(width: 300, height: 50, alignment: .leading).padding(.trailing, UIScreen.main.bounds.size.width / 5).padding(.bottom))
List {
//using zip, as safer than enumerated
ForEach(Array(zip(pubRecArray.recipeArrayPublished.indices, pubRecArray.recipeArrayPublished)), id: .0) { index, element in
HStack {
//Text(String(stepNumber[index]) + ".").bold()
//EditorView(container: self.$recipeStep, index: index, text: recipeStep[index])
TextField("", text: Binding<String>(get: { element }, set: { pubRecArray.recipeArrayPublished[index] = $0 }))
}
}
.onDelete(perform: removeRows)
HStack {
TextField("Step (textFieldCount)", text: $stepInfo).multilineTextAlignment(.leading).padding(.leading).overlay(
RoundedRectangle(cornerRadius: 10).stroke(stepErrorColor, lineWidth: 1).textFieldStyle(PlainTextFieldStyle()).frame(width:100, height: 30, alignment: .leading).padding(.trailing, UIScreen.main.bounds.size.width / 2))
Spacer()
Button(action: {
manageAddStepFlow()
//vm.getRecipeSteps()
}) {
Image(systemName: "plus")
}
.padding()
}
}
//added listStyle to allow list to expand to fill safe area
.listStyle(PlainListStyle())
}
Spacer()
HStack {
Button("Cancel") {
presentationMode.wrappedValue.dismiss()
}
.cornerRadius(15)
.frame(width: 75, height: 75)
.foregroundColor(.white)
.background(Color(red: 198/255, green: 40/255, blue: 40/255, opacity: 1.0))
.clipShape(Circle())
.font(.body)
.overlay(RoundedRectangle(cornerRadius: 100)
.stroke(Color(red: 198/255, green: 40/255, blue: 40/255, opacity: 1.0)))
Button("Finish") {
errorCheckOnFinishClicked()
}
.cornerRadius(15)
.frame(width: 75, height: 75)
.foregroundColor(.black)
.background(Color(red: 171/255, green: 245/255, blue: 209/255, opacity: 1.0))
.clipShape(Circle())
.font(.body)
.overlay(RoundedRectangle(cornerRadius: 100)
.stroke(Color(red: 171/255, green: 245/255, blue: 209/255, opacity: 1.0)))
}
.navigationTitle("Add Recipe")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
}
}
}
2
Answers
Edit:
You should restructure your code in "AddItemView" to be more modular.
In any case,
try this until you restructure the code:
Edit:
You could structure your code with separate structs.
"…will that lead to fix", well I don’t know but you will
certainly understand your code better if it’s organised well.
Another simple way is to do something like this with each major modules you have:
In your addItemView() wrap everything in the body in a VStack. TabView will create a new tab for everything that is directly underneath body. In your case it is a seperate tab for your ZStack, VStack, Spacer (maybe), and HStack. Once you wrap it all in a VStack then it will only create a single tab with everything inside that VStack