#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 }