I'm using a Form to create some TextFields, but I'm wondering how to align the input fields (notice the "Required" placeholders do not have the same leading alignment).
The code
NavigationView {
Form {
Section {
HStack {
Text("Full Name")
.fontWeight(.semibold)
TextField("Required", text: $companyName)
}
HStack {
Text("JobTitle")
.fontWeight(.semibold)
TextField("Required", text: $companyName)
}
}
}
.navigationTitle(Text("Title"))
}
The Result
This is a "hack" that will align these both as if they both said "Full Name". The second "full name" is hidden.
NavigationView {
Form {
Section {
HStack {
Text("Full Name")
.fontWeight(.semibold)
TextField("Required", text: $companyName)
}
HStack {
ZStack{
Text("Full Name")
.fontWeight(.semibold)
.hidden() // <-- HERE! This will fix alignment but hide text
Text("JobTitle")
.fontWeight(.semibold)
}
TextField("Required", text: $companyName)
}
}
}
.navigationTitle(Text("Title"))
}
This will make SwiftUI align it correctly but still show the text. Another option is the .offset modifier (docs here: https://developer.apple.com/documentation/swiftui/view/offset(x:y:)).
Related
I'm using text field in Tab view, but when keyboard shows out. There has a space upon keyboard.
enter image description here
var body: some View {
TabView {
TestView()
}
}
var body: some View {
VStack {
ScrollView(.vertical, showsIndicators: false) {
ForEach(0..<100) { data in
Text("\(data)")
}
}
Spacer()
HStack {
Image(systemName: "paperplane")
TextField("test field", text: $test)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
}
.padding()
.ignoresSafeArea(.keyboard, edges: .bottom)
}
If I'm using without Tab view, the keyboard works totally fine.
enter image description here
I took some search and put .ignoresSafeArea(.keyboard, edges: .bottom), I don't know why it still doesn't work.
Modifier should be applied in correct place:
HStack {
Image(systemName: "paperplane")
TextField("test field", text: $test)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
.ignoresSafeArea(.keyboard, edges: .bottom) // << here !!
Tested with Xcode 13.4 / iOS 15.5
What I want to achieve is a form with a different background color. But I want the items inside always using the light mode variant (dark text colors). But not the complete view. I've searched for it but couldn't find any solution.
I've tried listRowBackground to change background of the items which works and preferredColorScheme to use the light mode but that will make the complete view in light mode:
...
Form {
DatePicker(selection: $date, displayedComponents: .date) { ... }
.listRowBackground(Color.yellow)
TextField("Name", text: $name)
.listRowBackground(Color.yellow)
Picker("amount", selection: $amount) {
ForEach(amounts, id: \.self) {
Text($0)
}
}
.listRowBackground(Color.yellow)
Button(action: { ... }) {
Label("More", systemImage: "chevron.up")
}
.listRowBackground(Color.yellow)
}
.preferredColorScheme(.light)
...
You can use .environment to force the color scheme of the view:
.environment(\.colorScheme, .light)
If you don't want to do this for the whole form, you can inject it to the nested views only:
Form {
Group {
DatePicker(selection: $date, displayedComponents: .date) { ... }
.listRowBackground(Color.yellow)
TextField("Name", text: $name)
.listRowBackground(Color.yellow)
Picker("amount", selection: $amount) {
ForEach(amounts, id: \.self) {
Text($0)
}
}
.listRowBackground(Color.yellow)
Button(action: { ... }) {
Label("More", systemImage: "chevron.up")
}
.listRowBackground(Color.yellow)
}
.environment(\.colorScheme, .light)
}
(or to a single view only)
Form {
// ...
TextField("Name", text: $name)
.listRowBackground(Color.yellow)
.environment(\.colorScheme, .light)
// ...
}
You can use foregroundColor(Color.black) or accentColor(Color.black) for this work:
import SwiftUI
struct ContentView: View {
#State private var string: String = String()
#State var selectedColor: Int = 2
let colorArray: [String] = ["Black", "Blue", "Gray", "Green"]
var body: some View {
VStack(spacing: 20) {
Text("This text has a dark text color!")
.foregroundColor(Color.black)
.padding()
TextField("Enter your text here. . .", text: $string)
.background(Color.red)
.foregroundColor(Color.black)
Picker(selection: $selectedColor, label: Text(String())) {
ForEach(colorArray.indices, id: \.self) { index in
Text(colorArray[index])
.foregroundColor(Color.black)
}
}
Text("This text has a normal text color!")
.foregroundColor(Color.primary)
.padding()
}
.padding()
.background(Color.red)
.cornerRadius(10)
}
}
I have a View which shows Text view to show help text for user to tap on plus icon to add group. Once group is added, it shows List view. To show navigation bar, I need to call navigationBarTitle and navigationBarItems on both Text and List view. Below is my code snippet.
import SwiftUI
struct Home:View {
#EnvironmentObject var dataStore:DataStore
var body: some View {
NavigationView {
if dataStore.groups.isEmpty {
Text("Tap on + icon to add group.")
.font(.caption)
.multilineTextAlignment(.center)
.padding()
.foregroundColor(.gray)
.navigationBarTitle(Text("My App Name"), displayMode: .automatic)
.navigationBarItems(
trailing:
NavigationLink(
destination:
CreateGroup(),
label: {
Image(systemName: "plus")
.foregroundColor(Color.blue)
})
)
} else {
List(dataStore.groups) { groupElement in
GroupRow(group: groupElement)
}
.navigationBarTitle(Text("My App Name"), displayMode: .automatic)
.navigationBarItems(
trailing:
NavigationLink(
destination:
CreateGroup(),
label: {
Image(systemName: "plus")
.foregroundColor(Color.blue)
})
)
}
}
}
}
Is there a way to call navigationBarTitle and navigationBarItems only once rather than calling on both Text and List views?
Is there a way to call navigationBarTitle and navigationBarItems only
once rather than calling on both Text and List views?
Yes, you can wrap condition into any container, like Group or xStack:
struct Home:View {
#EnvironmentObject var dataStore:DataStore
var body: some View {
NavigationView {
Group {
if dataStore.groups.isEmpty {
Text("Tap on + icon to add group.")
.font(.caption)
.multilineTextAlignment(.center)
.padding()
.foregroundColor(.gray)
} else {
List(dataStore.groups) { groupElement in
GroupRow(group: groupElement)
}
}
}
.navigationBarTitle(Text("My App Name"), displayMode: .automatic)
.navigationBarItems(
trailing:
NavigationLink(
destination:
CreateGroup(),
label: {
Image(systemName: "plus")
.foregroundColor(Color.blue)
})
)
}
}
}
In the following SwiftUI code I noticed some unexpected behaviour.
I wonder if this is a bug, if this is normal or if I am only missing something obvious.
List {
ForEach(self.myList, id: \.self.name) {
item in
HStack {
Spacer()
Button(action: {
print("Button One tapped!")
....
}) {
item.name.map(Text.init)
.font(.largeTitle)
.foregroundColor(.secondary)
}
Spacer()
Button(action: {
print("Button Two tapped!")
....
}) {
Image(systemName: "pencil.circle")
.font(.title)
.foregroundColor(.secondary)
.padding(.leading, 17)
}
}
}
.onDelete(perform: deleteFunc)
}
Now here is what happens when tapping one of the two buttons on a row.
I can see these two messages:
Button One tapped!
Button Two tapped!
I expect to see only one message, depending on the button tapped.
If the order of the messages varied according to the button tapped; I could use a boolean or two to enforce the end result I want. But the two messages always appear in the same order.
Has anyone had the same experience? Or does anyone see any mistake?
Use PlainButtonStyle (or any custom one), because default button style is detected by List automatically to highlight entire row.
Here is a simplified (from your code) demo:
struct DemoListWithButtons: View {
var body: some View {
List {
ForEach(0..<5, id: \.self) {
item in
HStack {
Spacer()
Button(action: {
print("Button One tapped!")
}) {
Text("First")
}.buttonStyle(PlainButtonStyle()) // << here !!
Spacer()
Button(action: {
print("Button Two tapped!")
}) {
Text("Second")
}.buttonStyle(PlainButtonStyle()) // << here !!
}
}
}
}
}
I'm trying to figure out how to use the navigation bar in SwiftUI
I want to put BarButtonItem and images inside the NavigationBar
I have been able to display the navigation bar and put titles
var body: some View {
NavigationView{
List(0...5) { note in
VStack(alignment: .leading) {
Text("title")
Text("Date")
.font(.subheadline)
.foregroundColor(.secondary)
}
}
.navigationBarTitle(Text("Notes"))
}
}
iOS 14
You should use the toolbar modifier:
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Cancel") { /* action */ }
}
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: { /* Actions */ }, label: {
HStack {
Image(systemName: "trash")
Text("Delete")
}
})
.foregroundColor(.red) // You can apply colors and other modifiers too
}
}
Note 1: You can have ANY View there. (not only a Button) and also any modifiers
Note 2: Both codes above and below will generate the same look items but with different approachs
iOS 13 and above (deprecated but still works)
You should use .navigationBarItems() modifier. For example you can add Button or Image like this:
.navigationBarItems(
leading: Button("Cancel") {
// Actions
},
trailing: Button(action: {
// Actions
}, label: { Label("Delete", systemImage: "trash") }
).foregroundColor(.red) // You can apply colors and other modifiers too
)
💡 Pro TIP
Always try to encapsulate each item in a separated struct, so your code will be simplified and very easy to replace with newer technologies. for example, take a look at this sample:
.navigationBarItems(
leading: MyCustomButtonItem(),
trailing: MyCustomButtonItem(text: "foo", image: "Bard")
)
.navigationBarItems() is the function you are looking for. You can specify a leading view, trailing view, or both. Within the view, you can specify horizontal and vertical stacks to add additional buttons.
var body: some View {
NavigationView{
List(0...5) { note in
VStack(alignment: .leading) {
Text("title")
Text("Date")
.font(.subheadline)
.foregroundColor(.secondary)
}
}
.navigationBarItems(leading: HStack {
Button(action: {}, label: {Image(systemName: "star.fill")})
Button(action: {}, label: {Text("Edit")})
}, trailing: VStack {
Button(action: {}, label: {Image(systemName: "star.fill")})
Button(action: {}, label: {Text("Edit")})
})
.navigationBarTitle(Text("Notes"))
}
}
SwiftUI 2
In SwiftUI 2 / iOS 14 the navigationBarItems modifier is deprecated.
Instead we should use a toolbar with ToolbarItems.
NavigationView {
List {
// ...
}
.navigationTitle("Notes")
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Tap me") {
// action
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Image(systemName: "plus")
}
}
}
You can see the documentation for more ToolbarItemPlacements.
Check the image here
The toolbar() modifier lets us add single or multiple bar button items to the leading and trailing edge of a navigation view, as well as other parts of our view if needed. These might be tappable buttons, but there are no restrictions – you can add any sort of view.
var body: some View {
NavigationView {
ScrollView {
VStack{
}//: VStack
}//: Scroll
.navigationTitle("Settings")
.toolbar(content: {
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button {
presentationMode.wrappedValue.dismiss()
} label: {
Image(systemName: "xmark")
}
}
})
.padding()
}//: Navigation
}
put this from parentViewController
NavigationLink(destination: NotesListView())