• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

ios - 如何在 SwiftUI View 上使用组合

[复制链接]
菜鸟教程小白 发表于 2022-12-12 15:12:47 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题

这个问题与这个问题有关:How to observe a TextField value with SwiftUI and Combine?

但我要问的是更笼统的问题。 这是我的代码:

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 应用程序)中的组合内容。



Best Answer-推荐答案


你应该在你的 ViewModel 中做你想做的事。您的 View 模型是 TestModel 类(我建议您在 TestViewModel 中重命名它)。这是您应该在模型和 View 之间放置逻辑的地方。 ViewModel 应该准备好模型以便为可视化做好准备。这是放置组合逻辑的正确位置(当然,如果它与 View 相关)。

现在我们可以使用您的具体示例来实际制作示例。老实说,根据您真正想要实现的目标,有几个略有不同的解决方案。但现在我会尽量保持通用,然后你可以告诉我解决方案是否良好或需要一些改进:

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 发布者来延迟清理字符串和对保存方法的调用。根据文档:

debounce(for:schedulerptions

Use this operator when you want to wait for a pause in the delivery of events from the upstream publisher. For example, call debounce on the publisher from a text field to only receive elements when the user pauses or stops typing. When they start typing again, the debounce holds event delivery until the next pause.

当用户停止输入时,debounce 发布者会等待指定的时间(在我的示例中为 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)")
    }
}

关于ios - 如何在 SwiftUI View 上使用组合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57922766/

回复

使用道具 举报

懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关注0

粉丝2

帖子830918

发布主题
阅读排行 更多
广告位

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap