Обновите каждый элемент кнопки в списке вместо всех кнопок сразу в Swift / SwiftUI

#ios #swift #swiftui

Вопрос:

В настоящее время я пытаюсь изменить кнопку с одного вида на другой в зависимости от того, что нажимает пользователь. Они получат список/прокрутку ожидающих запросов, и если пользователь нажмет «Принять», он должен изменить эту кнопку на кнопку «Принято», а если пользователь нажмет «отклонить», он должен изменить эту кнопку на кнопку «отклонено». Я сталкиваюсь с проблемой, когда, если пользователь нажимает «Принять», все кнопки ожидающего запроса меняются на кнопку «Принято» и аналогично для случая отклонения.

 
import SwiftUI
import Firebase
import SDWebImageSwiftUI
import Foundation
import Combine


struct BottomSheet: View {
    
//    @ObservedObject var userData : UserViewModel
    
    var edges = UIApplication.shared.windows.first?.safeAreaInsets
    
 
    @StateObject var userData = UserViewModel()

    @State var declinedRequest: Bool = false

    @State var acceptedRequest: Bool = false
    
    
    
    var body: some View {
        
        VStack{
            
            Spacer()
            
            VStack(spacing: 12){
                
               
                Divider()
               
                
                
                   
                    // here are the buttons inside the scrollview 
                    ScrollView{
                        ForEach(userData.pendingFriendUsers){ person in
                            
                            HStack{
                                if person.pic != ""{
                                    WebImage(url: URL(string: person.pic)!)
                                        .resizable()
                                        .aspectRatio(contentMode: .fill)
                                        .frame(width: 60, height: 60)
                                        .clipShape(Circle())
                                        .padding(.leading, 30)
                                        .padding(.trailing, 10)


                                }else{
                                    Circle()
                                        .stroke(Color.black.opacity(0.8), lineWidth: 2)
                                        .frame(width: 60, height: 60)
                                        .padding(.leading, 20)
                                        .padding(.trailing, 10)

                                }
                                VStack(alignment: .leading){
                                    Text("(person.name)")
                                        .font(.custom("Helvetica Neue", size: 16))
                                        .foregroundColor(Color.white).bold()
                                    Text("@(person.username)")
                                        .font(.custom("Helvetica Neue", size: 16))
                                        .foregroundColor(Color.white)
                                        .opacity(0.8)
                                }
                                Spacer()
                                
                                if person.isFriends == 2 {
                                    RoundedRectangle(cornerRadius: 15, style: .continuous)
                                        .fill(Color("Dark-Grey"))
                                        .frame(width: 150, height: 40)
                                        .padding(.trailing, 25)
                                        .overlay(
                                            Text("Request removed")
                                                .font(.custom("Helvetica Neue", size: 14))
                                                .foregroundColor(Color.white)
                                                .padding(.trailing, 25)
                                        )
                                }else if person.isFriends == 1 {
                                    RoundedRectangle(cornerRadius: 15, style: .continuous)
                                        .fill(Color.white)
                                        .frame(width: 150, height: 40)
                                        .padding(.trailing, 25)
                                        .overlay(
                                            Text("Request accepted")
                                                .font(.custom("Helvetica Neue", size: 14))
                                                .foregroundColor(Color.black)
                                                .padding(.trailing, 25)
                                        )
                                }else {
                                    Button(action: {
                                        print("declined friend request for (person.uid)")
                                        withAnimation(){declinedRequest = true}
                                        userData.declineFriendRequest(otherUserUID: person.uid)

                                    }){
                                        RoundedRectangle(cornerRadius: 12, style: .continuous)
                                            .fill(Color("Dark-Grey"))
                                            .frame(width: 55, height: 26.5)
                                            .padding(.trailing, 13)
                                            .overlay(
                                                Image("x")
                                                    .renderingMode(.template)
                                                    .resizable()
                                                    .foregroundColor(Color.white)
                                                    .opacity(0.8)
                                                    .frame(width: 12, height: 12)
                                                    .padding(.trailing, 12)
                                            )
                                    }
                                   
                                    Button(action: {
                                        print("accepted friend request for (person.uid)")
                                        withAnimation(){acceptedRequest = true}
                                        userData.acceptFriendRequest(otherUserUID: person.uid)

                                    }){
                                    RoundedRectangle(cornerRadius: 15, style: .continuous)
                                        .fill(Color.white)
                                        .frame(width: 50, height: 25)
                                        .padding(.trailing, 35)
                                        .overlay(
                                            Image("check")
                                                .resizable()
                                                .aspectRatio(contentMode: .fill)
                                                .frame(width: 20, height: 20)
                                                .padding(.trailing, 33)
                                        )
                                    }
                                }
                                
                                
                            }.padding(.top, 12.5)
                            
                        }
            
                    }
                       
            
                        
                    Spacer()
                    
                .contentShape(Rectangle())
            }
            .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height / 1.15)
            .padding(.top)
            .background(Color("gray")
                            .clipShape(CustomCorner(corners: [.topLeft,.topRight])))
            .offset(y: offset)
            // bottom sheet remove swipe gesture....
            .gesture(DragGesture().onChanged(onChanged(value:)).onEnded(onEnded(value:)))
            .offset(y: showSheet ? 0 : UIScreen.main.bounds.height)
        }
        .ignoresSafeArea()
        .background(
            Color.black.opacity(showSheet ? 0.3 : 0).ignoresSafeArea()
                .onTapGesture(perform: {
                    withAnimation{showSheet.toggle()}
                })
        )
    }
    
    
    
    
}


 

Когда отклоненный или принятый запрос изменяется, он не сопоставляется с кнопкой, поэтому все в представлении » Для » изменяется вместо отдельной кнопки. Я добавил свой базовый код ниже, но некоторые вещи, которые я пробовал, делают опубликованную переменную в моем классе модели пользовательских данных, но к тому времени она не обновляется по мере продвижения, и я также пытался сделать опубликованную переменную здесь. Похоже, я как-то связан с индексами и отображением, но я не уверен, каков наилучший подход. Спасибо за вашу помощь

Изменить: Вот соответствующие разделы из моей модели представления пользователей:

 import SwiftUI
import Firebase
import Combine
import Foundation



struct pendingFriendUser: Identifiable {
    var id: Int
    var uid: String
    var name: String
    var username: String
    var pic: String
    var isFriends: Int
    
    
    init(id: Int, uid: String, name: String, username: String, pic: String, isFriends: Int){
        self.id = id
        self.uid = uid
        self.name = name
        self.username = username
        self.pic = pic
        self.isFriends = isFriends
    }
}



class UserViewModel : ObservableObject{
    
    @Published var userInfo = UserModel(username: "", pic: "", name: "", age: 1, uid: "", phoneNumber: "")
    
    let ref = Firestore.firestore()
    let uid = Auth.auth().currentUser!.uid
    
 
    @Published var pendingFriendUsers: [pendingFriendUser]

    //show add friends sheet
    @AppStorage("showSheet") var showSheet = false

    //check friendship variabe
    @Published var isFriend = 0
    
    

    init() {
        self.searchedUsers = []
        
        self.pendingFriendUsers = []
        
        fetchUser(uid: uid) { (user) in
                    self.userInfo = user
        }
    }
func getPendingRequests(){
        //check if friends has any false memberships
        var pendingFriendRequests: [String: Bool] = [:]
        self.ref.collection("Users").document(uid).getDocument(){
            (document, err) in
            
            if let err = err {
                print("Error getting documents (err)")
            } else {
                
                if document!.data()!["friends"] != nil {
                    pendingFriendRequests = document!.data()!["friends"] as! [String : Bool]
                }
                
                //filter based on false pending friend requests
                self.pendingFriendUsers.removeAll()

                var friendUserID = 0 
                for key in pendingFriendRequests.keys {
                    if pendingFriendRequests[key] == false {
                        self.ref.collection("Users").document(key).getDocument(){
                            (friendDocument, err) in
                            
                            if let err = err {
                                print("Error getting documents (err)")
                            } else {
                                let pendingFriendUsername = (friendDocument?.data()?["username"]) as! String
                                let pendingFriendUID = (friendDocument?.data()?["uid"]) as! String
                                let pendingFriendName = (friendDocument?.data()?["name"]) as! String
                                let pendingFriendPic = (friendDocument?.data()?["imageurl"]) as! String

                                self.pendingFriendUsers.append(pendingFriendUser(id: friendUserID, uid: pendingFriendUID , name: pendingFriendName , username: pendingFriendUsername, pic: pendingFriendPic, isFriends: 0))
                                
                                friendUserID  = 1

                            }
                        }

                    }
                }
            }

        }
    }

   ...
    func acceptFriendRequest(otherUserUID: String){
        for var pendingFriend in pendingFriendUsers {
            if pendingFriend.uid == otherUserUID {
                pendingFriend.isFriends = 1
            }
        }
        self.ref.collection("Users").document(self.uid).setData(
            [ "friends": [
                otherUserUID: true
            ] ]
            , merge: true)
        
        self.ref.collection("Users").document(otherUserUID).setData(
            [ "friends": [
                self.uid: true
            ] ]
            , merge: true)
    }
    
    func declineFriendRequest(otherUserUID: String){
        for var pendingFriend in pendingFriendUsers {
            if pendingFriend.uid == otherUserUID {
                pendingFriend.isFriends = 2
            }
        }
        
        self.ref.collection("Users").document(self.uid).updateData([
            "friends.(otherUserUID)": FieldValue.delete(),
        ]) { err in
            if let err = err {
                print("Error updating document: (err)")
            } else {
                print("Document successfully updated")
            }
        }
    }
    func checkFriendRequest(otherUserUID: String){
        
        //0 if not found in friend list or friend request
        //1 means theyre friends
        //2 means that user sent self/me a friend request
        var pendingFriendRequests: [String: Bool] = [:]
        self.ref.collection("Users").document(self.uid).getDocument(){
            (document, err) in
            
            if let err = err {
                print("Error getting documents (err)")
            } else {
                pendingFriendRequests = document!.data()!["friends"] as! [String : Bool]
                

                for key in pendingFriendRequests.keys {
                    if key == otherUserUID{

                        if pendingFriendRequests[key] == true {
                            self.isFriend = 1
                        }else if pendingFriendRequests[key] == false {
                            self.isFriend = 2
                            
                        }
                    }
                }
            }
        }
    }
    
    
    
}
 

Отредактировано с помощью переменной isFriends

Комментарии:

1. Я не верю, что здесь достаточно кода или информации, чтобы действительно дать хорошее решение. Вам нужен какой — то метод для хранения accepted и declined для каждого пользователя. Но вы не поделились своей UserViewModel информацией или какой-либо информацией о том, как запросы на добавление в друзья отражаются в вашей модели или на серверной части. Похоже, что должно быть относительно просто иметь свойство на каждом User , говорящее, являются ли они друзьями или нет.

2. Верно, но сейчас у вас есть только одно состояние «принято» или «отклонено». Вам нужно место для хранения состояния для каждого пользователя. Логично сделать это в модели представления, если только я чего-то не упускаю.

3. Как я уже говорил, здесь недостаточно информации, чтобы дать рекомендацию по этому поводу, поскольку нет информации о том, как все хранится. Вы можете выполнить запрос на запись, изменив состояние дружбы, а затем изменить свое локальное состояние, чтобы отразить это, не выполняя чтение. Но, возможно, у вас уже есть что-то вроде базы snapshotListener данных in Firebase, например, которая автоматически получит новые данные.

4. Почему бы просто не добавить friends свойство в вашу pendingFriendUser модель (кстати, вы должны использовать имена типов с заглавной буквы в Swift) и обновить его, когда запрос будет принят, если вас это беспокоит? Недостатком является то, что если бы не удалось принять (проблема с сетевым подключением или что-то в этом роде), ваше состояние было бы несинхронизированным.

5. И как вы изменяете значение? Можете ли вы обновить свой вопрос новым кодом?

Ответ №1:

Прямо сейчас у вас есть это:

 for var pendingFriend in pendingFriendUsers {
   if pendingFriend.uid == otherUserUID {
     pendingFriend.isFriends = 1
   }
}
 

На самом деле это ничего не меняет pendingFriendUsers , потому pendingFriendUser что это a struct , которое передается по значению в Swift, а не по ссылке. Так var pendingFriend же как и копия версии в pendingFriendUsers .

Вместо этого вы могли бы сделать что-то вроде этого:

 self.pendingFriendUsers = self.pendingFriendUsers.map { pendingFriend in
  guard pendingFriend.uid == otherUserUID else { return pendingFriend } //return the original if the UID doesn't match
  var modified = pendingFriend //make a mutable copy
  modified.isFriends = 1
  return modified //return the modified version
}
 

или:

 guard let index = self.pendingFriendUsers.firstIndex(where: { $0.uid == otherUserUID }) else { return } //get the index of the matching UID
self.pendingFriendUsers[index].isFriends = 1 //modify the item at that index
 

Комментарии:

1. Это сработало потрясающе! Я изменил его следующим образом: если пусть index = pendingFriendUsers.firstIndex(где: {$0.uid == Идентификатор другого пользователя}) { pendingFriendUsers[индекс].isFriends = 2 }