I am creating a SwiftUI settings view where the user can manage account settings in the app.
The problem is my NavigationBar is white with the current modifiers.
If I don't use the .clipped() modifier then the NavigationBar matches the Section gray background color BUT when scrolling the Settings view up it is shown above the NavigationBar as seen in the first image.
**.clipped() is used to prevent the behavior above BUT then it makes the whole NavigationBar white so it doesn't match the default gray color that it should be.
Also, My grouped section items are not showing the default Divider lines that should be there.
2 Questions
How can I make my Navigation Bar match the gray Section background color.
How can i get the grouped together items in the same section to have Divider lines between them??
struct Settings: View {
var body: some View {
NavigationView {
List {
Section(header:
Text("SUBSCRIPTIONS")
.foregroundColor(.black)) {
SettingsItem(
title: "subscriptionDetails().title",
content: "subscriptionDetails().level",
contentColor: .blue,
action: { print("Action") })
}
Section(header:
Text("ACCOUNT SETTINGS")
.foregroundColor(.black)) {
SettingsItem(title: "Email", action: { print("Action")})
SettingsItem(title: "Gender", action: { print("Action") })
SettingsItem(title: "Phone Number", action: { print("Action") })
})
}
Section(header:
Text("PREFERENCES")
.foregroundColor(.black)) {
SettingsItem(title: "Notifications", action: { print("Action") })
}
Section(header:
Text("SECURITY")
.foregroundColor(.black)) {
SettingsItem(title: "Password", action: { print("Action") })
}
Section(header: Text("ABOUT")
.foregroundColor(.black)) {
SettingsItem(title: "FAQ", action: { print("Action") })
SettingsItem(title: "About", action: { print("Action") })
SettingsItem(title: "Contact", action: { print("Action") })
}
Section(header:
HStack {
Spacer()
Text("Version 1.0.0")
Spacer()
}.foregroundColor(.black)) {
EmptyView()
}
}
.clipped()
.edgesIgnoringSafeArea(.bottom)
.listStyle(.grouped)
.navigationBarTitle("Settings", displayMode: .inline)
.navigationBarBackButtonHidden(true)
.toolbar(content: {
ToolbarItem(placement: .navigationBarLeading) {
DoneButton(action: { print("Done") })
}
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
print("Logout")
}, label: {
Text("Log Out")
.font(.callout).bold()
.foregroundColor(.red)
})
}
})
}
}
}
struct SettingsItem: View {
var title: String
var content: String?
var contentColor: Color?
let action: () -> Void
var body: some View {
HStack {
Text(title)
.font(.system(size: 18, weight: .regular))
.foregroundColor(.black)
Spacer()
if let content = content, let contentColor = contentColor {
Text(content).padding(.trailing, 10)
.foregroundColor(contentColor)
}
Image(systemName: "chevron.right")
.font(.system(size: 16, weight: .bold))
.foregroundColor(.gray)
}.onTapGesture { print("Action") }
}
}
Related
I have a Picker within a Form. I don't want to show any label on this Picker, so I set the label to EmptyView(). Though, the UI still presents an empty space where the label would be. I'm looking for a way to remove this space.
var body: some View {
NavigationView{
Form {
...
Section(header: Text("Emails")){
HStack{
Picker(selection: $phoneType, label: EmptyView()){
ForEach(phoneTypes, id: \.self){
Text($0)
}
}
.pickerStyle(.navigationLink)
Spacer()
Link("my#email.com", destination: URL(string: "mailto:my#email.com")!)
}
Button("Add Email") {}
}
...
}
}
.navigationTitle("John Doe")
}
The correct approach is to use labelsHidden() to hide the label of Picker.
Picker(selection: $phoneType, label: EmptyView()){
ForEach(phoneTypes, id: \.self){
Text($0)
}
}
.labelsHidden()
.pickerStyle(.navigationLink)
A possible workaround is using Picker inside Menu:
HStack{
Menu {
Picker("", selection: $phoneType){
ForEach(["Work","Home","Cell"], id: \.self){ type in
Button(type) { phoneType = type }
}
}
} label: {
Text(phoneType).foregroundColor(.secondary)
}
Spacer()
Link("my#email.com", destination: URL(string: "mailto:my#email.com")!)
}
Put the Text in the loop inside an HStack and add a trailing Spacer
var body: some View {
NavigationView{
Form {
...
Section(header: Text("Emails")){
HStack{
Picker(selection: $phoneType, label: EmptyView()){
ForEach(phoneTypes, id: \.self){ t in
HStack {
Text(t)
Spacer()
}
}
}
.pickerStyle(.navigationLink)
Spacer()
Link("my#email.com", destination: URL(string: "mailto:my#email.com")!)
}
Button("Add Email") {}
}
...
}
}
.navigationTitle("John Doe")
}
I am new in SwiftUi and I am trying to show Alert message with TextField and action button, I want to use Alert function for this, but I can't add TextField in Alert function,I shared code at below, how can I add TextField in Alert function? thanks..
.alert(isPresented:$showAlertDialog) {
Alert(
title: Text("Please Enter sended E-mail Code"),
message: Text("There is no undo"),
primaryButton: .destructive(Text("Submit")) {
print("Deleting...")
},
secondaryButton: .cancel()
)
}
As of iOS 15 alerts are not capable of showing TextFields. But you can make a custom SwiftUI alert.
struct TextFieldAlert: ViewModifier {
#Binding var isPresented: Bool
let title: String
#Binding var text: String
let placeholder: String
let action: (String) -> Void
func body(content: Content) -> some View {
ZStack(alignment: .center) {
content
.disabled(isPresented)
if isPresented {
VStack {
Text(title).font(.headline).padding()
TextField(placeholder, text: $text).textFieldStyle(.roundedBorder).padding()
Divider()
HStack{
Spacer()
Button(role: .cancel) {
withAnimation {
isPresented.toggle()
}
} label: {
Text("Cancel")
}
Spacer()
Divider()
Spacer()
Button() {
action(text)
withAnimation {
isPresented.toggle()
}
} label: {
Text("Done")
}
Spacer()
}
}
.background(.background)
.frame(width: 300, height: 200)
.cornerRadius(20)
.overlay {
RoundedRectangle(cornerRadius: 20)
.stroke(.quaternary, lineWidth: 1)
}
}
}
}
}
extension View {
public func textFieldAlert(
isPresented: Binding<Bool>,
title: String,
text: Binding<String>,
placeholder: String = "",
action: #escaping (String) -> Void
) -> some View {
self.modifier(TextFieldAlert(isPresented: isPresented, title: title, text: text, placeholder: placeholder, action: action))
}
}
Use it on the root view. (don't attach it to Buttons to other inner elements for example). This will ensure that presenting view is disabled.
Sample usage:
struct ContentView: View {
#State var isAlertDisplayed = false
#State var text = "Text to change"
var body: some View {
VStack {
Text("Press the button to show the alert").multilineTextAlignment(.center)
Text("Current value is: \(text)")
Button("Change value") {
withAnimation {
isAlertDisplayed = true
}
}
.padding()
}
.textFieldAlert(isPresented: $isAlertDisplayed, title: "Some title", text: $text, placeholder: "Placeholder", action: { text in
print(text)
})
.padding()
}
}
I have a list item with some text stuff, navigationLink and button to show .sheet.When I click on either navigation link or show sheet button both navigation destination and the sheet appear.How to avoid this behaviour?
Note: This is minimal produceable code.
struct ContentView: View {
#State var shouldSheetShow:Bool = false;
var body: some View {
NavigationView{
List{
VStack(alignment: .leading){
Text("Some Text Stuff")
NavigationLink(
destination: Text("navigation link"),
label: {
Text("Navigate To some view")
.background(Color.green)
})
Button(action: {
self.shouldSheetShow = true
}, label: {
HStack{
Text("Show sheet")
}
.background(Color.blue)
.sheet(isPresented: $shouldSheetShow, content: {
Text("sheet")
})
})
}
.frame(width: 300, height: 150, alignment: .center)
.background(Color.gray)
}
}
}
}
A possible solution is to replace the Button with Text plus an onTapGesture
Replace
Button(action: {
self.shouldSheetShow = true
}, label: {
HStack{
Text("Show sheet")
}
.background(Color.blue)
.sheet(isPresented: $shouldSheetShow, content: {
Text("sheet")
})
})
with
Text("Show sheet")
.background(Color.blue)
.sheet(isPresented: $shouldSheetShow, content: {
Text("sheet")
})
.onTapGesture {
self.shouldSheetShow = true
}
My Custom button does not tap and passes to next view called AddCreditCardView.
I have tested the button action with print statement and it won't work too.
I copied my code below in separate.
This is my ContentView
import SwiftUI
struct ContentView: View {
let membershipRows = MembershipData.listData()
let corporateRows = CorporateData.listData()
let otherOperationRows = OtherOperationsData.listData()
#State var selectedCard = CreditCard(id: "", cardOwnerName: "", cardNumber: "", cardExpMonth: "", cardExpYear: "", ccv: "")
#State var shown: Bool = false
var body: some View {
NavigationView {
VStack {
List {
Section(header: Text("Bireysel")) {
ForEach(membershipRows) { row in
NavigationLink(destination: CreditCardView()) {
RowElementView(row: row)
}
}
}
if self.corporateRows.count == 0
{
Rectangle()
.background(Color(.white))
.edgesIgnoringSafeArea(.all)
.foregroundColor(.white)
.padding(.vertical,32)
}
else {
Section(header: Text("Kurumsal")) {
ForEach(corporateRows) { row in
RowElementView(row: row)
}
}
}
Section(header: Text("Diger Islemler")) {
ForEach(otherOperationRows) { row in
RowElementView(row: row)
}
}
Rectangle()
.foregroundColor(.clear)
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height )
}
.navigationBarTitle("Odeme Yontemleri", displayMode: .inline)
.font(Font.custom("SFCompactDisplay", size: 16))
Button(action: {
AddCreditCardView(item: self.selectedCard)
}, label: { CustomButton(title: "Odeme Yontemi Ekle", icon: .none, status: .enable)
})
}
}
}
This is my AddCreditCardView
import SwiftUI
struct AddCreditCardView: View {
var item: CreditCard
var body: some View {
NavigationView {
VStack {
TopBar()
Spacer()
CardInfo()
Spacer()
}
.navigationBarTitle("Odeme Yontemi", displayMode: .inline)
}
}
}
struct TopBar : View {
var body: some View {
VStack {
HStack() {
Image("addcreditcard")
Image("line")
Image("locationBar")
Image("line")
Image("check-circle")
}
.padding(.horizontal,62)
VStack {
Text("Kredi Karti Ekle")
.font(Font.custom("SFCompactDisplay-Bold", size: 14))
Text("1. Adim")
.font(Font.custom("SFCompactDisplay", size: 14))
.fontWeight(.regular)
.foregroundColor(.gray)
}
}
.padding()
}
}
struct CardInfo : View {
var body: some View {
VStack {
CustomTextField(tFtext: "Kartin Uzerindeki Isim", tFImage: "user")
.textContentType(.givenName)
CustomTextField(tFtext: "Kredi Kart Numarasi", tFImage: "credit")
.textContentType(.oneTimeCode)
.keyboardType(.numberPad)
HStack {
CreditCardDateTextField(tFtext: "", tFImage: "date")
.textContentType(.creditCardNumber)
Spacer()
Text("|")
.foregroundColor(.black)
.overlay(
Rectangle()
.frame(width: 60, height: 53))
CustomTextField(tFtext: "CCV", tFImage: "")
.textContentType(.creditCardNumber)
}
.foregroundColor(Color(#colorLiteral(red: 0.9647058824, green: 0.9725490196, blue: 0.9882352941, alpha: 1)))
CustomTextField(tFtext: "Kart Ismi", tFImage: "cardEdit")
Spacer()
}
}
}
And Finally, this is my CreditCard Model
import SwiftUI
struct CreditCard: Identifiable {
var id: String = UUID().uuidString
var cardOwnerName : String
var cardNumber: String
var cardExpMonth: String
var cardExpYear: String
var ccv: String
Seems like you are trying to navigate to AddCreditCardView on the button press. The action closure can not present a view automatically like that! You should change that code to something like this:
#State var navigated = false
,,,
NavigationLink("AddCreditCardView", destination: AddCreditCardView(), isActive: $navigated)
Button(action: { self.navigated.toggle() },
label: { CustomButton(title: "Odeme Yontemi Ekle", icon: .none, status: .enable) })
changing the navigated state will show the next page as it seems you wished.
I've added a .toolbar to the top level of a NavigationView that will eventually be used to select items in a list without using swipe gestures (up button, down button, etc.). I also have a .navigationBar going on, to access other views for Account and Settings.
For the most part it's looking really good, but when I follow a NavigationLink (in .navigationBarItems) within NavigationView, and then use the built-in back navigation, my .toolbar disappears from the top level.
Am I putting the .toolbar in the wrong place? It feels like a problem with .navigationViewStyle(StackNavigationViewStyle()) because when I comment that out, the toolbar will not disappear upon navigation... but I don't like how the default behavior works in landscape so I'm relying on it.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
List {
Group {
Section(header: Text("List Items").foregroundColor(.gray).font(.footnote)) {
Text("List Item One")
Text("List Item Two")
Text("List Item Three")
}
}
}.navigationTitle("Top Level List").navigationBarTitleDisplayMode(.inline)
.ignoresSafeArea(.all)
// MARK: NAVBAR
.navigationBarItems(
leading:
NavigationLink(destination: UserView()) {
Image(systemName: "person.crop.circle").font(.title2)
},
trailing:
NavigationLink(destination: SettingsView()) {
Image(systemName: "gear").font(.title2)
})
//MARK: - CONTENT NAV
.toolbar {
ToolbarItemGroup(placement: .bottomBar) {
Button(action: {}, label: {Label("Mute", systemImage: "speaker.slash.fill")})
Spacer()
Button(action: {}, label: {Label("Repeat", systemImage: "arrow.clockwise")})
Spacer()
Button(action: {}, label: {Label("Previous", systemImage: "arrow.up")})
Spacer()
Button(action: {}, label: {Label("Next", systemImage: "arrow.down")})
Spacer()
Button(action: {}, label: {Label("Select", systemImage: "arrow.right")})
}
}
}.navigationViewStyle(StackNavigationViewStyle())
}
}
struct UserView: View {
#State private var username: String = ""
#State private var password: String = ""
var body: some View {
Form {
TextField("Username", text: $username)
SecureField("Password", text: $password)
}
.navigationBarTitle("Account").font(.subheadline)
}
}
struct SettingsView: View {
#State private var setting1: String = ""
#State private var setting2: String = ""
var body: some View {
Form {
TextField("Setting One", text: $setting1)
SecureField("Setting Two", text: $setting2)
}
.navigationBarTitle("Settings").font(.subheadline)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
You right, it's in a wrong place. Here is how it should be if you need a toolbar always shown:
struct ContentView: View {
var body: some View {
NavigationView {
List {
Group {
Section(header: Text("List Items").foregroundColor(.gray).font(.footnote)) {
Text("List Item One")
Text("List Item Two")
Text("List Item Three")
}
}
}.navigationTitle("Top Level List").navigationBarTitleDisplayMode(.inline)
.ignoresSafeArea(.all)
// MARK: NAVBAR
.navigationBarItems(
leading:
NavigationLink(destination: UserView()) {
Image(systemName: "person.crop.circle").font(.title2)
},
trailing:
NavigationLink(destination: SettingsView()) {
Image(systemName: "gear").font(.title2)
})
//MARK: - CONTENT NAV
}.navigationViewStyle(StackNavigationViewStyle())
.toolbar {
ToolbarItemGroup(placement: .bottomBar) {
Button(action: {}, label: {Label("Mute", systemImage: "speaker.slash.fill")})
Spacer()
Button(action: {}, label: {Label("Repeat", systemImage: "arrow.clockwise")})
Spacer()
Button(action: {}, label: {Label("Previous", systemImage: "arrow.up")})
Spacer()
Button(action: {}, label: {Label("Next", systemImage: "arrow.down")})
Spacer()
Button(action: {}, label: {Label("Select", systemImage: "arrow.right")})
}
}
}
}