How can I reduce the spacing between Sections on a form for the following view?
var body: some View {
Form {
Section {
TextField("Name", text: $patient.name, onEditingChanged: { changed in
if !isNewPatient {
isSaveDisabled = false
}
}, onCommit: {
if !isNewPatient {
isSaveDisabled = false
}
})
.validation(patient.nameValidation)
TextField("Surname", text: $patient.surname)
.validation(patient.surnameValidation)
TextField("ID Number", text: $patient.id)
.validation(patient.idValidation)
.validation(patient.idRegexValidation)
.disabled(!self.isNewPatient)
.foregroundColor(isNewPatient ? .black : .gray)
}
Section {
TextField("Street Number and Name", text: $patient.street)
TextField("Suburb", text: $patient.suburb)
TextField("Area Code", text: $patient.postCode)
.validation(patient.postCodeRegexValidation)
}.isHidden(isDependant, remove: true)
Section {
Picker("Medical Aid", selection: $patient.medicalAidName) {
ForEach(medicalAids, id: \.self) {
Text($0)
}
}
TextField("Medical Aid Number", text: $patient.medicalAidNo)
}.isHidden(isDependant, remove: true)
Section {
Button("Save") {
if isNewPatient {
if patient.create(moc: moc, isDependant: isDependant, mainID: self.mainID) {
isDependantsActive.toggle()
}
} else {
if patient.update(moc: moc) {
self.presentationMode.wrappedValue.dismiss()
}
}
}.disabled(self.isSaveDisabled)
}
NavigationLink("Dependants", destination:
DependantListView(filter: mainID).environment(\.managedObjectContext, moc), isActive: $isDependantsActive
).disabled(isNewPatient).isHidden(isDependant, remove: true)
}
.onReceive(patient.allRequiredValidation) { validation in
if isNewPatient {
self.isSaveDisabled = !validation.isSuccess
}
}
.navigationBarTitle(title, displayMode: .inline)
}
}
This is how the form looks:
I've had a look at the following answers but they're not acceptable:
Spacing between sections in a form
Try to add the following in the View containing Form
init() {
UITableView.appearance().sectionHeaderHeight = .zero
}
Related
import SwiftUI
struct AddChildrenView: View {
#State var word : String = ""
#State var meaning : String = ""
#State var isShowAlert : Bool = false
#State var isClicked : Bool = false
#State var searchText : String = ""
#EnvironmentObject var itemModel : ItemModel
var item : Item
var searchItem : [Children] {
if searchText.isEmpty {
return item.children
} else {
return item.children.filter({$0.word!.contains(searchText)})
}
}
init() {
UINavigationBar.appearance().titleTextAttributes = [.font : UIFont(name: "NotoSans-Bold", size: 20)!]
}
var body: some View {
NavigationView {
ZStack {
if item.children.count == 0 {
NochildrenView()
} else {
List {
ForEach(searchItem) {child in
WordListRowView(children: child)
.swipeActions(edge: .trailing, allowsFullSwipe: true, content: {
Button(role: .destructive, action: {
itemModel.deleteChildren(children: child, item: item)
}, label: {
Image(systemName: "trash.fill")
})
Button(action: {
itemModel.favoriteChildren(item: item, children: child)
}, label: {
Image(systemName: "star.circle.fill")
.font(.title3)
})
.tint(.green)
})
}
}
.listStyle(.insetGrouped)
.padding(.top, -10)
.searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always))
.autocapitalization(.none)
}
}
.navigationBarTitle("\(item.group!)'s Words")
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(trailing:
HStack {
Button(action: {
addChildren()
}, label: {
Image(systemName: "plus")
})
Button(action: {
itemModel.shuffleChildren(item: item)
}, label: {
Image(systemName: "shuffle")
})
NavigationLink(destination: FlashCardView(item: item)) {
Image(systemName: "play.rectangle")
}
.disabled(item.children.isEmpty)
})
}
}
}
extension AddChildrenView {
func addChildren() {
let alert = UIAlertController(title: "Saving word", message: "Type word and meaning 😀", preferredStyle: .alert)
alert.addTextField { word in
word.placeholder = "Type a word"
word.keyboardType = .alphabet
}
alert.addTextField { meaning in
meaning.placeholder = "a meaning of word"
}
let addfolderAction = UIAlertAction(title: "Add", style: .default, handler: {
(_) in
self.word = alert.textFields![0].text!
self.meaning = alert.textFields![1].text!
itemModel.addNewChildren(item: item, word: word, meaning: meaning)
})
let cancelAction = UIAlertAction(title: "Cancel", style: .destructive, handler: {
(_) in
})
alert.addAction(cancelAction)
alert.addAction(addfolderAction)
UIApplication.shared.windows.first?.rootViewController?.present(alert, animated: true, completion: nil)
}
}
Hello, first, this view is the second view after click the navigationLink in firstView.
init() {
UINavigationBar.appearance().titleTextAttributes = [.font : UIFont(name: "NotoSans-Bold", size: 20)!]
}
I tried this code to use custom font on my navigationBartitle, but there is error like this.
"Return from initializer without initializing all stored properties"
Could you let me know the way I can solve this problem?
Thank you!
This is my NavigationLink DetailView,
import SwiftUI
struct AddChildrenView: View {
#State var word : String = ""
#State var meaning : String = ""
#State var isShowAlert : Bool = false
#State var isClicked : Bool = false
#EnvironmentObject var itemModel : ItemModel
var item : Item
var body: some View {
ZStack {
if item.children.count == 0 {
NochildrenView()
}
List {
ForEach(item.children) { child in
WordListRowView(children: child)
.onTapGesture {
withAnimation(.linear(duration: 0.3)) {
itemModel.favoriteChildren(item: item, children: child)
}
}
}
.onDelete { indexSet in
itemModel.deleteChildren1(item: item, indexSet: indexSet)
}
}
.id(UUID())
}
.navigationBarTitle("\(item.group!)'s Words")
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(trailing:
HStack {
Button(action: {
addChildren()
}, label: {
Image(systemName: "plus")
})
Button(action: {
itemModel.shuffleChildren(item: item)
}, label: {
Image(systemName: "shuffle")
})
NavigationLink(destination: FlashCardView(item: item)) {
Image(systemName: "play.rectangle")
}
.disabled(item.children.isEmpty)
})
}
}
extension AddChildrenView {
func addChildren() {
let alert = UIAlertController(title: "Saving word", message: "Type word and meaning 😀", preferredStyle: .alert)
alert.addTextField { word in
word.placeholder = "Type a word"
}
alert.addTextField { meaning in
meaning.placeholder = "a meaning of word"
}
let addfolderAction = UIAlertAction(title: "Add", style: .default, handler: {
(_) in
self.word = alert.textFields![0].text!
self.meaning = alert.textFields![1].text!
itemModel.addNewChildren(item: item, word: word, meaning: meaning)
})
let cancelAction = UIAlertAction(title: "Cancel", style: .destructive, handler: {
(_) in
})
alert.addAction(addfolderAction)
alert.addAction(cancelAction)
UIApplication.shared.windows.first?.rootViewController?.present(alert, animated: true, completion: nil)
}
}
This is MotherView with NavigationLink,
import SwiftUI
struct HomeView: View {
#State var folderName : String = ""
#EnvironmentObject var itemModel : ItemModel
var body: some View {
NavigationView{
ZStack {
if itemModel.items.count == 0 {
NoItemView()
}
List{
ForEach(itemModel.items) {item in
if let group = item.group {
NavigationLink(destination: {
AddChildrenView(item : item)
}, label: {
HStack{
Image(systemName: "folder")
.foregroundColor(.yellow)
Text(group)
.font(.headline)
.lineLimit(1)
}
})
}
}
.onMove(perform: itemModel.moveItem)
.onDelete(perform: itemModel.deleteItem)
}
.listStyle(.plain)
}
.navigationViewStyle(StackNavigationViewStyle())
.navigationTitle("SelfDic 📒")
.navigationBarItems(trailing:
Button(action: {
addFolderView()
}, label: {
Image(systemName: "folder.badge.plus")
}))
.navigationBarItems(leading: EditButton())
}
}
}
extension HomeView {
func addFolderView() {
let alert = UIAlertController(title: "Saving folder", message: "Type a name of folder 🙂", preferredStyle: .alert)
alert.addTextField { name in
name.placeholder = "folder's name"
}
let addfolderAction = UIAlertAction(title: "Add", style: .default, handler: {
(_) in
self.folderName = alert.textFields![0].text!
itemModel.addNewFolder(text: folderName)
})
let cancelAction = UIAlertAction(title: "Cancel", style: .destructive, handler: {
(_) in
})
alert.addAction(addfolderAction)
alert.addAction(cancelAction)
UIApplication.shared.windows.first?.rootViewController?.present(alert, animated: true, completion: nil)
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
}
}
**I wanted to use Zstack in my linked View
The logic is that, If there is no item.children, I will show NoChildrenView().
But, It doesn't work.
How can I treat this problem?**
I hope I can solve this!
Thank you all senior developers!!
I didn't fully check all code, but in AddChildrenView there might be missing an else. Like you have it now the empty list will be always shown above your NochildrenView()
if item.children.count == 0 {
NochildrenView()
} else { // here
...
I am trying to create a multi-select list:
#Binding var selection:[String]
List {
ForEach(self.items, id: \.self) { item in
MultipleSelectionRow(title: item, isSelected: self.selection.contains(item)) {
if self.selection.contains(item) {
self.selection.removeAll(where: { $0 == item }) <=== NO AFFECT
}
else {
self.selection.append(item). <=== NO AFFECT
}
self.queryCallback()
}
}//ForEach
.listRowBackground(Color("TPDarkGrey"))
}//list
I have a row which is a button that calls the above action
struct MultipleSelectionRow: View {
var title: String
var isSelected: Bool
var action: () -> Void
var body: some View {
Button(action: self.action) {
HStack {
Text(self.title)
Spacer()
if self.isSelected {
Image(systemName: "checkmark")
}
}
.font(.system(size: 14))
}
}
}
Why does it not append or remote the item in the bound array? It seems to change on the second time through the view
I managed to produce an example from your code that works:
I don't know how the rest of your code is setup so I cannot hint you to anything unfortunately.
struct MultipleSelectionRow: View {
var title: String
var isSelected: Bool
var action: () -> Void
var body: some View {
Button(action: self.action) {
HStack {
Text(self.title)
Spacer()
if self.isSelected {
Image(systemName: "checkmark")
}
}
.font(.system(size: 14))
}
}
}
struct ContentView: View {
#State var selection:[String] = []
#State var items:[String] = ["Hello", "my", "friend", "did", "I", "solve", "your", "question", "?"]
var body: some View {
List {
ForEach(self.items, id: \.self) { item in
MultipleSelectionRow(title: item, isSelected: self.selection.contains(item)) {
if self.selection.contains(item) {
self.selection.removeAll(where: { $0 == item })
}
else {
self.selection.append(item)
}
}
}
.listRowBackground(Color("TPDarkGrey"))
}
}
}
I hope this helps to clarify things.
I want to make every button change its colour when I tap on it and change colour back when I tap again. So I made a boolean didTap and according to its value, it should change its background.
The code at the moment changes every button background. As I understand from other posts, this system for Buttons is not working perfectly.
What to do? How to get the result i need? Should I use something else(not Button) ? Should I use something like this didTapB1 and so on?(Seems its gonna be a long code if using that?)
import SwiftUI
var headLine = ["B", "I", "N", "G", "O"]
var numB = ["5","9","11","15","9"]
var numI = ["16","19","21","25","22"]
var numN = ["35","39","41","45","42"]
var numG = ["55","59","61","57","52"]
var numO = ["66","69","71","75","72"]
struct TestView: View {
#State private var didTap:Bool = false
var body: some View {
ZStack {
Color.orange
.edgesIgnoringSafeArea(.all)
HStack {
VStack {
Text("B")
ForEach(numB, id: \.self) { tekst in
Button(action: {
if self.didTap == false {
self.didTap = true
} else {
self.didTap = false
}
}) {
Text(tekst)
.padding()
.background(self.didTap ? Color.red : Color.black)
.clipShape(Circle())
}
}
}
VStack {
Text("I")
ForEach(numI, id: \.self) { tekst in
Button(action: {
if self.didTap == false {
self.didTap = true
} else {
self.didTap = false
}
}) {
Text(tekst)
.padding()
.background(self.didTap ? Color.red : Color.black)
.clipShape(Circle())
}
}
}
VStack {
Text("N")
ForEach(numN, id: \.self) { tekst in
Button(action: {
if self.didTap == false {
self.didTap = true
} else {
self.didTap = false
}
}) {
Text(tekst)
.padding()
.background(self.didTap ? Color.red : Color.black)
.clipShape(Circle())
}
}
}
VStack {
Text("G")
ForEach(numG, id: \.self) { tekst in
Button(action: {
if self.didTap == false {
self.didTap = true
} else {
self.didTap = false
}
}) {
Text(tekst)
.padding()
.background(self.didTap ? Color.red : Color.black)
.clipShape(Circle())
}
}
}
VStack {
Text("O")
ForEach(numO, id: \.self) { tekst in
Button(action: {
if self.didTap == false {
self.didTap = true
} else {
self.didTap = false
}
}) {
Text(tekst)
.padding()
.background(self.didTap ? Color.red : Color.black)
.clipShape(Circle())
}
}
}
}
}
}
}
You need to add didTap to every Button. You can simply do it by creating custom view:
struct BingoButton: View {
var text: String
#State private var didTap = false
var body: some View {
Button(action: {
self.didTap.toggle()
}) {
Text(text)
.padding()
.background(didTap ? Color.red : Color.black)
.clipShape(Circle())
}
}
}
And then you can change your implementation to something like this:
VStack {
Text("I")
ForEach(numI, id: \.self) { tekst in
BingoButton(text: tekst)
}
}
}
You can consider changing your model and make your UI definition smaller and non-repetitive:
struct BingoRow: Identifiable {
let id = UUID()
let headline: String
let numbers: [String]
}
struct SimpleView: View {
var rows = [
BingoRow(headline: "B", numbers: ["5","9","11","15","9"]),
BingoRow(headline: "I", numbers: ["16","19","21","25","22"]),
BingoRow(headline: "N", numbers: ["35","39","41","45","42"]),
BingoRow(headline: "G", numbers: ["55","59","61","57","52"]),
BingoRow(headline: "O", numbers: ["66","69","71","75","72"])
]
var body: some View {
HStack {
ForEach(rows) { row in
VStack {
Text(row.headline)
ForEach(row.numbers, id: \.self) { text in
BingoButton(text: text)
}
}
}
}
}
}
I'm trying to use onTapGesture but doing so makes it nearly impossible to access the list - if I remove onTapGesture it works perfectly
Here's what I'm working with:
struct NavPickerView: View {
#State var selected: Int = -1
#State var cities: [String] = ["New York", "San Diego", "Houston", "Boston"]
#State var selectionStarted: Bool = false
var body: some View {
VStack {
Text("\(selectionStarted ? "SELECTING" : "WAITING")")
NavigationView {
Form {
Section {
Picker(selection: $selected, label: Text("Search for...")) {
Text("Select City").tag(-1)
ForEach(0..<cities.count) {
Text(verbatim: self.cities[$0]).tag($0)
}
}
}
}
.onTapGesture { self.selectionStarted = true }
.navigationBarTitle(Text("Cities"))
}
}
}
}
Any way of running code when an item is selected without making the list nearly inaccessible?
In your case, it seems .onAppear is a better fit:
Form {
Section {
Picker(selection: $selected, label: Text("Search for...")) {
Text("Select City").tag(-1)
ForEach(0..<cities.count) {
Text(verbatim: self.cities[$0]).tag($0)
}
.onAppear { self.selectionStarted = true }
}
}
}