繁体   English   中英

如何在SwiftUI视图上使用Combine

[英]How to use Combine on a SwiftUI View

这个问题与此有关: 如何使用SwiftUI和Combine观察TextField的值?

但是我要问的是比较笼统的。 这是我的代码:

struct MyPropertyStruct {
    var text: String
}

class TestModel : ObservableObject {
    @Published var myproperty = MyPropertyStruct(text: "initialText")

    func saveTextToFile(text: String) {
        print("this function saves text to file")
    }
}

struct ContentView: View {
    @ObservedObject var testModel = TestModel()
    var body: some View {
        TextField("", text: $testModel.myproperty.text)
    }
}

场景:随着用户在文本字段中键入内容,应该调用saveTextToFile函数。 由于这是保存到文件,因此应放慢/限制它。

所以我的问题是:

  1. 将合并操作放在下面的代码中的适当位置在哪里。
  2. 我要完成什么组合代码: (A)字符串不能包含空格。 (B)字符串必须为5个字符长。 (C)弦必须去抖动/掉线

我想在这里使用响应作为以下常规模式: 我们应该如何处理SwiftUI应用程序(而非UIKit应用程序)中的组合内容。

您应该在ViewModel 您的视图模型是TestModel类(建议您在TestViewModel重命名)。 在这里应该将逻辑放在模型和视图之间。 ViewModel应该准备好模型以便进行可视化。 这是放置合并逻辑的正确位置(当然,如果它与视图有关)。

现在,我们可以使用您的特定示例来实际创建一个示例。 老实说,根据您真正想要实现的目标,有几种稍微不同的解决方案。 但是现在,我将尝试尽可能通用一些,然后您可以告诉我解决方案是否完善或需要一些改进:

struct MyPropertyStruct {
    var text: String
}

class TestViewModel : ObservableObject {
    @Published var myproperty = MyPropertyStruct(text: "initialText")
    private var canc: AnyCancellable!

    init() {
        canc = $myproperty.debounce(for: 0.5, scheduler: DispatchQueue.main).sink { [unowned self] newText in
            let strToSave = self.cleanText(text: newText.text)
            if strToSave != newText.text {
                //a cleaning has actually happened, so we must change our text to reflect the cleaning
                self.myproperty.text = strToSave
            }
            self.saveTextToFile(text: strToSave)
        }
    }

    deinit {
        canc.cancel()
    }

    private func cleanText(text: String) -> String {
        //remove all the spaces
        let resultStr = String(text.unicodeScalars.filter {
            $0 != " "
        })

        //take up to 5 characters
        return String(resultStr.prefix(5))
    }

    private func saveTextToFile(text: String) {
        print("text saved")
    }
}

struct ContentView: View {
    @ObservedObject var testModel = TestViewModel()

    var body: some View {
        TextField("", text: $testModel.myproperty.text)
    }
}

您应该将您自己的subscriber附加到TextField publisher并使用debounce发布者来延迟字符串的清理和对save方法的调用。 根据文档:

反跳(用于:调度:选择:)

当您要等待上游发布者传递事件的暂停时,请使用此运算符。 例如, 发布者上从文本字段 调用反跳操作 以仅在用户暂停或停止键入时接收元素 当他们再次开始键入时,防跳动将保持事件传递直到下一个暂停。

当用户停止键入时,防反弹发布者将等待指定的时间(在我的示例中为0.5秒以上),然后使用新值调用其订阅者。

以上的延迟解决方案的字符串节约 TextField更新。 这意味着在更新发生之前,用户会看到原始字符串(带有空格且可能超过5个字符的字符串)一段时间。 这就是为什么在这个答案的开头,我说根据需要有几种不同的解决方案。 如果确实确实要延迟字符串的保存,但是我们希望禁止用户输入空格字符或长度超过5个字符的字符串,则可以使用两个订阅者(我将仅发布更改的代码,即TestViewModel类):

class TestViewModel : ObservableObject {
    @Published var myproperty = MyPropertyStruct(text: "initialText")
    private var saveCanc: AnyCancellable!
    private var updateCanc: AnyCancellable!

    init() {
        saveCanc = $myproperty.debounce(for: 0.5, scheduler: DispatchQueue.main)
            .map { [unowned self] in self.cleanText(text: $0.text) }
            .sink { [unowned self] newText in
            self.saveTextToFile(text: self.cleanText(text: newText))
        }

        updateCanc = $myproperty.sink { [unowned self] newText in
            let strToSave = self.cleanText(text: newText.text)
            if strToSave != newText.text {
                //a cleaning has actually happened, so we must change our text to reflect the cleaning
                DispatchQueue.main.async {
                    self.myproperty.text = strToSave
                }
            }
        }
    }

    deinit {
        saveCanc.cancel()
        updateCanc.cancel()
    }

    private func cleanText(text: String) -> String {
        //remove all the spaces
        let resultStr = String(text.unicodeScalars.filter {
            $0 != " "
        })

        //take up to 5 characters
        return String(resultStr.prefix(5))
    }

    private func saveTextToFile(text: String) {
        print("text saved: \(text)")
    }
}
import Combine

struct MyPropertyStruct {
    var text: String
}

class TestViewModel : ObservableObject {
    @Published var myproperty = MyPropertyStruct(text: "initialText")
    private var canc: AnyCancellable!

    init() {
        canc = $myproperty
            .debounce(for: 0.5, scheduler: DispatchQueue.main)
            .map { $0.text.replacingOccurrences(of: " ", with: "")}
            .map { String($0.prefix(5)) }
            .sink { [unowned self] strToSave in
                self.saveTextToFile(text: strToSave)
        }
    }

    private func saveTextToFile(text: String) {
        print("text saved")
    }
}

struct ContentView: View {
    @ObservedObject var testModel = TestViewModel()

    var body: some View {
        TextField("", text: $testModel.myproperty.text)
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM