I want to add a button to swiftui picker's label.
But the button is not clickable.
When I click on the button the picker is clicked.
How Do I make the picker take clicks only in the area of the selected value?
and the buttons take his clicks?
import SwiftUI
enum Animal: String, CaseIterable, Identifiable {
case dog
case cat
case bird
var id: String { self.rawValue }
}
struct ContentView: View {
#State private var selectedAnimal = Animal.dog
var body: some View {
Form {
Group {
Section(header: Text("Animales")) {
VStack{
Picker(
selection: $selectedAnimal,
content: {
ForEach(Animal.allCases, id:\.self) {
Text($0.rawValue)
}},
label: {
HStack {
Text ("Chose Animale")
Spacer ()
Button (
action: {
print ("clicked")
},
label: {
Image(systemName: "arrow.clockwise")
})
Spacer ()
}
}
)
}
}
}
}
}
}
To solve this issue we need to separate picker and button and block Form tracking click inside row (which is by default track entire row).
For first move button out of picker and place everything in HStack, for second we need couple of tricks like tapGesture on label and non-default button style for button (for simplicity I used primitive button style, but it's better to create custom with appropriate highlight, etc.)
Here is a simplified updated and tested your code (Xcode 13 / iOS 15):
var body: some View {
Form {
Group {
Section(header: Text("Animales")) {
HStack{
HStack {
Text ("Chose Animale")
Spacer ()
}
.contentShape(Rectangle())
.onTapGesture {
// just blocker for label click
}
.overlay(
Button (
action: {
print ("clicked")
},
label: {
Image(systemName: "arrow.clockwise").foregroundColor(.blue)
})
.buttonStyle(PlainButtonStyle()) // << needed custom !!
)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.layoutPriority(1) // << to cover much area
//.border(Color.red) // << for testing area
Picker("",
selection: $selectedAnimal,
content: {
ForEach(Animal.allCases, id:\.self) {
Text($0.rawValue)
}}
)
.labelsHidden() // << hide own label
.fixedSize() // << limit size !!
}
.listRowInsets(EdgeInsets()) // << consume row space !!
}
}
}
}
Related
Currently, I have to implement bottom sheet. And I found the very example of my need.
Is this component system component of swift or swiftui?
OR do I need to implement on my own?
PLEASE LET ME KNOW IF U HAVE SOME INFOS! XD
At first I implement with ZStack, drag gesture but the animation is not what I expected.
I need Information about whether there is component like .sheet(isPresented: Bool, content: View) of the modal like above image.
As our friend said before, it is a sheet. Inside the sheet you can either define a new view or call any of your views. Then you have to use the modifier .presentationDetents which receive a Set of PresentationDetents to say where the view has to stop when appearing on the screen. This modifier must be apply to the content of the sheet and not directly to the sheet.
struct ContentView: View {
#State var isSheetShown = false
var body: some View {
VStack {
Button("Show view"){
isSheetShown = true
}
}.sheet(isPresented: $isSheetShown, content: {
StackOfButtons()
.presentationDetents([.medium])
})
.padding()
}
}
Finally, to create that stack type of buttons you can put them all in a HStack, give them individually some padding, set a little of spacing in the HStack and the round the corners of the stack. Something like this:
struct StackOfButtons: View {
var body: some View {
HStack(spacing: 2){
Button {
print("Hola que ase")
} label: {
Image(systemName: "list.bullet")
.padding()
.background(.thinMaterial)
.foregroundColor(.black)
}
Button {
print("Hola que ase")
} label: {
Image(systemName: "list.dash")
.padding()
.background(.thinMaterial)
.foregroundColor(.black)
}
Button {
print("Hola que ase")
} label: {
Image(systemName: "list.number")
.padding()
.background(.thinMaterial)
.foregroundColor(.black)
}
}.cornerRadius(10)
}
}
Result
I have a SwiftUI Picker in which an item is selected. The text of one element can be large, so I used UIKit UIPickerView and set the manual height to 100, but at some point it became not enough. Is it possible to make scrolling horizontal for each element?
I want to get something like this:
Picker("Items", select: self._selectItem) {
ForEach(self.items, id: \.self) { item in
ScrollView(.horizontal, showsIndicators: false) {
Text(item.description)
}
.tag(item)
}
}
That should work fine. If you only want to scroll one item, you would have to insert a check of the item length.
let items = [
"A long item text.",
"And a even longer item text which is really going further.",
"Another item text which is really going further."
]
struct ContentView: View {
#State private var select = ""
var body: some View {
VStack {
Text("Make your selection!")
List(items, id: \.self) { item in
ScrollView(.horizontal) {
Text(item)
}
.listRowBackground(item == select ? Color.red : Color.white)
.onTapGesture {
select = item
}
}
}
}
}
I would strongly suggest to separate the picking from the text display and scrolling, e.g. like this:
struct ContentView: View {
#State private var select = items[0]
var body: some View {
VStack {
Text("Make your selection!")
Picker("Items", selection: $select) {
ForEach(items) { item in
Text(item.title)
.tag(item)
}
}
ScrollView {
Text(select.text)
}
.padding()
.frame(height: 200)
}
}
}
I am working on a SwiftUI app. In the app I have a custom coded List that acts as a Form. The reason it is custom coded is because I am using a custom color. I have TextField rows among other rows that act as Navigation Links. My issue is that when I add a onTapGesture to dismiss the keyboard all other row functions stop working. For example the NavigationLinks.
NavigationLink Work Here
ZStack(alignment: .leading, content: {
Color.pacificBlue
.edgesIgnoringSafeArea(.all)
List {
Section(header: Text("Header") {
NavigationLink(
destination: SecondaryView(),
label: {
Text("Secondary View")
})
TextField("MyField", text: self.$myField)
}
}
}
NavigationLink Does Not Work Here
ZStack(alignment: .leading, content: {
Color.pacificBlue
.edgesIgnoringSafeArea(.all)
List {
Section(header: Text("Header") {
NavigationLink(
destination: SecondaryView(),
label: {
Text("Secondary View")
})
TextField("MyField", text: self.$myField)
}
}
}
.onTapGesture {
self.dismissKeyboard()
}
Dismiss Keyboard
extension View {
func dismissKeyboard() {
let resign = #selector(UIResponder.resignFirstResponder)
UIApplication.shared.sendAction(resign, to: nil, from: nil, for: nil)
}
}
Is there a way to work around this without having to move to a secondary view to add text. Any help would be appreciated.
Taking the answer from: SwiftUI NavigationLink in list
the following works for me for NavigationLink. If you have
a Button for example, this will not work.
struct ContentView: View {
#State var myField = ""
#State private var showIt: Int? = 0 // <-- here
var body: some View {
NavigationView {
ZStack(alignment: .leading) {
Color.blue.edgesIgnoringSafeArea(.all)
List {
Section(header: Text("Header")) {
// -- here --
NavigationLink(destination: Text("destination view"), tag: 1, selection: $showIt) {
Text("Secondary View")
}.disabled(true) // <-- here
.onTapGesture { showIt = 1 } // <-- here
TextField("MyField", text: $myField)
}
}.listStyle(.plain)
}
.onTapGesture {
self.dismissKeyboard()
}
}.navigationViewStyle(.stack)
}
}
I have a form with three different picker views. When I run the app and click on one of the pickers, the drop-down content is populated from another picker and it spontaneously cycles through the other two picker contents before returning to the main view. I am gogin to kick myself when someone points to something very fundamental and basic.... but here is the code . And thanks in advance!
var body: some View {
Form{
VStack {
HStack {
Text("PaO2")
TextField("mmHg", text: $PaO2)
.keyboardType(.numberPad)
Spacer()
Text("O2(%)")
TextField("%", text: $FiO2)
.keyboardType(.numberPad)
}
Toggle("Mechnical Ventilation", isOn: $MV)
HStack {
Text("Platelets")
TextField("(x1000)", text: $Platelets)
.keyboardType(.numberPad)
}
Picker(selection: $GCSSelected, label: Text("Glasgow Coma Scale")) {
ForEach(0..<GCS.count){ index1 in
Text(self.GCS[index1]).tag(index1)
}
}
Spacer()
Picker(selection: $HDSelected, label: Text("MAP/use of vasoactive Rx")){
ForEach(0..<HD.count){ index2 in
Text(self.HD[index2]).tag(index2)
}
}
HStack{
Text("Bilirubin")
TextField("mg/dL", text: $Bili)
.keyboardType(.numbersAndPunctuation)
}
Picker(selection: $RenalSelected, label: Text("Creatinine or Urine output")){
ForEach(0..<Renal.count){ index3 in
Text(self.Renal[index3]).tag(index3)
}
}
}
}
}
}
The issue is due to used single view for entire Form content, but you should not, so
var body: some View {
Form{
VStack { // << remove this container and let every picker be in own row
I have a list with some items.
Below the list I'd like to have to button to load more items.
(As loading all items requires some user actions like entering a TAN, this should not be done automatically when the user scrolls to the end of the list, but only if he likes to.)
What I'd like to have is a view like this:
However, if I place the List and the Button in a VStack, the Button get always displayed at the bottom of the screen, not only when I scroll to the end of the List:
struct ContentView: View {
private let items = Range(0...15).map { "Item " + String($0) }
var body: some View {
VStack {
List(items, id: \.self) { item in
Text(item)
}
HStack {
Spacer()
Button("Load more") { print("Load more items") }
Spacer()
}
}
}
}
If I add the Button to the List, the Button obviously gets displayed as a List item with a white background and without any space to the list:
struct ContentView: View {
private let items = Range(0...15).map { "Item " + String($0) }
var body: some View {
List {
ForEach(items, id: \.self) { item in
Text(item)
}
HStack {
Spacer()
Button("Load more") { print("Load more items") }
Spacer()
}
}.listStyle(GroupedListStyle())
}
}
Is there any way to add a view that becomes visible when the user scrolls to the end of the List but that is not part of the List? (Or at least looks like being below the List and not part of it?)
You should use second variant, but a bit tuned, like below (colors/spaces modify per your needs
var body: some View {
List {
ForEach(items, id: \.self) { item in
Text(item)
}
HStack {
Button("Load more") { print("Load more items") }
}
.listRowInsets(EdgeInsets())
.frame(maxWidth: .infinity, minHeight: 60)
.background(Color(UIColor.systemGroupedBackground))
}.listStyle(GroupedListStyle())
}