Keyboard pushing up buttons in Stack - swift

How do I add a scrollView without covering everything else up. I added .ignoresSafeArea(.keyboard) to the outer Stack to stop the keyboard from pushing my buttons at the bottom up but when I try to add a scrollView all that is displayed is the confirm and back buttons and navigation title and none of the form I want the user to scroll through and fill out
import SwiftUI
struct NewEventView: View{
#State private var newEventName: String = ""
#State private var newEventAddress: String = ""
#State private var newEventPrivacy: String = ""
#State private var newEventMaxAttendents: String = ""
#State private var newEventPrice: String = ""
#State private var newEventNotes: String = ""
#State private var newEventStartTime = Date.now
#State private var newEventEndTime = Date()
#State private var newEventStartDate = Date.now
#State private var newEventEndDate = Date()
#State private var newEventType: String = ""
var body: some View{
VStack{
Text("New Event")
List(){
HStack{
Text("Event Name: ")
TextField("Name", text: $newEventName)
}
HStack{
Text("Address: ")
TextField("Address", text: $newEventAddress)
}
HStack{
Picker(selection: $newEventPrivacy, label: Text("Privacy: ")) {
Text("Public ").tag("1")
Text("Private").tag("2")
}
.fixedSize()
.scaledToFit()
}
HStack{
Text("Attendance: ")
TextField("Max Attendents", text: $newEventMaxAttendents)
}
HStack{
DatePicker("Start Date ", selection: $newEventStartDate, displayedComponents: [.date])
}
.scaledToFit()
HStack{
DatePicker("Start Time ", selection: $newEventStartTime, displayedComponents: [.hourAndMinute])
}
.scaledToFit()
HStack{
DatePicker("End Date ", selection: $newEventEndDate, displayedComponents: [.date])
}
.scaledToFit()
HStack{
DatePicker("End Time ", selection: $newEventEndTime, displayedComponents: [.hourAndMinute])
}
.scaledToFit()
HStack{
Text("Price ")
TextField("Price", text: $newEventPrice)
}
HStack{
Picker(selection: $newEventType, label: Text("Type ")) {
Text("Private ").tag("1")
Text("Education ").tag("2")
Text("Concert ").tag("3")
Text("Festival ").tag("4")
Text("Food ").tag("5")
Text("Religious ").tag("6")
Text("Attraction").tag("7")
Text("Business ").tag("8")
}
.fixedSize()
}
}
// HStack for confirm and cancel buttons
HStack{
// BASH button
Button(action: {
print("Confirm")
}, label: {
Text("Confirm")
.font(.headline)
.fontWeight(.semibold)
.foregroundColor(.white)
.padding()
.padding(.horizontal, 20)
.background{
Color.red
.cornerRadius(10)
.shadow(radius: 5)
}
}) // confirm button
// back button
Button(action: {
print("Back")
}, label: {
Text(" Back ")
.font(.headline)
.fontWeight(.semibold)
.foregroundColor(.white)
.padding()
.padding(.horizontal, 20)
.background{
Color.red
.cornerRadius(10)
.shadow(radius: 5)
}
}) // BASH button
} // HStack for confirm and back buttons
} // VStack
.ignoresSafeArea(.keyboard)
} // View
}

Related

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

NavigationLink Dismisses after Textfield Value is Updated to Firestore

I'm trying to make something similar to the iOS notes app but for journaling; basically, I want there to be a list of journal entry cells users can scroll through which each display a detail view after they're clicked on where the user can view and edit their journal entry. The updating works fine, the only issue is that JournalDetailView dismisses itself after updateEntry() is called (after the user taps the "Done" button). I'm guessing this is because updateEntry() forces the view to reload, but I'm not sure how to get around this.
Here's the model:
struct JournalEntry: Identifiable, Hashable, Codable {
#DocumentID var id: String? = UUID().uuidString
#ServerTimestamp var date: Timestamp?
var text: String
var userId: String?
}
Here's the view code:
struct JournalCellView: View {
#ObservedObject var vm: JournalViewModel
#Binding var addButtonTapped: Bool
#State var showDetail = false
#State var entry: JournalEntry
var body: some View {
NavigationLink(destination: JournalDetailView(vm: vm, entry: $entry, text: entry.text), isActive: $showDetail, label: {
VStack {
HStack {
Text(entry.date!.dateValue(), style: .date)
.fontWeight(.bold)
.font(.system(size: 18))
.foregroundColor(.black)
.padding(.bottom, 3)
Spacer()
}
HStack {
Text(entry.text)
.font(.system(size: 14))
.foregroundColor(.secondary)
.lineLimit(1)
Spacer()
}
}.padding()
.background(RoundedRectangle(cornerRadius: 18).foregroundColor(.white))
.padding(.vertical, 4)
.onTapGesture {
showDetail = true
}
.onAppear {
if addButtonTapped {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
showDetail = true
}
addButtonTapped = false
}
}
})
}
}
struct JournalDetailView: View {
#Environment(\.presentationMode) var presentationMode
#ObservedObject var vm: JournalViewModel
#Binding var entry: JournalEntry
#State var text: String
#State var isTyping = false
var body: some View {
VStack {
HStack {
Button(action: {
presentationMode.wrappedValue.dismiss()
}, label: { Image(systemName: "chevron.left").foregroundColor(.burgundy) })
Spacer()
if isTyping {
Button(action: {
endEditing()
updateEntry()
isTyping = false
}) {
Text("Done")
.foregroundColor(.burgundy)
}
} else {
Text("")
}
}.padding(.vertical)
Text(entry.date!.dateValue(), style: .date)
TextEditor(text: $text)
.onTapGesture {
isTyping = true
}
Spacer()
}.padding()
.navigationBarHidden(true)
}
func updateEntry() {
vm.updateJournalEntry(docID: entry.id!, date: entry.date!, text: text)
}
}
Here's updateJournalEntry():
func updateJournalEntry(docID: String, date: Timestamp, text: String) {
db.collection("journals").document(docID
).updateData(["date": date, "text": text, "userId": Auth.auth().currentUser!.uid])
}
I managed to get around this by only updating after the view would be dismissed naturally using .onDisappear and .onReceive. Not the cleanest solution, but it works. If someone has another suggestion, please contribute!
.onDisappear {
updateEntry()
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) { _ in
updateEntry()
}

SwiftUI Textfield not actually making updates to data

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

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)

How to make a button clickable to another view controller only with textfield

I have this scenario that if the user clicks the button then another view controller will popuped which includes only a textView where the user can write something inside.
You can use the the .sheet modifier to display a modal / popup like view in SwiftUI.
struct ContentDetail: View {
struct Item {
let uuid = UUID()
let value: String
}
#State private var items = [Item]()
#State private var show_modal = false
var lectureName:String
var body: some View {
ZStack {
VStack {
Spacer()
HStack {
Spacer()
Button(action: {
self.show_modal.toggle()
}, label: {
Text("✏️")
.font(.system(.largeTitle))
.frame(width: 77, height: 70)
.foregroundColor(Color.white)
.padding(.bottom, 7)
})
.background(Color.blue)
.cornerRadius(38.5)
.padding()
.shadow(color: Color.black.opacity(0.3),
radius: 5,
x: 3,
y: 3)
}
}
}
.sheet(isPresented: self.$show_modal) {
CustomModalView()
}
}
}
struct CustomModalView: View {
#State private var text = ""
var body: some View {
TextField("test", text: $text)
.padding(5)
.textFieldStyle(RoundedBorderTextFieldStyle())
.font(.system(size: 60, design: .default))
.multilineTextAlignment(.center)
}
}
You can read more about it here:
https://blog.appsbymw.com/posts/how-to-present-and-dismiss-a-modal-in-swiftui-155c/