this is my code! I want to complete this code and when I tapped on button the values of two selections start changing..
import SwiftUI
struct Container {
var one: String
var two: String
}
struct ContentView: View {
#State var container: Container
#State var list = ["1","2","3","4","5","6","7","8","9"]
var body: some View {
Picker("Select",selection: $container.one) {
ForEach(list, id: \.self) { item in
Text(item)
}
}
.pickerStyle(.menu)
Picker("Select",selection: $container.two) {
ForEach(list, id: \.self) { item in
Text(item)
}
}
.pickerStyle(.menu)
}
}
I found the solution with tuples
using this button :
Button {
(container.one , container.two) = (container.two , container.one)
}label: {
Image(systemName: "plus")
}
Related
I'm working with a SwiftUI List with a TextEditor at the bottom of the List. The TextEditor becomes active or "Focused" when a button is pressed.
To achieve this, I have added a .focused modifier to the TextEditor. When the List is scrolled to the bottom and the TextEditor is visible this works as expected and shows the keyboard when the button is pressed.
But the problem is that when the List is scrolled to the top and the TextEditor is not visible on the screen the TextField doesn't seem to get focused.
import SwiftUI
struct ContentView: View {
var array = Array(0...20)
#State private var newItemText : String = ""
#FocusState var focused: Bool
var body: some View {
VStack{
List{
ForEach(array, id: \.self) {
Text("\($0)")
}
TextEditor(text: $newItemText)
.focused($focused)
}
Button {
focused = true
} label: {
Text("Add Item")
}
}
}
}
You have to set the field in a DispatchQueue.
import SwiftUI
struct ContentView: View {
var array = Array(0...20)
#State private var newItemText : String = ""
#FocusState var focusedField: Bool
var body: some View {
VStack {
ScrollViewReader { scrollView in
List {
ForEach(array, id: \.self) {
Text("\($0)")
}
TextField("", text: self.$newItemText)
.id("TextEditor")
.focused($focusedField)
}
Button(action: {
scrollView.scrollTo("TextEditor")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.focusedField = true
}
}) {
Text("Add Item")
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Hope this solves your problem!
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())
}
}
}
}
}
}
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()
}
}
On the track to learn more and more about SwiftUI. I come accross some weird behaviors.
I have a simple view called Modal. I am using a Picker in it and set a title in the navigation bar to go in the detail view.
That works fine. The problem starts when I add a button in the nav bar.
It end up looking like this
Without the + button
With the + button
And the code is the following:
ContentView.swift
import SwiftUI
struct ContentView: View {
#State var isShowing = false
var body: some View {
VStack(content: {
Button("Modal") {
isShowing = true
}
})
.sheet(isPresented: $isShowing, content: content)
}
#ViewBuilder
func content() -> some View {
Modal()
}
}
Modal.swift
import SwiftUI
import Combine
struct Modal: View {
#State var selection: String = ""
#State var list: [String] = ["1", "2", "3", "4", "5", "6"]
var body: some View {
NavigationView(content: {
Form(content: {
self.type()
})
.navigationBarTitle("Modal", displayMode: .inline)
})
}
}
private extension Modal {
func type() -> some View {
Section(content: {
Picker(selection: $selection, label: Text("Type").bold()) {
ForEach(list, id: \.self) { item in
Text(item)
.tag(UUID())
}
.navigationBarTitle("Select")
.navigationBarItems(trailing: button())
}
})
}
func button() -> some View {
HStack(alignment: .center, content: {
Button(action: {
// Action
}) {
Image(systemName: "plus")
}
})
}
}
This is because .navigationBarItems modifier generates flat view from dynamic ForEach views, attach instead it to one view inside ForEach, like
Section(content: {
Picker(selection: $selection, label: Text("Type").bold()) {
ForEach(list, id: \.self) { item in
if item == list.last {
Text(item)
.navigationBarTitle("Select")
.navigationBarItems(trailing: button())
.tag(UUID())
} else {
Text(item)
.tag(UUID())
}
}
}
})
I have an issue using a sheet inside a ForEach. Basically I have a List that shows many items in my array and an image that trigger the sheet. The problem is that when my sheet is presented it only shows the first item of my array which is "Harry Potter" in this case.
Here's the code
struct ContentView: View {
#State private var showingSheet = false
var movies = ["Harry potter", "Mad Max", "Oblivion", "Memento"]
var body: some View {
NavigationView {
List {
ForEach(0 ..< movies.count) { movie in
HStack {
Text(self.movies[movie])
Image(systemName: "heart")
}
.onTapGesture {
self.showingSheet = true
}
.sheet(isPresented: self.$showingSheet) {
Text(self.movies[movie])
}
}
}
}
}
}
There should be only one sheet, so here is possible approach - use another sheet modifier and activate it by selection
Tested with Xcode 12 / iOS 14 (iOS 13 compatible)
extension Int: Identifiable {
public var id: Int { self }
}
struct ContentView: View {
#State private var selectedMovie: Int? = nil
var movies = ["Harry potter", "Mad Max", "Oblivion", "Memento"]
var body: some View {
NavigationView {
List {
ForEach(0 ..< movies.count) { movie in
HStack {
Text(self.movies[movie])
Image(systemName: "heart")
}
.onTapGesture {
self.selectedMovie = movie
}
}
}
.sheet(item: self.$selectedMovie) {
Text(self.movies[$0])
}
}
}
}
I changed your code to have only one sheet and have the selected movie in one variable.
extension String: Identifiable {
public var id: String { self }
}
struct ContentView: View {
#State private var selectedMovie: String? = nil
var movies = ["Harry potter", "Mad Max", "Oblivion", "Memento"]
var body: some View {
NavigationView {
List {
ForEach(movies) { movie in
HStack {
Text(movie)
Image(systemName: "heart")
}
.onTapGesture {
self.selectedMovie = movie
}
}
}
.sheet(item: self.$selectedMovie, content: { selectedMovie in
Text(selectedMovie)
})
}
}
}
Wanted to give my 2 cents on the matter.
I was encountering the same problem and Asperi's solution worked for me.
BUT - I also wanted to have a button on the sheet that dismisses the modal.
When you call a sheet with isPresented you pass a binding Bool and so you change it to false in order to dismiss.
What I did in the item case is I passed the item as a Binding. And in the sheet, I change that binding item to nil and that dismissed the sheet.
So for example in this case the code would be:
var movies = ["Harry potter", "Mad Max", "Oblivion", "Memento"]
var body: some View {
NavigationView {
List {
ForEach(0 ..< movies.count) { movie in
HStack {
Text(self.movies[movie])
Image(systemName: "heart")
}
.onTapGesture {
self.selectedMovie = movie
}
}
}
.sheet(item: self.$selectedMovie) {
Text(self.movies[$0])
// My addition here: a "Done" button that dismisses the sheet
Button {
selectedMovie = nil
} label: {
Text("Done")
}
}
}
}