标签: combine

SwiftUI 和 MVVM - 模型和视图模型之间的通信

我一直在试验使用的 MVVM 模型,SwiftUI但有些东西我还不太明白。

SwiftUI使用@ObservableObject/@ObservedObject检测视图模型中触发body属性重新计算以更新视图的更改。

在 MVVM 模型中,这是视图和视图模型之间的通信。我不太明白的是模型和视图模型是如何通信的。

当模型发生变化时,视图模型应该如何知道这一点?我想到了手动使用新Combine框架在模型内部创建视图模型可以订阅的发布者。

但是,我创建了一个简单的示例,我认为该示例使这种方法非常乏味。有一个模型称为Game保存Game.Character对象数组。一个角色有一个strength可以改变的属性。

那么如果一个视图模型改变strength了一个角色的那个属性呢?为了检测这种变化,模型必须订阅游戏中的每一个角色(可能还有很多其他的东西)。是不是有点过分了?或者有很多发布者和订阅者是正常的吗?

还是我的示例没有正确遵循 MVVM?我的视图模型不应该将实际模型game作为属性吗?如果是这样,什么是更好的方法?

// My Model
class Game {

  class Character {
    let name: String
    var strength: Int
    init(name: String, strength: Int) {
      self.name = name
      self.strength = strength
    }
  }

  var characters: [Character]

  init(characters: [Character]) {
    self.characters = characters
  }
}

// ...

// My view model
class ViewModel: …
Run Code Online (Sandbox Code Playgroud)

swift swiftui combine

22
推荐指数
3
解决办法
2万
查看次数

在 Swift 中为 @Binding var 设置 didSet

通常我们可以didSet在 swift中使用来监视变量的更新。但它不适用于@Binding变量。例如,我有以下代码:

@Binding var text {
   didSet {
       ......
   }
}
Run Code Online (Sandbox Code Playgroud)

但是didSet从来没有被调用过。知道吗?谢谢。

swift swiftui combine

21
推荐指数
2
解决办法
5871
查看次数

如何在 SwiftUI 中使用 onReceive 从 ObservedObject 获取数据?

在我的 SwiftUI 应用程序中,每次值更改时我都需要从 ObservedObject 获取数据。我知道我们可以用 .onReceive 做到这一点?我不太了解 Apple 的文档。我不知道我怎么能做到这一点。

我的代码:

import SwiftUI
import CoreLocation

struct Compass: View {
  
  @StateObject var location = LocationManager()
  @State private var angle: CGFloat = 0
  
  var body: some View {
    VStack {
      Image("arrow")
        .resizable()
        .aspectRatio(contentMode: .fit)
        .frame(width: 300, height: 300)
        .modifier(RotationEffect(angle: -CGFloat(self.angle.degreesToRadians)))
        .onReceive(location, perform: {
          withAnimation(.easeInOut(duration: 1.0)) {
            self.angle = self.location.heading
          }
        })
      
      Text(String(self.location.heading.degreesToRadians))
        .font(.system(size: 20))
        .fontWeight(.light)
        .padding(.top, 15)
    }
  }
}

struct RotationEffect: GeometryEffect {
  var angle: CGFloat

  var animatableData: CGFloat {
    get { …
Run Code Online (Sandbox Code Playgroud)

swift swiftui combine

21
推荐指数
3
解决办法
2万
查看次数

UserDefaults 绑定与 SwiftUI 中的切换

我试图找出构建绑定到 UserDefaults 的简单设置屏幕的最佳方法。

基本上,我有一个 Toggle 并且我想要:

  • 每当此 Toggle 更改时要保存的 UserDefault 值(UserDefault 应该是事实来源)
  • Toggle 始终显示 UserDefault 的值

带有切换功能的设置屏幕

我已经观看了许多 SwiftUI WWDC 会议,但我仍然不确定应该如何使用 Combine 和 SwiftUI 中提供的不同工具来设置所有内容。我目前的想法是我应该使用 BindableObject 以便我可以使用 hat 来封装许多不同的设置。

我想我很接近,因为它几乎按预期工作,但行为不一致。

当我在设备上构建和运行它时,我打开它并打开 Toggle,然后如果我向上和向下滚动视图一点开关切换回关闭(好像它实际上没有保存 UserDefaults 中的值)。

但是,如果我打开开关,离开应用程序,然后再回来,它仍然打开,就像它记住了设置一样。

有什么建议?我发布此内容是希望它能帮助其他不熟悉 SwiftUI 和 Combine 的人,因为我找不到关于此主题的任何类似问题。

import SwiftUI
import Combine

struct ContentView : View {

    @ObjectBinding var settingsStore = SettingsStore()

    var body: some View {
        NavigationView {
            Form {
                Toggle(isOn: $settingsStore.settingActivated) {
                    Text("Setting Activated")
                }
            }
        }.navigationBarTitle(Text("Settings"))
    }
}

class SettingsStore: BindableObject {

    var didChange …
Run Code Online (Sandbox Code Playgroud)

ios swiftui combine

19
推荐指数
3
解决办法
8533
查看次数

SwiftUI 绑定默认值(参数标签 '(wrappedValue:)' 不匹配任何可用的重载)

在 Swift 中,您可以在初始化时覆盖的结构上定义默认值:

struct myStruct {
    var a: Int = 1
}
var instance1 = myStruct() // instance1.a -> 1 
var instance2 = myStruct(a: 10) // instance2.a -> 10
Run Code Online (Sandbox Code Playgroud)

但是,当我尝试将其应用于 SwiftUI 视图中的 Bindings 时,出现错误:

struct MyView: View {
    @Binding var a: Bool = Binding.constant(true)
    var body: some View {
        Text("MyView")
    }
}
Run Code Online (Sandbox Code Playgroud)
Argument labels '(wrappedValue:)' do not match any available overloads
Run Code Online (Sandbox Code Playgroud)

我想创建一个默认使用常量布尔值但可以被“真实”绑定覆盖的视图:

Argument labels '(wrappedValue:)' do not match any available overloads
Run Code Online (Sandbox Code Playgroud)

是否可以在 SwiftUI 中为绑定定义这样的默认值?

swift swiftui combine

18
推荐指数
1
解决办法
4676
查看次数

无法将“Published<Bool>.Publisher”类型的值转换为预期的参数类型“Binding<Bool>”

尝试编译以下代码时:

class LoginViewModel: ObservableObject, Identifiable {
    @Published var mailAdress: String = ""
    @Published var password: String = ""
    @Published var showRegister = false
    @Published var showPasswordReset = false

    private let applicationStore: ApplicationStore

    init(applicationStore: ApplicationStore) {
        self.applicationStore = applicationStore
    }

    var passwordResetView: some View {
        PasswordResetView(isPresented: $showPasswordReset) // This is where the error happens
    }
}
Run Code Online (Sandbox Code Playgroud)

其中 PasswordResetView 看起来像这样:

struct PasswordResetView: View {
    @Binding var isPresented: Bool
    @State var mailAddress: String = ""
    
    var body: some View {
            EmptyView()
        }
    } …
Run Code Online (Sandbox Code Playgroud)

ios swift swiftui combine

17
推荐指数
4
解决办法
8509
查看次数

如何定义协议以包含带有@Published 属性包装器的属性

在遵循当前 SwiftUI 语法使用 @Published 属性包装器时,似乎很难定义包含带有 @Published 的属性的协议,或者我肯定需要帮助:)

当我在 View 和它的 ViewModel 之间实现依赖注入时,我需要定义一个 ViewModelProtocol 以便注入模拟数据以轻松预览。

这是我第一次尝试的,

protocol PersonViewModelProtocol {
    @Published var person: Person
}
Run Code Online (Sandbox Code Playgroud)

我得到“在协议中声明的属性‘人’不能有包装器”。

然后我尝试了这个,

protocol PersonViewModelProtocol {
    var $person: Published
}
Run Code Online (Sandbox Code Playgroud)

显然没有用,因为 '$' 是保留的。

我希望有一种方法可以在 View 和它的 ViewModel 之间建立一个协议,并利用优雅的 @Published 语法。非常感谢。

protocols swiftui combine

16
推荐指数
4
解决办法
4377
查看次数

错误:初始化程序 'init(_:)' 要求 'Binding<String>' 符合 'StringProtocol'

我收到上述错误,无法弄清楚如何解决它。我有一个包含布尔值的对象数组,需要为每个布尔值显示一个切换。

下面是代码。

class Item: Identifiable {
    var id: String
    var label: String
    var isOn: Bool
}

class Service: ObservableObject {
    var didChange = PassthroughSubject<Void, Never>()

    var items: [Item] {
        didSet {
            didChange.send(())
        }
    }
}

struct MyView: View {
    @ObservedObject var service: Service

    var body: some View {
        List {
            ForEach(service.items, id: \.self) { (item: Binding<Item>) in
                Section(header: Text(item.label)) {  // Error: Initializer 'init(_:)' requires that 'Binding<String>' conform to 'StringProtocol'
                    Toggle(isOn: item.isOn) {
                        Text("isOn")
                    }
                }
            }
        }
        .listStyle(GroupedListStyle())
    } …
Run Code Online (Sandbox Code Playgroud)

swift swiftui combine

16
推荐指数
1
解决办法
2万
查看次数

CurrentValueSubject 和 @Published 之间的区别

所以我正在研究结合,这个问题就出现了。

使用CurrentValueSubject(并使用设置其值currentValueSubject.value)或使用 a@Published var并使用 a访问其发布者之间有什么真正的区别$吗?我的意思是我知道一个返回 aSubject而不是 a Publisher,但我能找到的唯一真正的区别是它CurrentValueSubject更有用,因为你可以在协议上声明它。

我真的不明白@Published如果我们可以使用它怎么会有用PassthroughSubject,我在这里遗漏了什么吗? 请注意,这是使用 UIKit,它可能对 SwiftUI 有其他用途。

谢谢你。

uikit ios swift combine

16
推荐指数
4
解决办法
4834
查看次数

组合框架:如何在继续之前异步处理数组的每个元素

我在使用 iOS Combine 框架时遇到了一些心理障碍。

我正在将一些代码从“手动”从远程 API 获取转换为使用组合。基本上,API 是 SQL 和 REST(实际上是 Salesforce,但这与问题无关)。代码用来做的是调用一个接受完成处理程序的 REST 查询方法。我正在做的是用结合未来到处替换它。到现在为止还挺好。

当以下场景发生时,问题就出现了(并且经常发生):

  1. 我们执行 REST 查询并返回一组“对象”。

  2. 但是这些“对象”并没有完全填充。它们中的每一个都需要来自某个相关对象的附加数据。因此,对于每个“对象”,我们使用来自该“对象”的信息进行另一个 REST 查询,从而为我们提供另一个“对象”数组。

  3. 这可能允许也可能不允许我们完成第一个“对象”的填充——否则,我们可能必须使用来自第二个“对象”中的每个“对象”的信息进行另一个REST 查询,依此类推。

结果是很多这样结构的代码(这是伪代码):

func fetchObjects(completion: @escaping ([Object] -> Void) {
    let restQuery = ...
    RESTClient.performQuery(restQuery) { results in
        let partialObjects = results.map { ... }
        let group = DispatchGroup()
        for partialObject in partialObjects {
            let restQuery = ... // something based on partialObject
            group.enter()
            RESTClient.performQuery(restQuery) { results in
                group.leave()
                let partialObjects2 = results.map …
Run Code Online (Sandbox Code Playgroud)

ios swift combine

16
推荐指数
2
解决办法
3593
查看次数

标签 统计

combine ×10

swift ×8

swiftui ×8

ios ×4

protocols ×1

uikit ×1