SwiftUI:用户平移地图后触发视图更新
SwiftUI: Trigger view update after user pans the map
我是 Swift/SwiftUI 的新手,所以如果这是微不足道的,请原谅我。
在一个应用程序中,我试图检索用户的位置,并从维基百科获取附近的网站以显示。下面是一个简单的例子:
//
// ContentView.swift
// MRE
//
// Created by Philipp Maier on 2/25/22.
//
import SwiftUI
import CoreLocationUI
import MapKit
import Foundation
struct ContentView: View {
enum ViewState {
case waiting, fetching
}
@State private var viewState = ViewState.waiting
@StateObject private var viewModel = LocationViewModel()
// some values to initialize
@State var listEntries = [
Geosearch(pageid: 45348219,
title: "Addison Apartments",
lat: 35.21388888888889,
lon: -80.84472222222222,
dist: 363.7 ),
Geosearch(pageid: 35914731,
title: "Midtown Park (Charlotte, North Carolina)",
lat: 35.2108,
lon: -80.8363,
dist: 1034.5 )
]
var body: some View {
NavigationView{
VStack{
ZStack(alignment: .leading){
Map(coordinateRegion: $viewModel.mapRegion, showsUserLocation: true)
.frame(height: 300)
LocationButton(.currentLocation, action: {
viewModel.requestAllowLocationPermission()
viewState = .fetching
print("Button Press: Latitude: \(viewModel.mapRegion.center.latitude), Longitude: \(viewModel.mapRegion.center.longitude)")
})
}
HStack{
Text("Latitude: \(viewModel.mapRegion.center.latitude), Longitude: \(viewModel.mapRegion.center.longitude)")
}
switch viewState {
case .waiting:
Text("Waiting for your location")
Spacer()
case .fetching:
List{
ForEach(listEntries) {location in
NavigationLink(destination: Text("Target") ) {
HStack(alignment: .top){
Text(location.title)
}
}
.task{
await fetchNearbyLandmarks(lat: viewModel.mapRegion.center.latitude, lon: viewModel.mapRegion.center.longitude)
}
}
}
}
}
}
}
func fetchNearbyLandmarks(lat: Double, lon: Double) async {
let urlString = "https://en.wikipedia.org/w/api.php?action=query&list=geosearch&gscoord=\(lat)%7C\(lon)&gsradius=5000&gslimit=25&format=json"
print(urlString)
guard let url = URL(string: urlString) else {
print("Bad URL: \(urlString)")
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
let items = try JSONDecoder().decode(wikipediaResult.self, from: data)
listEntries = items.query.geosearch
print ("Loadingstate: Loaded")
} catch {
print ("Loadingstate: Failed")
}
}
}
final class LocationViewModel: NSObject, ObservableObject, CLLocationManagerDelegate {
@Published var mapRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 40, longitude: -80.5), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
let locationManager = CLLocationManager()
override init() {
super.init()
locationManager.delegate = self
}
func requestAllowLocationPermission() {
locationManager.requestLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let latestLocation = locations.first else {
print("Location Error #1")
return
}
DispatchQueue.main.async {
self.mapRegion = MKCoordinateRegion(center: latestLocation.coordinate, span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1))
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Location Error #2:")
print(error.localizedDescription)
}
}
struct wikipediaResult: Codable {
let batchcomplete: String
let query: Query
}
struct Query: Codable {
let geosearch: [ Geosearch ]
}
struct Geosearch: Codable, Identifiable {
var id: Int { pageid }
let pageid: Int
let title: String
let lat: Double
let lon: Double
let dist: Double
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
该应用程序的核心功能有效,一旦用户按下“位置”按钮,下面的维基百科页面列表就会更新。我还添加了文本字段以在用户四处移动时跟踪地图的中心。
这是我的问题:如果我平移地图,下面的位置列表不会更新。然而,一旦我点击 link 显示页面,并点击“后退”按钮,更新的列表就会“正确”构建,即使用新坐标。
我确定我忽略了一些简单的事情,但我如何才能跟踪用户的平移并“实时”动态调整下方的列表?
谢谢,菲利普
对于 Swift/SwiftUI 的新手来说,这很酷!
您想在地图坐标更改时重新获取。所以你可以使用 .onChange
, e.g.on 地图的 ZStack
:
.onChange(of: viewModel.mapRegion.center.latitude) { _ in
Task {
await fetchNearbyLandmarks(lat: viewModel.mapRegion.center.latitude, lon: viewModel.mapRegion.center.longitude)
}
}
通常这可行,但它可能获取得太频繁(每次更改纬度时)。所以您可能想要添加某种延迟,或者尝试弄清楚拖动何时结束。
我是 Swift/SwiftUI 的新手,所以如果这是微不足道的,请原谅我。
在一个应用程序中,我试图检索用户的位置,并从维基百科获取附近的网站以显示。下面是一个简单的例子:
//
// ContentView.swift
// MRE
//
// Created by Philipp Maier on 2/25/22.
//
import SwiftUI
import CoreLocationUI
import MapKit
import Foundation
struct ContentView: View {
enum ViewState {
case waiting, fetching
}
@State private var viewState = ViewState.waiting
@StateObject private var viewModel = LocationViewModel()
// some values to initialize
@State var listEntries = [
Geosearch(pageid: 45348219,
title: "Addison Apartments",
lat: 35.21388888888889,
lon: -80.84472222222222,
dist: 363.7 ),
Geosearch(pageid: 35914731,
title: "Midtown Park (Charlotte, North Carolina)",
lat: 35.2108,
lon: -80.8363,
dist: 1034.5 )
]
var body: some View {
NavigationView{
VStack{
ZStack(alignment: .leading){
Map(coordinateRegion: $viewModel.mapRegion, showsUserLocation: true)
.frame(height: 300)
LocationButton(.currentLocation, action: {
viewModel.requestAllowLocationPermission()
viewState = .fetching
print("Button Press: Latitude: \(viewModel.mapRegion.center.latitude), Longitude: \(viewModel.mapRegion.center.longitude)")
})
}
HStack{
Text("Latitude: \(viewModel.mapRegion.center.latitude), Longitude: \(viewModel.mapRegion.center.longitude)")
}
switch viewState {
case .waiting:
Text("Waiting for your location")
Spacer()
case .fetching:
List{
ForEach(listEntries) {location in
NavigationLink(destination: Text("Target") ) {
HStack(alignment: .top){
Text(location.title)
}
}
.task{
await fetchNearbyLandmarks(lat: viewModel.mapRegion.center.latitude, lon: viewModel.mapRegion.center.longitude)
}
}
}
}
}
}
}
func fetchNearbyLandmarks(lat: Double, lon: Double) async {
let urlString = "https://en.wikipedia.org/w/api.php?action=query&list=geosearch&gscoord=\(lat)%7C\(lon)&gsradius=5000&gslimit=25&format=json"
print(urlString)
guard let url = URL(string: urlString) else {
print("Bad URL: \(urlString)")
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
let items = try JSONDecoder().decode(wikipediaResult.self, from: data)
listEntries = items.query.geosearch
print ("Loadingstate: Loaded")
} catch {
print ("Loadingstate: Failed")
}
}
}
final class LocationViewModel: NSObject, ObservableObject, CLLocationManagerDelegate {
@Published var mapRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 40, longitude: -80.5), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
let locationManager = CLLocationManager()
override init() {
super.init()
locationManager.delegate = self
}
func requestAllowLocationPermission() {
locationManager.requestLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let latestLocation = locations.first else {
print("Location Error #1")
return
}
DispatchQueue.main.async {
self.mapRegion = MKCoordinateRegion(center: latestLocation.coordinate, span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1))
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Location Error #2:")
print(error.localizedDescription)
}
}
struct wikipediaResult: Codable {
let batchcomplete: String
let query: Query
}
struct Query: Codable {
let geosearch: [ Geosearch ]
}
struct Geosearch: Codable, Identifiable {
var id: Int { pageid }
let pageid: Int
let title: String
let lat: Double
let lon: Double
let dist: Double
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
该应用程序的核心功能有效,一旦用户按下“位置”按钮,下面的维基百科页面列表就会更新。我还添加了文本字段以在用户四处移动时跟踪地图的中心。
这是我的问题:如果我平移地图,下面的位置列表不会更新。然而,一旦我点击 link 显示页面,并点击“后退”按钮,更新的列表就会“正确”构建,即使用新坐标。
我确定我忽略了一些简单的事情,但我如何才能跟踪用户的平移并“实时”动态调整下方的列表?
谢谢,菲利普
对于 Swift/SwiftUI 的新手来说,这很酷!
您想在地图坐标更改时重新获取。所以你可以使用 .onChange
, e.g.on 地图的 ZStack
:
.onChange(of: viewModel.mapRegion.center.latitude) { _ in
Task {
await fetchNearbyLandmarks(lat: viewModel.mapRegion.center.latitude, lon: viewModel.mapRegion.center.longitude)
}
}
通常这可行,但它可能获取得太频繁(每次更改纬度时)。所以您可能想要添加某种延迟,或者尝试弄清楚拖动何时结束。