我目前有一个名为.onAppear的函数,该函数根据用户选择的状态将状态设置为位置名称。该值将添加到Firestore查询中,并根据所选位置给出结果。我的问题是,当用户选择其他位置时,不会再次调用.onAppear中的函数。如何更改代码结构,以便在状态更改后再次运行Firestore查询?下面是主视图和模式视图中的代码。
struct ExploreView: View {
@State var selectedTab = "Explore"
@State var data: [RestaurantObject] = []
@State var newsData: [NewsObject] = []
@State var metro = "Orlando"
@State var didAppear = false
@State var appearCount = 0
@State var showingLocations = false
let db = Firestore.firestore()
var body: some View {
NavigationView {
ScrollView(.vertical, showsIndicators: false) {
VStack {
HStack {
Text("Explore ")
.font(.title)
.fontWeight(.bold)
+ Text(self.metro)
.font(.title)
.fontWeight(.bold)
Spacer()
Button(action: {
self.showingLocations.toggle()
}) {
Text ("Change")
.font(.callout)
}.sheet(isPresented: $showingLocations) {
LocationsModal(showingLocations: self.$showingLocations, metro: self.$metro)
}
}.padding(.horizontal)
.padding(.bottom, 30)
.onAppear(perform: getNews)
// Cuisine Slider
HStack {
Text("All cuisines")
.font(.title2)
.fontWeight(.bold)
Spacer()
NavigationLink (destination: CuisinesView()) {
Text("View all")
}
}.padding(.horizontal)
.padding(.bottom, 20)
ScrollView (.horizontal, showsIndicators: false) {
HStack(spacing: 12.0) {
CuisineTile(image: "cuisine_breakfast", name: "Brunch")
CuisineTile(image: "cuisine_bbq", name: "BBQ")
CuisineTile(image: "cuisine_brazilian", name: "Brazilian")
CuisineTile(image: "cuisine_caribbean", name: "Caribbean")
CuisineTile(image: "cuisine_cuban", name: "Cuban")
CuisineTile(image: "cuisine_mexican", name: "Mexican")
CuisineTile(image: "cuisine_seafood", name: "Seafood")
CuisineTile(image: "cuisine_soulfood", name: "Soul Food")
.padding(.trailing, 31)
}.offset(x: 16)
}
// Featured Section
VStack {
HStack {
Text("Featured Eats")
.font(.title2)
.fontWeight(.bold)
.foregroundColor(Color.white)
Spacer()
}.padding(.horizontal)
.padding(.top)
HStack {
Text("Join us every month as we highlight business owners with uplifting and inspiring stories.")
.foregroundColor(Color.white)
.font(.subheadline)
Spacer()
}.padding(.horizontal)
.padding(.vertical, 5)
ZStack {
VStack {
HStack {
Image("placeholder_feature")
// .frame(width: /*@START_MENU_TOKEN@*/100/*@END_MENU_TOKEN@*/, height: /*@START_MENU_TOKEN@*/100/*@END_MENU_TOKEN@*/, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
.resizable()
.scaledToFill()
}.padding(.horizontal)
}
VStack {
Spacer()
HStack {
Text ("The story behind Papa Llama. Orlando's newest Peruvian restaurant.")
.font(.body)
.foregroundColor(Color.white)
.padding()
Spacer()
}.background(Color("CharcoalGray"))
.cornerRadius(10, corners: [.bottomLeft, .bottomRight])
.padding(.horizontal)
}
}
HStack {
Button(action: /*@START_MENU_TOKEN@*//*@PLACEHOLDER=Action@*/{}/*@END_MENU_TOKEN@*/) {
Text ("Read More")
.font(.subheadline)
.fontWeight(.medium)
.padding(.horizontal, 16)
.padding(.vertical, 10)
.background(Color.black)
.foregroundColor(.white)
.overlay (
RoundedRectangle(cornerRadius: 6) .stroke(Color.white, lineWidth: 2)
)
}.padding(.top, 12)
.padding(.horizontal)
Spacer()
}
.padding(.bottom)
}.background(Color.black)
.padding(.vertical)
VStack {
HStack {
Text(self.metro).font(.title2)
.fontWeight(.bold) + Text(" Eats")
.font(.title2)
.fontWeight(.bold)
Spacer()
NavigationLink(destination: LocationRestaurants(location: self.metro)) {
Text("View all")
}
}.padding(.horizontal)
.padding(.top, 20)
VStack {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 12.0) {
ForEach((self.data), id: \.self.restaurantID) { item in
NavigationLink(destination: RestaurantDetail(name: item.restaurantName, image: item.restaurantImage, address: item.restaurantAddress)) {
ExploreTile(image: item.restaurantImage, name: item.restaurantName, category: item.restaurantCategory)
}.buttonStyle(PlainButtonStyle())
}
}.offset(x: 16)
}
}.padding(.bottom, 30)
}
HStack {
Text("News Bites")
.font(.title2)
.fontWeight(.bold)
Spacer()
}.padding(.horizontal)
ScrollView(.horizontal, showsIndicators: false) {
HStack {
ForEach((self.newsData), id: \.self.newsID) { item in
NavigationLink(destination: NewsDetailView(url: item.newsURL)) {
NewsArticleTile(title: item.newsTitle, photo: item.newsPhoto, author: item.newsAuthor, source: item.newsSource, url: item.newsURL).padding(.trailing, 10)
}.buttonStyle(PlainButtonStyle())
}
}
}.offset(x: 16)
.padding(.bottom, 100)
}
}
.onChange(of: self.metro, perform: { _ in
getRestaurants()
print("Metro value changed to \(self.metro)")
})
.onAppear(perform: getRestaurants)
.navigationBarTitle("")
.toolbar {
ToolbarItem(placement: .principal) {
Image("ue_logo")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
}
}
}
}
func getRestaurants() {
if didAppear == false {
appearCount += 1
self.data.removeAll()
self.db.collection("businesses").whereField("metro", isEqualTo: self.metro).limit(to: 4).addSnapshotListener ( {(querySnapshot, err) in
if let err = err {
print("Error getting documents \(err)")
} else {
for document in querySnapshot!.documents {
let id = document.documentID
let name = document.get("name") as! String
let image = document.get("photo") as? Array ?? [""]
let category = document.get("category") as? Array ?? [""]
let address = document.get("address1") as! String
let city = document.get("city") as! String
let state = document.get("state") as! String
let zipcode = document.get("zip") as! String
let owned = document.get("owned") as! String
let phone = document.get("phone") as! String
let metro = document.get("metro") as! String
let url = document.get("website") as! String
let delivery = document.get("delivery") as! Bool
let sitdown = document.get("sitdown") as! Bool
let takeout = document.get("takeout") as! Bool
let outdoor = document.get("outdoor") as! Bool
self.data.append(RestaurantObject(id: id, name: name, image: image[0], category: category[0], address: address, city: city, state: state, zipcode: zipcode, owned: owned, phone: phone, metro: metro, url: url, delivery: delivery, sitdown: sitdown, takeout: takeout, outdoor: outdoor))
}
}
})
}
didAppear = true
}
func getNews() {
if didAppear == false {
appearCount += 1
self.newsData.removeAll()
self.db.collection("news").limit(to: 3).getDocuments() {(querySnapshot, err) in
if let err = err {
print("Error getting articles \(err)")
} else {
for document in querySnapshot!.documents {
let id = document.documentID
let title = document.get("title") as! String
let photo = document.get("photo") as! String
let author = document.get("author") as! String
let source = document.get("source") as! String
let url = document.get("url") as! String
self.newsData.append(NewsObject(id: id, title: title, photo: photo, author: author, source: source, url: url))
}
}
}
}
}
}
struct ExploreView_Previews: PreviewProvider {
static var previews: some View {
ExploreView()
}
}
extension View {
func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View {
clipShape( RoundedCorner(radius: radius, corners: corners) )
}
}
struct RoundedCorner: Shape {
var radius: CGFloat = .infinity
var corners: UIRectCorner = .allCorners
func path(in rect: CGRect) -> Path {
let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
return Path(path.cgPath)
}
}
class RestaurantObject: ObservableObject {
@Published var restaurantID: String
@Published var restaurantName: String
@Published var restaurantImage: String
@Published var restaurantCategory: String
@Published var restaurantAddress: String
@Published var restaurantCity: String
@Published var restaurantState: String
@Published var restaurantZip: String
@Published var restaurantOwned: String
@Published var restaurantPhone: String
@Published var restaurantMetro: String
@Published var restaurantURL: String
@Published var restaurantDelivery: Bool
@Published var restaurantSitdown: Bool
@Published var restaurantTakeout: Bool
@Published var restaurantOutdoor: Bool
init(id: String, name: String, image: String, category: String, address: String, city: String, state: String, zipcode: String, owned: String, phone: String, metro: String, url: String, delivery: Bool, sitdown: Bool, takeout: Bool, outdoor: Bool) {
restaurantID = id
restaurantName = name
restaurantImage = image
restaurantCategory = category
restaurantAddress = address
restaurantCity = city
restaurantState = state
restaurantZip = zipcode
restaurantOwned = owned
restaurantPhone = phone
restaurantMetro = metro
restaurantURL = url
restaurantDelivery = delivery
restaurantSitdown = sitdown
restaurantTakeout = takeout
restaurantOutdoor = outdoor
}
}
class NewsObject: ObservableObject {
@Published var newsID: String
@Published var newsTitle: String
@Published var newsPhoto: String
@Published var newsAuthor: String
@Published var newsSource: String
@Published var newsURL: String
init(id: String, title: String, photo: String, author: String, source: String, url: String) {
newsID = id
newsTitle = title
newsPhoto = photo
newsAuthor = author
newsSource = source
newsURL = url
}
}
模式视图
import SwiftUI
import Firebase
import SDWebImageSwiftUI
struct LocationsModal: View {
@State var data: [LocationsObject] = []
@Binding var showingLocations: Bool
@Binding var metro: String
let db = Firestore.firestore()
var body: some View {
NavigationView {
ScrollView(/*@START_MENU_TOKEN@*/.vertical/*@END_MENU_TOKEN@*/, showsIndicators: false) {
HStack {
Text("Choose a location")
.font(.title)
.fontWeight(.bold)
Spacer()
}.padding(.horizontal)
.padding(.bottom, 30)
LazyVStack {
ForEach((self.data), id: \.self.locationID) { item in
Button(action: {
self.showingLocations = false; self.metro = item.locationName}) {
HStack {
WebImage(url: URL(string: item.locationImage))
.onSuccess { image, data, cacheType in
}
.resizable()
.placeholder(Image(systemName: "photo"))
.placeholder {
Rectangle().foregroundColor(.gray)
}
.indicator(.activity) // Activity Indicator
.transition(.fade(duration: 0.5))
.scaledToFill()
.frame(width: 80, height: 80, alignment: .center)
.clipped()
Text(item.locationName)
Spacer()
}
}
Spacer()
}
.buttonStyle(PlainButtonStyle())
.padding(.horizontal)
}
}.onAppear {
self.getLocations()
}
.navigationBarTitle("")
.toolbar {
ToolbarItem(placement: .principal) {
Image("ue_logo")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
}
}
}
}
func getLocations() {
self.data.removeAll()
self.db.collection("metros").order(by: "name").getDocuments() {(querySnapshot, err) in
if let err = err {
print("Error getting documents \(err)")
} else {
for document in querySnapshot!.documents {
let id = document.documentID
let name = document.get("name") as! String
let image = document.get("image") as! String
let lat = document.get("lat") as! Double
let long = document.get("long") as! Double
self.data.append(LocationsObject(id: id, name: name, image: image, lat: lat, long: long))
}
}
}
}
}
答案 0 :(得分:1)
尚不清楚您想对代码中的哪个状态执行与.onAppear
相同的操作,但是方法如下:
.onAppear(perform: getNews)
//.onReceive(Just(_your_state_property_)) { _ in // SwiftUI 1.0 + import Combine
.onChange(of: _your_state_property_) { _ in // SwiftUI 2.0
self.getNews()
}
答案 1 :(得分:0)
感谢Asperi带领我走上了正确的道路。我的代码中的问题是我需要将didAppear的值重置为“ false”才能使我的函数再次运行。将其设置回false,然后调用getRestaurants函数可以按预期更新我的查询。