如何在SwiftUI的视图中调用方法
刚刚开始使用SwiftUI 我在如何在SwiftUI的视图中调用方法,swiftui,google-maps-sdk-ios,Swiftui,Google Maps Sdk Ios,刚刚开始使用SwiftUI 我在ContentView中有一个 使用CLLocationManagerI捕获AppDelegate或SceneDelegate类中的事件,方法是使用CLLocationManagerDelegate扩展它们 如何从AppDelegate或SceneDelegate调用GoogleMapsView中的方法 在本例中,当位置更改事件通过CLLocationManagerDelegate发送到AppDelegate实例时,我想调用.animate方法,但问题实际上更一般
ContentView中有一个
使用CLLocationManager
I捕获AppDelegate
或SceneDelegate
类中的事件,方法是使用CLLocationManagerDelegate
扩展它们
如何从AppDelegate
或SceneDelegate
调用GoogleMapsView
中的方法
在本例中,当位置更改事件通过CLLocationManagerDelegate
发送到AppDelegate
实例时,我想调用.animate
方法,但问题实际上更一般。而不是直接从外部调用视图
方法,您应该稍微修改一下逻辑,只需在某个地方更改某种状态
,然后让视图
自行更新。看看这个算法:
经典(也是最糟糕的)方式:
位置改变
在应用程序委托中调用的委托方法(更好地重构到其他位置)
App delegate直接在视图
上调用方法(您应该将对该视图的引用一直传递到App delegate)
虽然上面的算法是您最初寻找的,但它不是最好的方法,我根本不推荐它!但是它将工作我制作并实现了CLLocationManager和MKMapView,它几乎与“地图”相同,希望它能帮助您:
简短回答:声明一个@Binding var foo:Any
每次foo更改时,您都可以在GoogleMapView中进行更改,在本例中,foo是您的位置,因此您可以在每次更新foo时调用animate
长答案:
struct MapView: UIViewRepresentable {
@Binding var location: CLLocation // Create a @Binding variable that keeps the location where I want to place the view, every time it changes updateUIView will be called
private let zoomMeters = 400
func makeUIView(context: UIViewRepresentableContext<MapView>) -> MKMapView {
let mapView = MKMapView(frame: UIScreen.main.bounds)
return mapView
}
func updateUIView(_ mapView: MKMapView, context: Context) {
//When location changes, updateUIView is called, so here I move the map:
let region = MKCoordinateRegion(center: location.coordinate,
latitudinalMeters: CLLocationDistance(exactly: zoomMeters)!,
longitudinalMeters: CLLocationDistance(exactly: zoomMeters)!)
mapView.setRegion(mapView.regionThatFits(region), animated: true)
}
}
struct ContentView: View {
@ObservedObject var viewModel: ContentViewModel
var body: some View {
VStack {
MapView(location: self.$viewModel.location)
}
}
}
首先,我创建了一个符合UIViewRepresentable协议的Mapview,正如您所做的,但添加了@Binding变量,这是我的“触发器”
地图视图:
struct MapView: UIViewRepresentable {
@Binding var location: CLLocation // Create a @Binding variable that keeps the location where I want to place the view, every time it changes updateUIView will be called
private let zoomMeters = 400
func makeUIView(context: UIViewRepresentableContext<MapView>) -> MKMapView {
let mapView = MKMapView(frame: UIScreen.main.bounds)
return mapView
}
func updateUIView(_ mapView: MKMapView, context: Context) {
//When location changes, updateUIView is called, so here I move the map:
let region = MKCoordinateRegion(center: location.coordinate,
latitudinalMeters: CLLocationDistance(exactly: zoomMeters)!,
longitudinalMeters: CLLocationDistance(exactly: zoomMeters)!)
mapView.setRegion(mapView.regionThatFits(region), animated: true)
}
}
struct ContentView: View {
@ObservedObject var viewModel: ContentViewModel
var body: some View {
VStack {
MapView(location: self.$viewModel.location)
}
}
}
在我的ViewModel中,我使用代理处理位置更改,下面是注释中包含更多详细信息的代码:
class ContentViewModel: ObservableObject {
//location is a Published value, so the view is updated every time location changes
@Published var location: CLLocation = CLLocation.init()
//LocationWorker will take care of CLLocationManager...
let locationWorker: LocationWorker = LocationWorker()
init() {
locationWorker.delegate = self
}
}
extension ContentViewModel: LocationWorkerDelegate {
func locationChanged(lastLocation: CLLocation?) {
//Location changed, I change the value of self.location, it is a @Published value so it will refresh the @Binding variable inside MapView and call MapView.updateUIView
self.location = CLLocation.init(latitude: lastLocation!.coordinate.latitude, longitude: lastLocation!.coordinate.latitude)
}
}
最后是负责CLLocationManager()的LocationWorker:
class LocationWorker:NSObject,ObservableObject{
私有let locationManager=CLLocationManager()
var代表:位置WorkerDelegate?
let objectWillChange=PassthroughSubject()
@发布的变量位置状态:CLAuthorizationStatus{
意志{
objectWillChange.send()
}
}
@发布的var lastLocation:CLLocation{
意志{
objectWillChange.send()
}
}
重写init(){
super.init()
self.locationManager.delegate=self
//...
}
}
协议位置WorkerDelegate{
功能位置已更改(最后位置:CLLocation?)
}
扩展位置工作人员:CLLocationManagerDelegate{
func locationManager(manager:CLLocationManager,didUpdateLocations位置:[CLLocation]){
guard let location=locations.last else{return}
self.lastLocation=位置
//位置更改时:我使用我的代理->
如果委托!=nil{
委派!.locationChanged(lastLocation:lastLocation)
}
}
}
对于设置位置更改动画,位置数据是否处于视图中的@状态
或@ObservedObject
变量中?这里的想法是,对此状态的更改将自动更新视图,因此视图的更改可以使用.animation()
ViewModifier。将在GoogleMapsView对象(实际上是其内部地图视图)上调用.animate方法,以显示以不同位置为中心的地图。这并不是一个我可以控制的动画,因为它是由MapView本身提供的。更一般的问题是如何从AppDelegate或SceneDelegate调用视图方法。似乎我必须查看新SwiftUI范型的Combine元素。我没有机会查看GoogleMapsView,但是作为一个UIViewRepresentable
我想知道它在这方面的可扩展性是什么。通过提供的链接不清楚您将调用什么、在哪里以及在什么条件下调用。在伪代码的注释中显示你想要实现什么。太棒了,谢谢你,这正是我想要的。我确实明白,在这个新的范例中,视图不应该通过获取引用的方式进行外部操纵,但在尝试使用Combine连接所有内容时迷失了方向。我知道这个线程已经存在几个月了,但我希望我可以问一个与从ViewModel访问视图相关的问题。使用UIViewRepresentable表示地图视图时,ViewModel访问所需地图视图对象以添加覆盖等的功能将消失。。。。我如何告诉uiview代表viewmodel添加覆盖或直接调用它来访问mapView对象?@Tim您可以传递另一个@Binding
变量来控制覆盖可能@Binding var:showOverlay=false
,每次更新时,都会调用updateUIView,您决定是否显示覆盖。zgluis,感谢您的回复。我通过将我在viewmodel中创建的多段线设置为@Published,然后在updateUI中访问覆盖,在那里我可以访问mapview。谢谢我确实试着去“迅捷”,但在路上迷路了。@zgluis的例子说明了这一点。非常感谢。