Consider this code:
struct ContentView: View {
var colors = ["Red", "Green", "Blue", "Tartan"]
#State private var selectedColor = "Red"
var body: some View {
Form {
Section (header:Text("color")) {
Picker("Please choose a color", selection: $selectedColor) {
ForEach(colors, id: \.self) {
Text($0)
}
}
}
}
}
}
Run it. The result is the picker disabled. Why? How to I enable it?
The problem is the picker being inside a form. Section makes no difference.
You have to add Form in Navigation View
struct ContentView: View {
var colors = ["Red", "Green", "Blue", "Tartan"]
#State private var selectedColor = "Red"
var body: some View {
NavigationView {
Form {
Section (header:Text("color")) {
Picker("Please choose a color", selection: $selectedColor) {
ForEach(colors, id: \.self) {
Text($0)
}
}
}
}
.navigationBarTitle("Color Picker")
}
}
}
Related
I'm using .searchable to embed a search bar into a list view. However, when the .searchable is nested in a NavigationStack (iOS 16 API), it is twinking when the page is loaded (shows up at first and disappears quickly). I hope both pages have a searchable feature.
I can reproduce this issue both on my device iPhone 12 and the simulator iPhone 14. Am I putting the modifier in an incorrect place?
struct ContentView: View {
#State private var selection = "2"
#State var items: [String] = ["0", "1", "2", "3", "4"]
#State var searchText = ""
var body: some View {
NavigationStack {
List {
ForEach(items, id: \.self) { item in
NavigationLink {
NestedListView(items: items)
} label: {
Text(item)
}
}
}
.searchable(text: $searchText)
}
}
}
struct NestedListView: View {
var items: [String]
#State var searchText = ""
var body: some View {
List {
ForEach(items, id: \.self) { item in
Text(item)
}
}
.searchable(text: $searchText)
}
}
How do I do text wrapping?
Here is my code:
struct ContentView: View {
private let items: [String] = [
"OneLineLongggggggggggggggggggggggggggggggggggggggggg",
"TwoLinesLonggggggggggggg\nLongggggggggggggggg",
"ThreeLinesLonggggggggggggg\nLongggggggggggggggg\nLongggggggggggggggg"
]
#State private var text: String = ""
var body: some View {
VStack {
Picker("Select Text", selection: self.$text) {
ForEach(self.items, id: \.self) {
Text($0)
.tag($0)
}
}
Text("select: \(self.text)")
}
}
}
Here is the result:
If I add fixSize then the elements run over each other:
struct ContentView: View {
private let items: [String] = [
"OneLineLongggggggggggggggggggggggggggggggggggggggggg",
"TwoLinesLonggggggggggggg\nLongggggggggggggggg",
"ThreeLinesLonggggggggggggg\nLongggggggggggggggg\nLongggggggggggggggg"
]
#State private var text: String = ""
var body: some View {
VStack {
Picker("Select Text", selection: self.$text) {
ForEach(self.items, id: \.self) {
Text($0)
.tag($0)
.fixedSize(horizontal: false, vertical: true)
}
}
Text("select: \(self.text)")
}
}
}
Here is the result:
Please tell me in which direction to look for the answer?
Please use .lineLimit(any number of max lines you want) with text.
In this case you can use
Text($0)
.tag($0)
.lineLimit(3)
Currently I've a picker included in a Section included in a Form what I'm trying to reach is to align the selected value of the picker to the leading in both iOS 13 and 14, I've tried many solutions such as labelsHidden() but with no result, kindly find the code sample that generates the following screenshot on iOS 14, any help would be appreciated
struct ContentView: View {
#State private var selectedStrength = "Mild"
let strengths = ["Mild", "Medium", "Mature"]
var body: some View {
NavigationView {
Form {
Section {
Picker("", selection: $selectedStrength) {
ForEach(strengths, id: \.self) {
Text($0)
}
}
}
}
}
}
}
Use the Text() with a Spacer() in a HStack()
struct ContentView: View {
#State private var selectedStrength = "Mild"
let strengths = ["Mild", "Medium", "Mature"]
var body: some View {
NavigationView {
Form {
Section {
Picker("", selection: $selectedStrength) {
ForEach(strengths, id: \.self) { t in
HStack {
Text(t)
Spacer()
}
}
}
}
}
}
}
}
You have to use .frame() and .labelsHidden()
struct ContentView: View {
#State private var selectedStrength = "Mild"
let strengths = ["Mild", "Medium", "Mature"]
var body: some View {
NavigationView {
Form {
Section {
Picker("", selection: $selectedStrength) {
ForEach(strengths, id: \.self) {
Text($0)
}
}
.frame(width: 160, alignment: .leading)
.labelsHidden()
}
}
}
}
}
tested on IOS 16
I'm having a problem where I have a ForEach loop inside a NavigationView. When I click the Edit button, and then click the pencil image at the right hand side on each row, I want it to display the text variable we are using from the ForEach loop. But when I click the pencil image for the text other than test123, it still displays the text test123 and I have absolutely no idea why.
Here's a video. Why is this happening?
import SwiftUI
struct TestPopOver: View {
private var stringObjects = ["test123", "helloworld", "reddit"]
#State private var editMode: EditMode = .inactive
#State private var showThemeEditor = false
#ViewBuilder
var body: some View {
NavigationView {
List {
ForEach(self.stringObjects, id: \.self) { text in
NavigationLink( destination: HStack{Text("Test!")}) {
HStack {
Text(text)
Spacer()
if self.editMode.isEditing {
Image(systemName: "pencil.circle").imageScale(.large)
.onTapGesture {
if self.editMode.isEditing {
self.showThemeEditor = true
}
}
}
}
}
.popover(isPresented: $showThemeEditor) {
CustomPopOver(isShowing: $showThemeEditor, text: text)
}
}
}
.navigationBarTitle("Reproduce Editing Bug!")
.navigationBarItems(leading: EditButton())
.environment(\.editMode, $editMode)
}
}
}
struct CustomPopOver: View {
#Binding var isShowing: Bool
var text: String
var body: some View {
VStack(spacing: 0) {
HStack() {
Spacer()
Button("Cancel") {
self.isShowing = false
}.padding()
}
Divider()
List {
Section {
Text(text)
}
}.listStyle(GroupedListStyle())
}
}
}
This is a very common issue (especially since iOS 14) that gets run into a lot with sheet but affects popover as well.
You can avoid it by using popover(item:) rather than isPresented. In this scenario, it'll actually use the latest values, not just the one that was present when then view first renders or when it is first set.
struct EditItem : Identifiable { //this will tell it what sheet to present
var id = UUID()
var str : String
}
struct ContentView: View {
private var stringObjects = ["test123", "helloworld", "reddit"]
#State private var editMode: EditMode = .inactive
#State private var editItem : EditItem? //the currently presented sheet -- nil if no sheet is presented
#ViewBuilder
var body: some View {
NavigationView {
List {
ForEach(self.stringObjects, id: \.self) { text in
NavigationLink( destination: HStack{Text("Test!")}) {
HStack {
Text(text)
Spacer()
if self.editMode.isEditing {
Image(systemName: "pencil.circle").imageScale(.large)
.onTapGesture {
if self.editMode.isEditing {
self.editItem = EditItem(str: text) //set the current item
}
}
}
}
}
.popover(item: $editItem) { item in //item is now a reference to the current item being presented
CustomPopOver(text: item.str)
}
}
}
.navigationBarTitle("Reproduce Editing Bug!")
.navigationBarItems(leading: EditButton())
.environment(\.editMode, $editMode)
}.navigationViewStyle(StackNavigationViewStyle())
}
}
struct CustomPopOver: View {
#Environment(\.presentationMode) private var presentationMode: Binding<PresentationMode>
var text: String
var body: some View {
VStack(spacing: 0) {
HStack() {
Spacer()
Button("Cancel") {
self.presentationMode.wrappedValue.dismiss()
}.padding()
}
Divider()
List {
Section {
Text(text)
}
}.listStyle(GroupedListStyle())
}
}
}
I also opted to use the presentationMode environment property to dismiss the popover, but you could pass the editItem binding and set it to nil as well (#Binding var editItem : EditItem? and editItem = nil). The former is just a little more idiomatic.
In a small sample SwiftUI app, I have a settings view that shows a couple of option selections, implemented as segmented controls. The text in these segmented controls visibly moves when an alert is presented or dismissed. Is there a way to get rid of this glitch?
Paste this in a Playground to reproduce:
import SwiftUI
import PlaygroundSupport
struct FlickeringSegmentsView: View {
#State var option = 0
#State var alerting = false
var body: some View {
VStack(alignment: .center, spacing: 120) {
Picker("options", selection: $option) {
Text("Option A").tag(0)
Text("Option B").tag(1)
}
.pickerStyle(SegmentedPickerStyle())
.padding(16)
Button(action: { self.alerting.toggle() },
label: { Text("Show Alert") }
)
.alert(isPresented: $alerting) {
Alert(title: Text("Alert"))
}
}
}
}
PlaygroundPage.current.setLiveView(FlickeringSegmentsView())
This issue is resolved in Xcode 12 beta using the included iOS 14 simulator (and hopefully stays that way).
I hope code below should help you:
public protocol SegmentedPickerViewElementTraits: Hashable {
var localizedText: String { get }
}
public struct SegmentedPickerView<Value, Data, ID, Label>: View
where
Value: SegmentedPickerViewElementTraits,
Data: RandomAccessCollection,
Data.Element == Value,
ID: Hashable,
Label: View {
public let data: Data
public let id: KeyPath<Data.Element, ID>
public let selection: Binding<Value>
public let label: Label
public init(data: Data,
id: KeyPath<Data.Element, ID>,
selection: Binding<Value>,
label: Label) {
self.data = data
self.id = id
self.selection = selection
self.label = label
}
public var body: some View {
Picker(selection: selection, label: label) {
ForEach(data, id: id) {
Text($0.localizedText).tag($0)
}
}
.pickerStyle(SegmentedPickerStyle())
}
}
and lets modify your code:
enum Options: UInt8, CaseIterable {
case optionA
case optionB
}
extension Options: SegmentedPickerViewElementTraits {
var localizedText: String {
switch self {
case .optionA:
return "Option A"
case .optionB:
return "Option B"
}
}
}
struct FlickeringSegmentsView: View {
#State
var option: Options = .optionA
#State
var alerting = false
var body: some View {
VStack(alignment: .center, spacing: 120) {
SegmentedPickerView(
data: Options.allCases,
id: \.self,
selection: $option,
label: Text("options")
)
.padding(16)
Button(
action: { self.alerting.toggle() },
label: { Text("Show Alert") }
)
.alert(isPresented: $alerting) {
Alert(title: Text("Alert"))
}
}
}
}