SwiftUI ForEach Buttons making 1 large button - swiftui

I am trying to list multiple cells of button in my scrollview but when I do that it lists them out when I click on any of them it clicks all of them and looks like its just 1 big button with 4 different cells
ZStack{
Color("Background")
VStack{
ScrollView(showsIndicators: false){
ForEach(userVM.user?.chats ?? [ChatModel()]){ chat in
Button(action:{
print(chat.name ?? " ")
},label:{
Text("\(chat.name ?? " ")")
})
}
}
}
}

In a ForEach loop, you want to make sure each ChatModel is unique. Usually you do this by including an id in your ChatModel and making it Identifiable.
So try this, if you do not have an id in ChatModel:
ForEach(userVM.user?.chats ?? [ChatModel()], id: \.self){ chat in ...}
or this, if ChatModel has an id:
ForEach(userVM.user?.chats ?? [ChatModel()], id: \.id){ chat in ...}

Related

swiftui 2.0 Image Gallery onTapgesture only shows the first image in the array

I am creating a reusable gallery view for an app and am having difficulties when any picture is tapped it suppose to become full screen but only the first picture in the array is shown every time no matter the picture tapped. Below is my code, thanks.
import SwiftUI
struct ReusableGalleryView: View {
let greenappData: GreenAppNews
let gridLayout: [GridItem] = Array(repeating: GridItem(.flexible()), count: 3)
#State private var fullscreen = false
#State private var isPresented = false
var body: some View {
VStack{
ScrollView{
LazyVGrid(columns: gridLayout, spacing: 3) {
ForEach(greenappData.GreenAppGallery, id: \.self) { item in
Image(item)
.resizable()
.frame(width: UIScreen.main.bounds.width/3, height: 150)
.onTapGesture {
self.isPresented.toggle()
print(" tapping number")
}
.fullScreenCover(isPresented: $isPresented) {
FullScreenModalView( imageFiller: item)
}
.background(Color.gray.opacity(0.5))
}
}
.padding(.horizontal)
}
}
}
}
This is an example of the json data:
{
"id" : "1",
"GreenAppGallery" : [
"Picture-1",
"Picture-2",
"Picture-3",
"Picture-4",
"Picture-5",
"Picture-6",
"Picture-7",
"Picture-8",
"Picture-9",
"Picture-10"
]
},
fullScreenCover, like sheet tends to create this type of behavior in iOS 14 when using isPresented:.
To fix it, you can change to the fullScreenCover(item: ) form.
Not having all of your code, I'm not able to give you an exact version of what it'll look like, but the gist is this:
Remove your isPresented variable
Replace it with a presentedItem variable that will be an optional. Probably a datatype that is in your gallery. Note that it has to conform to Identifiable (meaning it has to have an id property).
Instead of toggling isPresented, set presentedItem to item
Use fullScreenCover(item: ) { presentedItem in FullScreenModalView( imageFiller: presentedItem) } and pass it your presentedItem variable
Move the fullScreenCover so that it's attached to the ForEach loop rather than the Image
Using this system, you should see it respond to the correct item.
Here's another one of my answers that covers this with sheet: #State var not updated as expected in LazyVGrid

Handling focus event changes on tvOS in SwiftUI

How do I respond to focus events on tvOS in SwiftUI?
I have the following SwiftUI view:
struct MyView: View {
var body: some View {
VStack {
Button(action: {
print("Button 1 pressed")
}) {
Text("Button 1")
}.focusable(true) { focused in
print("Button 1 focused: \(focused)")
}
Button(action: {
print("Button 2 pressed")
}) {
Text("Button 2")
}.focusable(true) { focused in
print("Button 2 focused: \(focused)")
}
}
}
Clicking either of the buttons prints out correctly. However, changing focus between the two buttons does not print anything.
This guy is doing the same thing with rows in a list & says it started working for him with the Xcode 11 GM, but I'm on 11.5 and it's definitely not working (at least not for Buttons (or Toggles - I tried those too)).
Reading the documentation, this appears to be the correct way to go about this, but it doesn't seem to actually work. Am I missing something, or is this just broken?
In case anybody else stumbles upon this question, the answer is to make your view data-focused
So, if you have a list of movies in a scrollview, you would add this to your outer view:
var myMovieList: [Movie];
#FocusState var selectedMovie: Int?;
And then somewhere in your body property:
ForEach(0..<myMovieList.count) { index in
MovieCard(myMovieList[i])
.focusable(true)
.focused($selectedMovie, equals: index)
}
.focusable() tells the OS that this element is focusable, and .focused tells it to make that view focused when the binding variable ($selected) equals the value passed to equals: ...
For example on tvOS, if you wrap this in a scrollview and press left/right, it will change the selection and update the selected index in the $selectedMovie variable; you can then use that to index into your movie list to display extra info.
Here is a more complete example that will also scale the selected view:
https://pastebin.com/jejwYxMU

How can I achieve only one possible selection similar to radiogroup in SwiftUI

How can I achieve only one single selection similar to a group of radio buttons using a list of views in SwiftUI?
When a button is pressed, you can store the value for which one was selected.
And you can style the buttons based on which one is selected.
The following code should do what you're looking for. Whichever button is last pressed will be selected, and only the selected button will be blue, because the styling is based on the property. And another button clears the selection.
struct ContentView: View {
let buttons = ["A", "B", "C"]
#State public var buttonSelected: Int?
var body: some View {
VStack(spacing: 20) {
ForEach(0..<buttons.count) { button in
Button(action: {
self.buttonSelected = button
}) {
Text("Button \(self.buttons[button])")
.padding()
.foregroundColor(.white)
.background(self.buttonSelected == button ? Color.blue : Color.green)
.clipShape(Capsule())
}
}
Button(action: {
self.buttonSelected = nil
}) {
Text("Clear Selection")
}
}
}
}

Scroll up to see TextField when the keyboard appears in SwiftUI

In my use case, I have to put a TextField below the available items in a List and by using that TextField, we can add items to the List.
Initially, there're no list items (items array is empty)
Here's a minimal, reproducible example
import SwiftUI
struct ContentView: View {
#State var itemName = ""
#State var items = [String]()
var body: some View {
NavigationView {
List {
ForEach(self.items, id: \.self) {
Text($0)
}
VStack {
TextField("Item Name", text: $itemName)
.textFieldStyle(RoundedBorderTextFieldStyle())
Button(action: {
self.items.append(self.itemName)
self.itemName = ""
}) {
Text("Add Item")
}
}
}
.navigationBarTitle(Text("Title"))
}
}
}
We can add a new item to the list by typing something in the TextField and clicking "Add Item" Button , Every item that we add using TextField appears above the TextField in the List. So the TextField goes down in the List (Just like Apple’s Reminders app).
If the app has many items (more than 7 items), the keyboard covers the TextField when the keyboard appears and we can’t see the TextField.
Check this screenshot:
What I want to know is how to automatically scroll the List (move the view up) to see the TextField when keyboard appears (like in Apple's Reminders app).
I had a similar problem in my recent project, the easiest way for me to solve it was to wrap UITextField in SwiftUI and from my custom wrapper reach to the parent scroll view and tell it to scroll when the keyboard appears. I tried my approach on your project and it seems to work.
If you take my code for the wrapper and other files from this GitHub folder: https://github.com/LostMoa/SwiftUI-Code-Examples/tree/master/ScrollTextFieldIntoVisibleRange and then replace the SwiftUI TextField with my custom view (TextFieldWithKeyboardObserver) then it should scroll.
import SwiftUI
struct ContentView: View {
#State var itemName = ""
#State var items = [String]()
var body: some View {
NavigationView {
List {
ForEach(self.items, id: \.self) {
Text($0)
}
VStack {
TextFieldWithKeyboardObserver(text: $itemName, placeholder: "Item Name")
Button(action: {
self.items.append(self.itemName)
self.itemName = ""
}) {
Text("Add Item")
}
}
}
.navigationBarTitle(Text("Title"))
}
}
}
I recently wrote an article explaining this solution: https://lostmoa.com/blog/ScrollTextFieldIntoVisibleRange/

number input with SwiftUI

The new SwiftUI is fantastic to play with... I'm trying to use Forms instead of Eureka. A couple of questions:
What is the best way to let the user enter a number? I used to do that with a UIPickerView, see image .
With SwiftUI I only found Textfield, as in the following code:
import SwiftUI
struct SettingsView : View {
#State var email = ""
#State var amount = ""
var body: some View {
NavigationView {
Form {
Section(header: Text("Email")) {
TextField("Your email", text: $email)
.textFieldStyle(.roundedBorder)
}
Section(header: Text("Amount")) {
TextField("Amount", text: $amount)
.textFieldStyle(.roundedBorder)
}
.navigationBarTitle("Settings")
}
}
}
}
When you click in the field, the ABC keyboard comes up. The user can select '123' to get the number keyboard. But I would like to see a number pad instead.
Also, the keyboard blocks the view (if you have more fields); the view doesn't scroll up to make room for the keyboard.
Is it possible to get rid of the keyboard when the user clicks outside a TextField?
And is there a way to 'validate the entries'? For instance, the amount should be between 10 and 1.000?