Display Multiple views in Picker - swift

I am trying to display words in a Picker next to the selected option but the words are never displayed correctly. I want the user to be able to click the text to be able to open the Picker as well. I have tried putting the text outside the picker which leads to displaying correctly but the text is not clickable.
I am using Xcode 13 Beta 5 and iOS15.
struct ContentView: View {
#State var fruit = "Apple"
let fruits = ["Apple","Orange","Pear","Grape"]
var body: some View {
HStack {
Picker(selection: $fruit) {
Text("Picked:")
ForEach(fruits, id: \.self){ fruit in
Text(fruit)
}
} label: {
Text("")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Picture for Picker with text outside of Picker
Tweaks made to fit what I asked are shown below. Any thing put inside label is now clickable.
Menu {
Picker(selection: $fruit, label: EmptyView()) {
ForEach(fruits, id: \.self) { fruit in
Text(fruit)
}
}
} label: {
HStack {
Image(systemName: "hand.thumbsup.fill")
Text("Picked: \(fruit)")
}
}

Use a Menu with a inline Picker as its content:
Menu {
Picker(selection: $fruit, label: EmptyView()) {
ForEach(fruits, id: \.self) { fruit in
Text(fruit)
}
}
.labelsHidden()
.pickerStyle(InlinePickerStyle())
} label: {
HStack {
Text("Picked:")
Text(fruit)
}
}

Related

sth wrong with .searchable

I have added searchable to my SwiftUI List.
But the search TextField isn't showing.
Here is my code:
NavigationView {
List(searchResults, id: \.self) { item in
NavigationLink {
LegendDetailView(item: item)
} label: {
HStack {
Text(item.name).padding(1)
Spacer()
Image(systemName: "chevron.right").imageScale(.small)
}
}
}
}.searchable(text: $searchText)
EDIT(2021/5/29):
I thinks there is a piece of important infomation I forgot to say
This view is a popover
I add searchable to my SwiftUI List
No, you added it to the navigation view. Move it up
NavigationView {
List(searchResults, id: \.self) { item in
NavigationLink {
LegendDetailView(item: item)
} label: {
HStack {
Text(item.name).padding(1)
Spacer()
Image(systemName: "chevron.right").imageScale(.small)
}
}
}.searchable(text: $searchText)
}
Text example
struct TestView: View {
#State private var letters = ["alpha", "beta", "gamma", "delta", "epsilon", "zeta"]
#State private var searchText = ""
var body: some View {
let searchResults = searchText.isEmpty ? letters : letters.filter{$0.localizedCaseInsensitiveContains(searchText)}
NavigationView {
List(searchResults, id: \.self) { item in
NavigationLink {
Text(item)
} label: {
HStack {
Text(item).padding(1)
Spacer()
}
}
}.searchable(text: $searchText)
}
}
}

I want to use multiple popovers with buttons in the Swift UI

When using Popover with a button in SwiftUI, I want to popover with multiple buttons as shown below, but as it is, only the upper button
I can't get a popover. What if you want to popover both separately?
struct MySelection: Identifiable {
let id = UUID()
var text = ""
}
struct PopoverTest: View {
#State var selected: MySelection?
var body: some View {
VStack (spacing: 88) {
// First Button
Button(action: {
selected = MySelection(text: "Popover1")
}, label: {
Image(systemName: "stopwatch")
})
// Second Button
Button(action: {
selected = MySelection(text: "Popover2")
}, label: {
Image(systemName: "globe")
})
}
.buttonStyle(.plain)
.popover(item: $selected) { selection in
Text(selection.text).font(.largeTitle)
}
}
}
Thinking it over, the better way might be to create a custom "button with popover" view that can be used anywhere:
struct ContentView: View {
var body: some View {
VStack (spacing: 88) {
// First Button
ButtonWithPopover(image: "stopwatch",
item: MySelection(text: "Popover1"))
// Second Button
ButtonWithPopover(image: "globe",
item: MySelection(text: "Popover2"))
}
.buttonStyle(.plain)
}
}
struct ButtonWithPopover: View {
let image: String
let item: MySelection
#State var selected: MySelection?
var body: some View {
Button(action: {
selected = item
}, label: {
Image(systemName: image)
})
.popover(item: $selected) { selection in
Text(selection.text).font(.largeTitle)
}
}
}
In this case it seems you have to introduce 2 selected vars. The optional selected can hand over different values to the popover, but if it is triggered from different subviews, SwiftUI doesn't know where to anchor the popover to.
struct ContentView: View {
#State var selected1: MySelection?
#State var selected2: MySelection?
var body: some View {
VStack (spacing: 88) {
// First Button
Button(action: {
selected1 = MySelection(text: "Popover1")
}, label: {
Image(systemName: "stopwatch")
})
.popover(item: $selected1) { selection in
Text(selection.text).font(.largeTitle)
}
// Second Button
Button(action: {
selected2 = MySelection(text: "Popover2")
}, label: {
Image(systemName: "globe")
})
.popover(item: $selected2) { selection in
Text(selection.text).font(.largeTitle)
}
}
.buttonStyle(.plain)
}
}

TextField in a list not working well in SwiftUI

This problem is with SwiftUI for a iPhone 12 app, Using xcode 13.1.
I build a List with TextField in each row, but every time i try to edit the contents, it is only allow me tap one time and enter only one character then can not keep enter characters anymore, unless i tap again then enter another one character.Did i write something code wrong with it?
class PieChartViewModel: ObservableObject, Identifiable {
#Published var options = ["How are you", "你好", "Let's go to zoo", "OKKKKK", "什麼情況??", "yesssss", "二百五", "明天見"]
}
struct OptionsView: View {
#ObservedObject var viewModel: PieChartViewModel
var body: some View {
NavigationView {
List {
ForEach($viewModel.options, id: \.self) { $option in
TextField(option, text: $option)
}
}
.navigationTitle("Options")
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button {
addNewOption()
} label: {
HStack {
Image(systemName: "plus")
Text("Create a new option")
}
}
}
}
}
}
func addNewOption() {
viewModel.options.insert("", at: viewModel.options.count)
}
}
struct OptionsView_Previews: PreviewProvider {
static var previews: some View {
let pieChart = PieChartViewModel()
OptionsView(viewModel: pieChart)
}
}
Welcome to StackOverflow! Your issue is that you are directly updating an ObservableObject in the TextField. Every change you make to the model, causes a redraw of your view, which, of course, kicks your focus from the TextField. The easiest answer is to implement your own Binding on the TextField. That will cause the model to update, without constantly redrawing your view:
struct OptionsView: View {
// You should be using #StateObject instead of #ObservedObject, but either should work.
#StateObject var model = PieChartViewModel()
#State var newText = ""
var body: some View {
NavigationView {
VStack {
List {
ForEach(model.options, id: \.self) { option in
Text(option)
}
}
List {
//Using Array(zip()) allows you to sort by the element, but use the index.
//This matters if you are rearranging or deleting the elements in a list.
ForEach(Array(zip(model.options, model.options.indices)), id: \.0) { option, index in
// Binding implemented here.
TextField(option, text: Binding<String>(
get: {
model.options[index]
},
set: { newValue in
//You can't update the model here because you will get the same behavior
//that you were getting in the first place.
newText = newValue
}))
.onSubmit {
//The model is updated here.
model.options[index] = newText
newText = ""
}
}
}
.navigationTitle("Options")
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button {
addNewOption()
} label: {
HStack {
Image(systemName: "plus")
Text("Create a new option")
}
}
}
}
}
}
}
func addNewOption() {
model.options.insert("", at: model.options.count)
}
}

SwiftUI - Form Picker - How to prevent navigating back on selected?

I'm implementing Form and Picker with SwiftUI. There is a problem that it automatically navigates back to Form screen when I select a Picker option, how to keep it stay in selection screen?
Code:
struct ContentView: View {
#State private var selectedStrength = "Mild"
let strengths = ["Mild", "Medium", "Mature"]
var body: some View {
NavigationView {
Form {
Section {
Picker("Strength", selection: $selectedStrength) {
ForEach(strengths, id: \.self) {
Text($0)
}
}
}
}
.navigationTitle("Select your cheese")
}
}
}
Actual:
Expect: (sample from Iphone Settings)
You may have to make a custom view that mimics what the picker looks like:
struct ContentView: View {
let strengths = ["Mild", "Medium", "Mature"]
#State private var selectedStrength = "Mild"
var body: some View {
NavigationView {
Form {
Section {
NavigationLink(destination: CheesePickerView(strengths: strengths, selectedStrength: $selectedStrength)) {
HStack {
Text("Strength")
Spacer()
Text(selectedStrength)
.foregroundColor(.gray)
}
}
}
}
.navigationTitle("Select your cheese")
}
}
}
struct CheesePickerView: View {
let strengths: [String]
#Binding var selectedStrength: String
var body: some View {
Form {
Section {
ForEach(0..<strengths.count){ index in
HStack {
Button(action: {
selectedStrength = strengths[index]
}) {
HStack{
Text(strengths[index])
.foregroundColor(.black)
Spacer()
if selectedStrength == strengths[index] {
Image(systemName: "checkmark")
.foregroundColor(.blue)
}
}
}.buttonStyle(BorderlessButtonStyle())
}
}
}
}
}
}

SwiftUI - ForEach Simultaneously Toggle Multiple Buttons

I have a ForEach loop that displays ten unfilled buttons. When the button is clicked, the button assigns the index of the clicked button to a #State variable; only when the value of the state variable is equal to the index of the clicked button is the button filled. What should I change to be able to 1) click on the same button to unfill the button, and 2) fill another button without "unfilling" any already filled buttons?
Here is a minimal, reproducible example:
import SwiftUI
struct Test: View {
#State var selection: Int? = nil
var body: some View {
VStack {
ForEach (0 ..< 10, id: \.self) { number in
Button (action: {
self.selection = number
}, label: {
self.selection == number ? Image(systemName: "heart.fill") : Image(systemName: "heart")
})
}
}
}
}
struct Test_Previews: PreviewProvider {
static var previews: some View {
Test()
}
}
Thanks in advance.
You need to have a selection property that can keep track of multiple buttons, not just one button. I would use a Set here (not an Array, because there's no handy remove-object method there)
struct Test: View {
#State var selection = Set<Int>() /// will contain the indices of all selected buttons
var body: some View {
VStack {
ForEach (0 ..< 10, id: \.self) { number in
Button(action: {
if selection.contains(number) {
selection.remove(number)
} else {
selection.insert(number)
}
}) {
selection.contains(number) ? Image(systemName: "heart.fill") : Image(systemName: "heart")
}
}
}
}
}
You have to maintain state separately for each button.Better option is to use array. Check code below.
import SwiftUI
struct Test: View {
#State var selection: [Int] = Array(repeating:-1,count:10)
var body: some View {
VStack {
ForEach (0..<selection.count, id: \.self) { index in
Button (action: {
if self.selection[index] == index {
self.selection[index] = -1
}else{
self.selection[index] = index
}
}, label: {
self.selection[index] == index ? Image(systemName: "heart.fill") : Image(systemName: "heart")
})
}
}
}
}
struct Test_Previews: PreviewProvider {
static var previews: some View {
Test()
}
}