how to create picker menu like drop down in SwiftUI - swiftui

I am using a picker. Onclick textField, it shows the picker menu. But picker options show the top of the textField. Onclick textField toggle shows
Here is the image
Here is my code:
struct PickerView: View {
#State private var text:String = ""
#State private var options = ["Option 1", "Option 2", "Option 3"]
#State private var selectedOption = "Option 1"
#State private var showPicker: Bool = false
var body: some View {
VStack {
HStack {
TextField("", text: $selectedOption)
.disabled(true)
Image(systemName: "chevron.down")
.foregroundColor(.gray)
}
.padding()
.background(Color.gray)
.cornerRadius(5.0)
Picker("Options", selection: $selectedOption) {
ForEach(options, id: \.self) { option in
Text(option).tag(option)
}
}
.pickerStyle(.automatic)
.padding()
.background(Color.white)
.cornerRadius(5.0)
.shadow(radius: 5)
.offset(y: -100)
.opacity(showPicker ? 1 : 0)
.animation(.default)
}
.onTapGesture {
self.showPicker.toggle()
}
}
}
How to show the picker onClick textField directly like drop down?
Please help me..

This might be what you wanted
var body: some View {
VStack(alignment: .leading) {
HStack {
Picker("Options", selection: $selectedOption) {
ForEach(options, id: \.self) { option in
Text(option).tag(option)
}
}
.tint(.black)
.pickerStyle(.menu)
Spacer()
}
.background(.gray)
.cornerRadius(5.0)
}
}

Related

Dropdown menu button SwiftUI

I'm trying to implement such dropdown menu https://imgur.com/a/3KcKhv4 but could do it like that https://imgur.com/67bKU5Q
The problem is that selected option doesn't have to repeated. Could you please help me how can I do dropdown menu like in design?
class MenuViewModel: ObservableObject {
#Published var selectedOption: String = "За все время"
}
struct DropdDown: View {
let buttons = ["За все время", "За день", "За неделю"]
#ObservedObject var viewModel = MenuViewModel()
#State var expanded: Bool = false
var body: some View {
VStack(spacing: 30) {
Button {
self.expanded.toggle()
} label: {
Text(viewModel.selectedOption)
.fontWeight(.bold)
.foregroundColor(Color.black)
Spacer()
Image(systemName: "chevron.down")
.foregroundColor(Color.white)
}
if expanded {
ForEach(self.buttons, id: \.self) { buttonTitle in
VStack(alignment: .leading, spacing: 5) {
Button {
self.expanded.toggle()
viewModel.selectedOption = buttonTitle
} label: {
Text(buttonTitle)
.padding(10)
}
.foregroundColor(Color.black)
}
.frame(maxWidth: .infinity, alignment: .leading)
}
}
}
.padding()
.frame(width: 300)
.background(Color.gray)
.cornerRadius(10)
}
}
struct DropdDown_Previews: PreviewProvider {
static var previews: some View {
DropdDown()
}
}
Just create computed property array in DropdDown View for store buttons without selectedOption
var availableButtons: [String] {
return buttons.filter { $0 != viewModel.selectedOption }
}
And use in ForEach loop instead buttons array
ForEach(self.availableButtons, id: \.self) {}

Can't select Picker

I am trying to make a stock control system in SwiftUI where users can add an ingredient and then ad an amount and then select a unit of measurement(kg, g, l ,ml).
The app allows them to click a button which allows them to add an ingredient to a text-box which is created and then their input is added to a list.
I am having trouble allowing the user to also type in a number in a text-box next to the ingredient text-box and making the picker clickable
Here is my code
import SwiftUI
struct UploadView2: View {
#State var ingredients = [String]()
#State var amount = [String]()
#State var choices = ["g", "kg", "ml", "l"]
#State var choosen: String = ""
#EnvironmentObject var viewRouter: ViewRouter
func getBinding(forIndex index: Int) -> Binding<String> {
return Binding<String>(get: { ingredients[index] },
set: { ingredients[index] = $0 })
}
var body: some View {
VStack{
HStack{
Button {
print("Going Back")
viewRouter.currentPage = .UploadView
} label: {
Image(systemName: "arrow.left")
.font(.system(size: 30))
.foregroundColor(.black)
}
.padding(.horizontal)
Spacer()
Text("Add Ingredients")
.font(.system(size: 30))
.fontWeight(.bold)
Spacer()
Button {
print("Saved")
} label: {
Image(systemName: "bookmark")
.font(.system(size: 30))
.foregroundColor(.black)
}
.padding()
}
Form {
ForEach(0..<ingredients.count, id: \.self) { index in
HStack {
Button(action: { ingredients.remove(at: index) }) {
Image(systemName: "minus.circle.fill")
.foregroundColor(.red)
.padding(.horizontal)
}
TextField("Ingredient", text: getBinding(forIndex: index))
Picker("", selection: $choosen){
ForEach(choices, id: \.self) { i in
Text("\(i)").tag(i)
}
}
}
}
Button(action: { ingredients.append("") }) {
HStack {
Image(systemName: "plus")
.foregroundColor(.black)
.padding(.horizontal)
Text("add an ingredient")
.foregroundColor(.black)
}
}
}
Button {
//change view router
//add data to data class
viewRouter.currentPage = .UploadView3
} label: {
Label("next", systemImage: "arrow.right")
}
.padding()
.frame(width: 100)
.foregroundColor(Color.white)
.background(Color.red)
.cornerRadius(8)
}
}
}
struct UploadView2_Previews: PreviewProvider {
static var previews: some View {
UploadView2()
.previewDevice(PreviewDevice(rawValue: "iPhone 13"))
.previewInterfaceOrientation(.portrait)
UploadView2()
.previewDevice(PreviewDevice(rawValue: "iPhone 8"))
}
}
When i click on my click it removes the text-box like the dismiss button
My overall goal is to have a text-box to enter an ingredient and then a text-box to enter an amount and then a picker to select a unit of measurement.
How can I achieve this using my current code as much as I can?
to be able to "select" your Picker, you could try this approach, as shown in this example code:
struct ContentView: View {
#State var ingredient = ""
#State var amount = ""
#State var choosen = ""
#State var choices = ["g", "kg", "ml", "l"]
var body: some View {
HStack {
TextField("Ingredient", text: $ingredient) // <-- or your getBinding thing
TextField("Amount", text: $amount)
Picker("", selection: $choosen){
ForEach(choices, id: \.self) { i in
Text("\(i)").tag(i)
}
}
.pickerStyle(.inline)
.frame(width: 55)
.clipped() // <-- here
}
}
}

How to manually present DatePicker in SwiftUI

I want to show a DatePicker in SwiftUI. But the SwiftUI have no interface to customization the style of DatePicker, etc, font size ,font color . So I want to use a button to show DatePicker. After click this button , the SwiftUI DatePicker will be present.
Is there any way to implement this?
struct ContentView: View {
#State private var date = Date()
var body: some View {
VStack {
ZStack() {
DatePicker(selection: $date, in: ...Date(), displayedComponents: .date) {
Text("")
}
.opacity(0.01)
HStack() {
Text("Start Time")
Spacer()
Button("2021-08-22") {
print("Button was tapped")
// present DatePicker...
}
}
}
.frame(height: 44)
.padding(.horizontal, 20)
}
}
}
I find a solution as bellow:
struct ContentView: View {
#State private var date = Date()
var body: some View {
VStack {
ZStack() {
HStack() {
Text("Start Time")
Spacer()
Text("2021-08-22")
}
DatePicker(selection: $date, in: ...Date(), displayedComponents: .date) {
Text("")
}
.opacity(0.011)
}
.frame(height: 44)
.padding(.horizontal, 20)
}
}
}

SwiftUI: How to set the title for the toolbar on macOS 11?

I have a SwiftUI app that has two columns and a toolbar. I'm trying to emulate the latest macOS applications and use the toolbar in the same way that Mail does, i.e. select a sidebar item and show that as the title of the window.
Here is my code:
import SwiftUI
var listItems = ["Item 1", "Item 2", "Item 3", "Item 4"]
var secondItems = ["Second 1", "Second 2", "Second 3", "Second 4"]
struct ContentView: View
{
#State var select: String? = "Item 1"
var body: some View
{
VStack
{
NavigationView
{
List
{
ForEach((0..<listItems.count), id: \.self)
{index in
NavigationLink(destination: SecondView(), tag: listItems[index], selection: $select)
{
Text(listItems[index])
.padding(.vertical, 2.0)
}
}
Spacer()
}.frame(width:160)
}
.toolbar
{
Text("this is not the title")
Button(action: {})
{
Label("Upload", systemImage: "square.and.arrow.up")
}
}
}
}
}
struct SecondView: View
{
var body: some View
{
NavigationView
{
List
{
ForEach((0..<secondItems.count), id: \.self)
{index in
NavigationLink(destination: Text(secondItems[index]))
{
Text(secondItems[index])
.frame(height: 20)
}
}
}.frame(width:150)
}
}
}
So I get a toolbar like this:
but how do I set the title of the window in the toolbar when I select an item in the first list?
Here is how I solved it:
import SwiftUI
var listItems = ["Item 1", "Item 2", "Item 3", "Item 4"]
var secondItems = ["Second 1", "Second 2", "Second 3", "Second 4"]
struct ContentView: View
{
#State var select: String? = "Item 1"
var body: some View
{
VStack
{
NavigationView
{
List
{
ForEach((0..<listItems.count), id: \.self)
{index in
NavigationLink(destination: SecondView(), tag: listItems[index], selection: $select)
{
Text(listItems[index])
.frame(height: 20)
}
}
Spacer()
}
.listStyle(SidebarListStyle())
}
.toolbar
{
Text("this is not the title")
Button(action: {})
{
Label("Upload", systemImage: "square.and.arrow.up")
}
}
.navigationTitle(select!)
.navigationSubtitle("\(listItems.count) records")
.navigationViewStyle(DoubleColumnNavigationViewStyle())
}
}
}
struct SecondView: View {
var body: some View {
NavigationView {
List
{
ForEach((0..<secondItems.count), id: \.self)
{index in
NavigationLink(destination: Text(secondItems[index]))
{
Text(secondItems[index])
.frame(height: 20)
}
}
}
}
}
}
HStack(spacing: 0) {
ToolView()
Divider()
PanelView()
}
.frame(width: 290)
.toolbar(id: "id") {
ToolbarItem(id: "sidbar", placement: .primaryAction) {
Button {
} label: {
Label {
Text("Stack")
} icon: {
Image("stack")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 20, height: 20)
.offset(y: 10)
}
}
}
}
Use id to create ToolbarItem and you can show the title with right click on the toolbar.
Use Button to show the content, and the label and image will fit the toolbar.
NOTE: if you have multiple toolbars, make sure to provide each one with an id, or you still won't be able to show the title.
It is best to use SF Symbols, but if you use a custom font, make sure to resize it, and offset it.
If you are attempting to change the text that is currently set to "toolbar", you want to set the navigation title. This is done via .navigationTitle(String) and .navigationSubtitle(String).

How do I properly use NavigationView in a ZStack?

I am trying to add some filter options to sit at the top of my view, above the NavigationView. I wrote the following code that mostly does what I want, however it disabled the ability to click on the rows to get to the detailed view. I assume this is because my filter buttons are on top of the ZStack, but I'm not sure how else to get this to work.
Here is the code I wrote:
import SwiftUI
struct BonusList: View {
var bonuses = sampleBonusData
#State var showSettings = false
#State var showBonuses = false
#State var bonusEarned = true
#State var showStatePicker = false
#State var showCategoryPicker = false
var body: some View {
ZStack {
NavigationView {
List(bonuses) { item in
NavigationLink(destination: BonusDetail(bonusName: item.bonusName, bonusCode: item.bonusCode, city: item.city, sampleImage: item.sampleImage)) {
HStack(spacing: 12.0) {
Image(item.sampleImage)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 60, height: 60)
.background(Color.white)
.cornerRadius(15)
VStack(alignment: .leading) {
HStack {
Text(item.bonusName)
.font(.headline)
Spacer()
Image(systemName: "checkmark.shield")
.opacity(self.bonusEarned ? 100 : 0)
}
Text("\(item.city), \(item.state)")
.font(.subheadline)
.frame(height: 25.0)
HStack {
Text(item.bonusCategory)
.font(.caption)
.fontWeight(.bold)
.foregroundColor(.gray)
.padding(.top, 4)
Spacer()
Text(item.bonusCode)
.font(.caption)
.fontWeight(.bold)
.foregroundColor(.gray)
.padding(.top, 4)
}
}
}
}
}
.navigationBarTitle(Text("Bonuses"))
// .navigationBarHidden(true)
}
.saturation(self.bonusEarned ? 0 : 1)
HStack {
FilterByCategory(showCategoryPicker: $showCategoryPicker)
Spacer()
FilterByState(showStatePicker: $showStatePicker)
}
StatePicker(showStatePicker: $showStatePicker)
CategoryPicker(showCategoryPicker: $showCategoryPicker)
}
}
}
This is what it looks like when I run it:
If I'm understanding correctly, you have a view or two which sit higher in the ZStack that are off canvas and come in when those buttons are tapped?
You could consider using a modal and setting the view you want to show for each button as the view for the modal. This will keep your views off screen and still allow interaction with your list. Here's what I've done...
On the main view
import SwiftUI
struct MainView: View {
#State private var isPresented = false
var body: some View {
NavigationView {
VStack {
//...
}
//Modal
.sheet(isPresented: $isPresented, content: {
AddItem(showModal: self.$isPresented)
})
}
}
}
The modal's view
import SwiftUI
struct AddItem: View {
#Binding var showModal: Bool
var body: some View {
VStack {
Button(action: {
self.showModal = false
}, label: {
Text("Cancel")
})
}
}
}