SwiftUI - 如何通过视图模型完成调用函数

Posted

技术标签:

【中文标题】SwiftUI - 如何通过视图模型完成调用函数【英文标题】:SwiftUI - How to call function with completion from view model 【发布时间】:2020-09-01 23:45:27 【问题描述】:

我有一个获取邮政编码并将其传递给方法的表单。该方法查找城市和州,并将信息作为元组返回。该方法还有一个完成处理程序,以便在找到城市和州之前不会保存其余的表单数据。

我将函数移动到视图模型以保持秩序,但现在我很困惑。首先,必须在函数返回之前调用完成处理程序,即使这看起来与我想要的完全相反。我想获取城市和州的值,然后我想通过处理程序保存它们。我困惑的后半部分是函数的调用。如果我分配一个像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!

【问题讨论】:

【参考方案1】:

在完成处理程序中返回citystate,而不是在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

【讨论】:

【参考方案2】:
@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 变量发生变化一样完全刷新。此代码将以与视图中的按钮按下相同的方式运行!太棒了,对吧?)

但如果您不需要这种行为,您可以使用标准的 swiftUI 模型使用方式。我在这里描述过:

https://***.com/a/62919526/4423545

【讨论】:

以上是关于SwiftUI - 如何通过视图模型完成调用函数的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI - 如何在我的视图中调用函数?

如何将数据从父模型类传递到 SwiftUI 中的子视图

SwiftUI:如何更改子视图?

如何将我的 SwiftUI 视图的 @State 交换为我的视图模型 @Published 变量?

SwiftUI:如何在搜索栏的文本更改时触发 api 调用以检索数据源

如何将字符串数组从视图模型传递到 swiftUI 中播放歌曲