zwvista

导航

使用 UIKIt + Combine + MVVM 进行 GUI 编程

课题

  1. 程序界面由3个文本编辑框和1个文本标签组成。
  2. 要求文本标签实时显示3个文本编辑框所输入的数字之和。
  3. 文本编辑框输入的不是合法数字时,将其值视为0。
  4. 3个文本编辑框的初值分别为1,2,3。

创建工程

打开 Xcode,File / New / Project..
在 New Project 向导的第1页,选 iOS / App
在向导的第2页填上 Product Name: CombineExample
在向导的第3页选择任意文件夹点击 Create 按钮创建工程
关闭所创建的工程

添加 CombineCocoa 包

选中工程,在右键菜单中选 Add Packages... 打开 Apple Swift Packages 对话框
在搜索栏中输入 https://github.com/CombineCommunity/CombineCocoa.git
回车开始搜索
选中搜索结果中的 CombineCocoa,点击 Add Package 按钮关闭对话框。
工程下方会出现 Package Dependencies,其中包含 CombineCocoa 0.4.0

ViewModel

在工程中添加 NumbersViewModel.swift 文件,内容如下:

import Foundation
import Combine

class NumbersViewModel: ObservableObject {
    @Published
    var number1 = "1"
    @Published
    var number2 = "2"
    @Published
    var number3 = "3"
    @Published
    var result = ""

    init() {
        $number1.combineLatest($number2, $number3)
            .map { String((Int($0) ?? 0) + (Int($1) ?? 0) + (Int($2) ?? 0)) }
            .assign(to: &$result)
    }
}

配置 UI

打开 Main.storyboard 文件,在 View Controller Scene / View Controller / View 下面放置3个 Text Field 和1个 Label。

控件变量

打开 ViewController.swift 文件,在 ViewController 类中添加控件变量

@IBOutlet weak var tfNumber1: UITextField!
@IBOutlet weak var tfNumber2: UITextField!
@IBOutlet weak var tfNumber3: UITextField!
@IBOutlet weak var lblResult: UILabel!

使用 Assistant Editor 同时打开 Main.storyboard 文件,用 Control 键将控件变量挂到相应的控件上。

使用 MVVM 的解决方案

在 ViewController 类添加 NumbersViewModel 类的实例,并且在 viewDidLoad 方法中添加绑定控件的代码

import UIKit
import Combine
import CombineCocoa

class ViewController: UIViewController {
// ...
    var vm: NumbersViewModel!
    var subscriptions = Set<AnyCancellable>()

    override func viewDidLoad() {
        super.viewDidLoad()
        vm = NumbersViewModel()
        vm.$number1.assign(to: \UITextField.text!, on: tfNumber1).store(in: &subscriptions)
        vm.$number2.assign(to: \UITextField.text!, on: tfNumber2).store(in: &subscriptions)
        vm.$number3.assign(to: \UITextField.text!, on: tfNumber3).store(in: &subscriptions)
        vm.$result.assign(to: \UILabel.text!, on: lblResult).store(in: &subscriptions)
        tfNumber1.textPublisher.compactMap{$0}.assign(to: &vm.$number1)
        tfNumber2.textPublisher.compactMap{$0}.assign(to: &vm.$number2)
        tfNumber3.textPublisher.compactMap{$0}.assign(to: &vm.$number3)
    }
// ...
}

posted on 2022-08-22 13:03  zwvista  阅读(791)  评论(0编辑  收藏  举报