SwiftUI Sort users from the nearest to the most far from current user in LazyVstack ForEach - iphone

I'm building an application for a university project, I'm pretty new in coding, and I'm actually facing an issue, I hope one of you will be able to help me.
The idea is simple, you log yourself into the application and you have cards of other users, each card is the profile of a student(user). My issue actually is that I want those cards to be arranged from the nearest user to the most far from the currentUser. Every user's location is already stored in Firebase.
There is my code for this part:
ScrollView(){
LazyVStack{
ForEach(viewModel.users) { user in
NavigationLink {
UserProfilView(user: user)
.navigationBarHidden(true)
} label:{
ProfilCard(user: user)
.frame(width: proxy.size.width/1.05, height: proxy.size.width*1.75)
}.padding()
}
}
}
I looked a lot for an answer on the internet but I didn't find any solution.
I tried multiple things but this one was the one I had the most hope for:
#State private var currentUser: User?
ScrollView(){
LazyVStack{
ForEach(viewModel.users.sorted(by: { location.distance(from: $0.currentUser) < location.distance(from: $1.currentUser)})) { user in
NavigationLink {
UserProfilView(user: user)
.navigationBarHidden(true)
}label:{
ProfilCard(user: user)
.frame(width: proxy.size.width/1.05, height: proxy.size.width*1.75)
}.padding()
}
}
}

Related

How do we control the automatic dividers in SwiftUI forms?

I'm curious, is there a way we can control (or make sense of) the automatic dividers that swiftui puts between items?
Here's some code showing strange behaviour (on iOS simulator):
struct FormSeparator: View {
#State private var passwd = ""
var body: some View {
Form {
ZStack(alignment: .trailing) {
TextField("password", text:$passwd)
Text("8 chars min")
.font(.caption)
.foregroundColor(.secondary)
}
TextField("confirm", text:$passwd)
}
}
}
Here's the result:
This seems to have something to do with the Text that I'm stacking on top of the password field. If I change it the Text to Image(systemName: "lock") then everything works as you'd expect:
Mind you, I don't even understand why it doesn't extend all the way across the form in that case. Presumably this is deliberate, has anyone seen anything official about this situation? Do we know how to control this?
In iOS 16, List/Form row separator insets automatically and aligns with the text. In iOS 16, we got two new alignments, listRowSeparatorLeading and listRowSeparatorTrailing, to work with alignmentGuide(_:computeValue:).
ZStack(alignment: .trailing) {
TextField("password", text:$passwd)
Text("8 chars min")
.font(.caption)
.foregroundColor(.secondary)
}
.alignmentGuide(.listRowSeparatorLeading) { viewDimensions in
return 0
}

SwiftUI on Mac: Help Text Always Visible Even within View with Zero Opacity

I have run into some unexpected behavior while using SwiftUI in a macOS app. I filed a Feedback with Apple in case it's a bug, but it might actually be designed to work this way, so I'm looking for a workaround.
I rely heavily on the use of .opacity() to show and hide different sections of my app with tabs. I don't use if clauses because each time the user changes the tab, you have to wait for the entire view to rebuild and that is pretty slow.
Here's a basic example that demonstrates the problem:
struct ContentView: View {
#State var viewAVisible = false
var body: some View {
VStack{
ZStack{
Text("View A Visible")
.frame(width: 500, height: 500)
.background(Color.blue)
.help("This is View A's help text. It should be invisible when View A is invisible.")
.opacity(viewAVisible ? 1 : 0)
Text("View B Visible")
.frame(width: 500, height: 500)
.background(Color.gray)
.opacity(viewAVisible ? 0 : 1)
}
Button("Toggle"){
viewAVisible.toggle()
}
}.padding()
}
}
The default app state is to hide the "View A" Text() and only show the "View B" Text(). But if you hover over View B, you still see View A's .help text:
In my opinion, if a view has .opacity(0) then its help text shouldn't show up. But regardless, I need to find a way around this.
I thought about doing something like this:
.help(viewAVisible ? "This is View A's help text..." : "")
...but that doesn't scale across dozens of views in my app--particularly among child views that don't know if their parent view is shown or hidden. As I mouse across my app, I see the help text of tons of views all over the place even though they are invisible. 😅
Has anyone run into this or have any suggestions on how to handle it?
Looks like a bug (they do not remove tracking rects), here is a demo of workaround - move help tag into background and remove it manually (tested with macOS 12.0.1)
Text("View A Visible")
.frame(width: 500, height: 500)
.background(Group {
if viewAVisible {
Color.blue.help("This is View A's help text. It should be invisible when View A is invisible.")
} else {
Color.clear
}
})
.opacity(viewAVisible ? 1 : 0)

Why is my Searchbar not working if I type too fast?

I linked a video where you can see that my searchbar isn't working if I type too fast. And here is my Code:
List{
ForEach(copyOfGeraete.filter({item in self.suchBegriffGeraete.isEmpty ? true : item.lowercased().contains(suchBegriffGeraete.lowercased())}), id: \.self){ item in
NavigationLink(destination: Text("Destination")) {
Text(item)
}
}
}
You can download the Video of my Screen while my App is running under "https://www.transfernow.net/dl/20210521Mj4UvG0O"
Thanks for your time, It would be great if someone has a idea why its lagging...
Boothosh

How can I delete a list item in SwiftUI from a child view without using onDelete(perform:)?

I have a chat feature in my app that allows you to report and block someone from communicating with you any further.
Here's my InboxView.swift that shows a user's conversations:
List(Array(conversations.conversations.enumerated()), id: \.1.id){ (index, conversation) in
VStack{
NavigationLink(destination: ChatView(conversation_id: conversation.id, avatar: conversation.avatar, displayName: conversation.displayName, user_id: conversation.receiver_id, parentIndex: index)){
ConversationList(id : conversation.id, user_id : conversation.user_id, receiver_id : conversation.receiver_id, lastMessage : conversation.lastMessage, avatar : conversation.avatar, displayName : conversation.displayName, startedAt : conversation.startedAt)
}
Divider()
}
}
The above code simply provides the end-user an interface for them to select which conversation they want to go into. Here's where things get tricky with the following view diagram:
InboxView --> ChatView --> ProfileView
Each --> represents a NavigationLink that leads to the subsequent view. On the ProfileView.Swift page, I present a button in which the end-user can block the person they are talking to. I have already figured out how to take the user back to InboxView with a series of
#Environment(\.presentationMode) var mode
and
self.mode.wrappedValue.dismiss()
but for convenience, I also want to delete the list item that was associated with the blocked user's conversation.
How can I tell InboxView which ChatView triggered the delete request and pass that through a function like this?
func removeRow(at offsets: IndexSet){
if let first = offsets.first {
let conversationRemoving = conversations.conversations[first]
conversations.conversations.remove(at:first)
}
}
I don't see in the documentation for presentationMode to trigger a function via wrappedValue
It could be done directly inside List (as we have access to index in it) and remove record from already fetched results.
If person model would have specific field (say blocked), then it could be like below (in pseudo-code, to be shorter):
List(Array(conversations.conversations.enumerated()), id: \.1.id){ (index, conversation) in
VStack{
NavigationLink(destination: ChatView(...)) {
ConversationList(...)
}
Divider()
}
.onAppear { // called on show and on return back
if conversation.receiver.blocked { // << here !!
// better to do it asynchronously
DispatchQueue.main.async {
self.conversations.conversations.remove(at: index) // << here !!
}
}
}
}

SwiftUI pick a value from a list with ontap gesture

i'm try to pick some value from a swiftUI list with the ontapGesture.
I have a Searchlist of item, the user need to choose one item and then the app will send the choice to an array that later will be use for other info.
now my problem is how do I do that? how can do it?
as you can see from the code below, I want to pick the value of the item.icaoAirport corresponding to that raw and pass to an array.
List(dm.vettoreAeroporti.filter{
// $0.icaoCode.contains(searchTerm)
$0.icaoAirport.localizedCaseInsensitiveContains(searchTerm)
}) { item in
HStack {
Text(item.icaoAirport).bold()
Spacer()
Text(item.nameAirport)
}
}
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.dm.openFileJson(fileName: "data")
}
}
.onTapGesture {
// ?? I need to take the value of the item.icaoAirport corresponding to that raw
}
thanks in advance for the help.
While your, Damiano, answer is right it works only on tap on the Text.
Sometimes it is needed to have the whole row tappable, so here is the solution for this case:
List(items) { item in
HStack {
Text(item.icaoAirport).bold()
Spacer()
Text(item.nameAirport)
}
.contentShape(Rectangle())
.onTapGesture {
print("touched item \(item)")
}
}
Thanks Paul for this (https://www.hackingwithswift.com/quick-start/swiftui/how-to-control-the-tappable-area-of-a-view-using-contentshape)
Note, that if you have content only on one side of the HStack e.g:
HStack {
Text("Some text") // This text will be on the left side
}
then the .contentShape(Rectange()) will work only for the width of the text. So to enable it for the whole width just add a trailing Spacer() like this:
HStack {
Text("Some text")
Spacer() // This will take all the space from the end of the text up to the end of the whole row
}
.contentShape(Rectangle())
I solved my issue:
HStack {
Text(item.icaoAirport).bold()
Spacer()
Text(item.nameAirport)
.onTapGesture {
print("touched item \(item.icaoAirport)")
}
}
List {
ForEach(Type.allCases, id: \.self) { type in
FilterRow(Color: type.Color,
title: type.Title)
.contentShape(Rectangle())
.onTapGesture {
print("Ammu type:\(type)")
}
}
}
This one is quite old but the accepted answers are pretty strange. I was able to achieve this effect in a much simpler way. Consider:
List {
ForEach(myItems) { item in
Button {
// action
} label: {
Text(item.name)
}
}
}
Now doing it this way, the button is edge-to-edge in the row. However, the button is now the accent color of your app. That's probably not what you want! So I tried adding:
Button(...)
.buttonStyle(PlainButtonStyle())
The text of the button is now the primary foreground color, great! But there's a different problem now! The button no longer goes edge-to-edge, it's only the text. To fix this, i instead:
Button(...)
.foregroundColor(.primary)
This is edge-to-edge with primary color text and custom actions. No need to create HStacks with spacers and an onTap, it's all just handled. OP's post has text on both sides so likely still needs an HStack for their specific usecase but a Button is still probably a better choice.