SwiftUI - 如何从视图模型完成调用函数
SwiftUI - How to call function with completion from view model
我有一个获取邮政编码并将其传递给方法的表单。该方法查找城市和州以及 returns 作为元组的信息。该方法还有一个完成处理程序,以便在找到城市和州之前不会保存其余的表单数据。
我将函数移到了视图模型中以使事情井井有条,但现在我很困惑。首先,必须在函数 returns 之前调用完成处理程序,尽管这似乎与我想要的完全相反。我想获取城市和州的值,然后 我想通过处理程序保存它们。我困惑的后半部分是函数的调用。如果我分配一个像 cityState
这样的变量来从我的方法中接收元组,则保存发生在我无法到达的闭包中! (请参阅下面的设置视图代码。)
我可以将所有内容移回它工作正常的视图。但我正在尝试了解 MVVM 应该如何工作。
设置视图模型
func getCityStateFromPostalCode(zip: String, completion: @escaping () -> ()) -> (String, String) {
let geocoder = CLGeocoder()
var city = ""
var state = ""
geocoder.geocodeAddressString(zip) { (placemarks, error) in
if let placemark = placemarks?[0] {
if placemark.postalCode == zip {
city = placemark.locality!
state = placemark.administrativeArea!
}
}
completion()
}
return (city, state)
}
设置视图
let settingsVM = SettingsViewModel()
let cityState = settingsVM.getCityStateFromPostalCode(zip: companyZip) {
let newCompany = Company(context: self.moc)
newCompany.id = UUID()
newCompany.city = ?? //can't use cityState here.
newCompany.state = ?? // or here!
}
Return city
和 state
在完成处理程序中,而不是在 return
:
func getCityStateFromPostalCode(zip: String, completion: @escaping ((String, String)) -> ()) {
let geocoder = CLGeocoder()
var city = ""
var state = ""
geocoder.geocodeAddressString(zip) { (placemarks, error) in
if let placemark = placemarks?[0] {
if placemark.postalCode == zip {
city = placemark.locality!
state = placemark.administrativeArea!
}
}
completion((city, state))
}
}
那么你可以这样做:
settingsVM.getCityStateFromPostalCode(zip: companyZip) { city, state in
let newCompany = Company(context: self.moc)
newCompany.id = UUID()
newCompany.city = city
newCompany.state = state
}
@ObservedObject var model: someViewModel
var body: some View {
ScrollView {
ScrollViewReader { pageScroller in
ForEach(0..<100) { i in
Text("Example \(i)")
.frame(width: 200, height: 200)
.background(colors[i % colors.count])
.id(i)
}
//Magic here
.onReceive(model.scroller.objectWillChange) {
pageScroller.scrollTo(model.scroller.scrollToPageName, anchor: .top)
}
}
}
.frame(height: 350)
}
class someViewModel: ObservableObject {
//Magic here
@ObservedObject var scroller: scrollerHelper
init() {
someDelayedCall()
}
func someDelayedCall(){
scroller.scrollToPageName = 8
}
}
//Magic here
class scrollerHelper: ObservableObject {
@Published var scrollToPageName: Int = -1
}
scrollerHelper
的主要思想必须作为 ObservedObject 在模型内部。
在这种情况下,您可以将其用作从您的视图中强制执行代码 运行 的东西。
这里的神奇之处在于,View 不会像模型的任何 @Published
变量发生变化时那样完全刷新。此代码将 运行 与按下位于视图中的 Button 中的代码相同!太棒了,对吧?)
但如果您不需要这种行为,您可以使用使用 swiftUI 模型的标准方式。我在这里描述过:
我有一个获取邮政编码并将其传递给方法的表单。该方法查找城市和州以及 returns 作为元组的信息。该方法还有一个完成处理程序,以便在找到城市和州之前不会保存其余的表单数据。
我将函数移到了视图模型中以使事情井井有条,但现在我很困惑。首先,必须在函数 returns 之前调用完成处理程序,尽管这似乎与我想要的完全相反。我想获取城市和州的值,然后 我想通过处理程序保存它们。我困惑的后半部分是函数的调用。如果我分配一个像 cityState
这样的变量来从我的方法中接收元组,则保存发生在我无法到达的闭包中! (请参阅下面的设置视图代码。)
我可以将所有内容移回它工作正常的视图。但我正在尝试了解 MVVM 应该如何工作。
设置视图模型
func getCityStateFromPostalCode(zip: String, completion: @escaping () -> ()) -> (String, String) {
let geocoder = CLGeocoder()
var city = ""
var state = ""
geocoder.geocodeAddressString(zip) { (placemarks, error) in
if let placemark = placemarks?[0] {
if placemark.postalCode == zip {
city = placemark.locality!
state = placemark.administrativeArea!
}
}
completion()
}
return (city, state)
}
设置视图
let settingsVM = SettingsViewModel()
let cityState = settingsVM.getCityStateFromPostalCode(zip: companyZip) {
let newCompany = Company(context: self.moc)
newCompany.id = UUID()
newCompany.city = ?? //can't use cityState here.
newCompany.state = ?? // or here!
}
Return city
和 state
在完成处理程序中,而不是在 return
:
func getCityStateFromPostalCode(zip: String, completion: @escaping ((String, String)) -> ()) {
let geocoder = CLGeocoder()
var city = ""
var state = ""
geocoder.geocodeAddressString(zip) { (placemarks, error) in
if let placemark = placemarks?[0] {
if placemark.postalCode == zip {
city = placemark.locality!
state = placemark.administrativeArea!
}
}
completion((city, state))
}
}
那么你可以这样做:
settingsVM.getCityStateFromPostalCode(zip: companyZip) { city, state in
let newCompany = Company(context: self.moc)
newCompany.id = UUID()
newCompany.city = city
newCompany.state = state
}
@ObservedObject var model: someViewModel
var body: some View {
ScrollView {
ScrollViewReader { pageScroller in
ForEach(0..<100) { i in
Text("Example \(i)")
.frame(width: 200, height: 200)
.background(colors[i % colors.count])
.id(i)
}
//Magic here
.onReceive(model.scroller.objectWillChange) {
pageScroller.scrollTo(model.scroller.scrollToPageName, anchor: .top)
}
}
}
.frame(height: 350)
}
class someViewModel: ObservableObject {
//Magic here
@ObservedObject var scroller: scrollerHelper
init() {
someDelayedCall()
}
func someDelayedCall(){
scroller.scrollToPageName = 8
}
}
//Magic here
class scrollerHelper: ObservableObject {
@Published var scrollToPageName: Int = -1
}
scrollerHelper
的主要思想必须作为 ObservedObject 在模型内部。
在这种情况下,您可以将其用作从您的视图中强制执行代码 运行 的东西。
这里的神奇之处在于,View 不会像模型的任何 @Published
变量发生变化时那样完全刷新。此代码将 运行 与按下位于视图中的 Button 中的代码相同!太棒了,对吧?)
但如果您不需要这种行为,您可以使用使用 swiftUI 模型的标准方式。我在这里描述过: