如何用 ObservableObject 更新 UIViewRepresentable
How to update UIViewRepresentable with ObservableObject
我正在尝试学习结合 SwiftUI,我正在努力如何使用 ObservableObject
(以前是 BindableObject
)更新我的视图(从 UIKit)。问题是,很明显,一旦 @Published
对象发送它已更改的通知,方法 updateUIView
将不会触发。
class DataSource: ObservableObject {
@Published var locationCoordinates = [CLLocationCoordinate2D]()
var value: Int = 0
init() {
Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { timer in
self.value += 1
self.locationCoordinates.append(CLLocationCoordinate2D(latitude: 52, longitude: 16+0.1*Double(self.value)))
}
}
}
struct MyView: UIViewRepresentable {
@ObservedObject var dataSource = DataSource()
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}
func updateUIView(_ view: MKMapView, context: Context) {
let newestCoordinate = dataSource.locationCoordinates.last ?? CLLocationCoordinate2D(latitude: 52, longitude: 16)
let annotation = MKPointAnnotation()
annotation.coordinate = newestCoordinate
annotation.title = "Test #\(dataSource.value)"
view.addAnnotation(annotation)
}
}
如何以这种方式将 locationCoordinates
数组绑定到视图,实际上每次刷新时都会添加一个新点?
为了确保您的 ObservedObject
不会被多次创建(您只想要一份副本),您可以将它放在 UIViewRepresentable
之外:
import SwiftUI
import MapKit
struct ContentView: View {
@ObservedObject var dataSource = DataSource()
var body: some View {
MyView(locationCoordinates: dataSource.locationCoordinates, value: dataSource.value)
}
}
class DataSource: ObservableObject {
@Published var locationCoordinates = [CLLocationCoordinate2D]()
var value: Int = 0
init() {
Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { timer in
self.value += 1
self.locationCoordinates.append(CLLocationCoordinate2D(latitude: 52, longitude: 16+0.1*Double(self.value)))
}
}
}
struct MyView: UIViewRepresentable {
var locationCoordinates: [CLLocationCoordinate2D]
var value: Int
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}
func updateUIView(_ view: MKMapView, context: Context) {
print("I am being called!")
let newestCoordinate = locationCoordinates.last ?? CLLocationCoordinate2D(latitude: 52, longitude: 16)
let annotation = MKPointAnnotation()
annotation.coordinate = newestCoordinate
annotation.title = "Test #\(value)"
view.addAnnotation(annotation)
}
}
这个解决方案对我有用,但使用了 EnvironmentObject https://gist.github.com/svanimpe/152e6539cd371a9ae0cfee42b374d7c4
我将为任何使用组合表示的 UI/NS 视图提供通用解决方案。我的方法有性能优势。
- 创建了一个 Observable 对象并用
@Published 包装器
- 通过视图中的updateView方法注入Observed对象
可使用您将在步骤 3 中创建的方法表示
- 将视图模型作为参数对所需视图进行子类化。
创建一个 addViewModel 方法并使用
合并运营商/订阅者并将它们添加到可取消。
注意 - 适用于环境对象。
struct swiftUIView : View {
@EnvironmentObject var env : yourViewModel
...
...
UIViewRep(wm : env)
}
struct UIViewRep : UIViewRepresentable {
var wm : yourViewModel
func makeUIView {
let yv = yourView()
yv.addViewModel(wm)
return yv
}}
class yourView : UIView {
var viewModel : yourViewModel?
var cancellable = Set<AnyCancellable>()
...
...
func addViewModel( _ wm : yourViewModel) {
self.viewModel = wm
self.viewModel?.desiredProperty
.sink(receiveValue: { [unowned self] w in
print("Make changes with ", w)
}).store(in: &cancellable)
}
}
我正在尝试学习结合 SwiftUI,我正在努力如何使用 ObservableObject
(以前是 BindableObject
)更新我的视图(从 UIKit)。问题是,很明显,一旦 @Published
对象发送它已更改的通知,方法 updateUIView
将不会触发。
class DataSource: ObservableObject {
@Published var locationCoordinates = [CLLocationCoordinate2D]()
var value: Int = 0
init() {
Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { timer in
self.value += 1
self.locationCoordinates.append(CLLocationCoordinate2D(latitude: 52, longitude: 16+0.1*Double(self.value)))
}
}
}
struct MyView: UIViewRepresentable {
@ObservedObject var dataSource = DataSource()
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}
func updateUIView(_ view: MKMapView, context: Context) {
let newestCoordinate = dataSource.locationCoordinates.last ?? CLLocationCoordinate2D(latitude: 52, longitude: 16)
let annotation = MKPointAnnotation()
annotation.coordinate = newestCoordinate
annotation.title = "Test #\(dataSource.value)"
view.addAnnotation(annotation)
}
}
如何以这种方式将 locationCoordinates
数组绑定到视图,实际上每次刷新时都会添加一个新点?
为了确保您的 ObservedObject
不会被多次创建(您只想要一份副本),您可以将它放在 UIViewRepresentable
之外:
import SwiftUI
import MapKit
struct ContentView: View {
@ObservedObject var dataSource = DataSource()
var body: some View {
MyView(locationCoordinates: dataSource.locationCoordinates, value: dataSource.value)
}
}
class DataSource: ObservableObject {
@Published var locationCoordinates = [CLLocationCoordinate2D]()
var value: Int = 0
init() {
Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { timer in
self.value += 1
self.locationCoordinates.append(CLLocationCoordinate2D(latitude: 52, longitude: 16+0.1*Double(self.value)))
}
}
}
struct MyView: UIViewRepresentable {
var locationCoordinates: [CLLocationCoordinate2D]
var value: Int
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}
func updateUIView(_ view: MKMapView, context: Context) {
print("I am being called!")
let newestCoordinate = locationCoordinates.last ?? CLLocationCoordinate2D(latitude: 52, longitude: 16)
let annotation = MKPointAnnotation()
annotation.coordinate = newestCoordinate
annotation.title = "Test #\(value)"
view.addAnnotation(annotation)
}
}
这个解决方案对我有用,但使用了 EnvironmentObject https://gist.github.com/svanimpe/152e6539cd371a9ae0cfee42b374d7c4
我将为任何使用组合表示的 UI/NS 视图提供通用解决方案。我的方法有性能优势。
- 创建了一个 Observable 对象并用 @Published 包装器
- 通过视图中的updateView方法注入Observed对象 可使用您将在步骤 3 中创建的方法表示
- 将视图模型作为参数对所需视图进行子类化。 创建一个 addViewModel 方法并使用 合并运营商/订阅者并将它们添加到可取消。
注意 - 适用于环境对象。
struct swiftUIView : View {
@EnvironmentObject var env : yourViewModel
...
...
UIViewRep(wm : env)
}
struct UIViewRep : UIViewRepresentable {
var wm : yourViewModel
func makeUIView {
let yv = yourView()
yv.addViewModel(wm)
return yv
}}
class yourView : UIView {
var viewModel : yourViewModel?
var cancellable = Set<AnyCancellable>()
...
...
func addViewModel( _ wm : yourViewModel) {
self.viewModel = wm
self.viewModel?.desiredProperty
.sink(receiveValue: { [unowned self] w in
print("Make changes with ", w)
}).store(in: &cancellable)
}
}