将值从 viewcontroller 传递给 viewmodel 的更好方法

Which would be the better way to pass values to viewmodel from viewcontroller

这个问题可能看起来很基础,但我发布它是为了获得建议。

以下是使用 MVVM 模式的示例登录模块。

viewcontroller代码如下

class ViewController: UIViewController {

    private var loginviewmodel = LoginViewModel()

    @IBOutlet weak var textFieldUserName: UITextField!

    @IBOutlet weak var textFieldPassword: UITextField!

    @IBAction func signIn(_ sender: Any) {
        //CASE 1
        loginviewmodel.performLogin(name: textFieldUserName.text!, pwd: textFieldPassword.text!)
        //CASE 2
        //loginviewmodel.performLogin()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        textFieldUserName.delegate = self
        textFieldPassword.delegate = self
    }
}

extension ViewController: UITextFieldDelegate {

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

        let inputText = (textField.text! as NSString).replacingCharacters(in: range, with: string)

        switch textField {
        case textFieldUserName:
            loginviewmodel.updateUsername(inputText: inputText)
        case textFieldPassword:
            loginviewmodel.updatePassword(inputText: inputText)
        default:
            return false
        }      

        return true
    }
}

而viewmodel代码如下

class LoginViewModel {

    var userName: String?
    var password: String?

    func updateUsername(inputText: String) {
        self.userName = inputText
    }

    func updatePassword(inputText: String) {
        self.password = inputText
    }

    func performLogin() {
        print("Login successful with username = \(userName) and password = \(password).")
    }

    func performLogin(name: String, pwd: String) {
        print("Login successful with username = \(name) and password = \(pwd).")
    }

}

我有两种情况,其中值从 viewcontroller 传递到视图模型的方式不同。

这里首选哪种方式?

我更喜欢第二种情况

loginviewmodel.performLogin()

通过使用委托,您还可以在用户输入时验证两个文本字段中的输入,而不是等待用户输入错误的数据然后验证

请使用这个:

class ViewController: UIViewController {
    private var loginviewmodel = LoginViewModel()

    @IBOutlet weak var textFieldUserName: UITextField!
    @IBOutlet weak var textFieldPassword: UITextField!

    @IBAction func signIn(_ sender: Any) {
        //CASE 1 
        // Check validation textfield is empty or not  
        loginviewmodel.performLogin(name: textFieldUserName.text!, pwd: textFieldPassword.text!)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        textFieldUserName.delegate = self
        textFieldPassword.delegate = self
    }
}

class LoginViewModel {
    var userName: String?
    var password: String?

    func performLogin(name: String, pwd: String) {
        print("Login successful with username = \(name) and password = \(pwd).")
    }
}

首选的一种是使用delegate method

程序员工作的一个常见部分是保持 UI 状态与模型状态同步。和用户输入。当用户与屏幕交互时。这种互动应该立即反映出来 if it useful for user 不要等到他再采取行动 press submit Button

让我们解释一下 假设您的屏幕有 2 个输入,用户名或电子邮件和密码作为 TextFields

一键登录

  • 您不需要用户在输入用户名、密码之前按下登录按钮
  • 您需要通知viewController用户输入数据,现在他可以提交
  • 您需要在允许提交之前对用户输入数据进行一些验证。例如需要验证密码和电子邮件验证的字符数...等等

    没有委托的第一种方法

    不会进行任何验证,只是我们会在用户按下登录后通知用户错误消息,登录视图模型在用户输入时没有任何关于 ViewController 的信息,viewModel 仅在用户按下登录按钮时知道信息

Delegate 的第二种方法

在这种方法中,LoginViewModel 现在知道用户在按下登录按钮之前输入了什么,我们可以执行一些验证以启用或禁用 loginButton

ViewController:

class ViewController: UIViewController,LoginViewModelViewDelegate {

    private var loginviewmodel = LoginViewModel()

    @IBOutlet weak var textFieldUserName: UITextField!

    @IBOutlet weak var textFieldPassword: UITextField!

    @IBAction func signIn(_ sender: Any) {

        loginviewmodel.performLogin()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
            // delegate to allow ViewModel notify his view

            loginviewmodel.viewDelegate = self

          self.textFieldUserName.addTarget(self, action: #selector(userNameFieldDidChange(_:)), for: UIControlEvents.editingChanged)
        self.textFieldPassword.addTarget(self, action: #selector(passwordFieldDidChange(_:)), for: UIControlEvents.editingChanged)
    }

  // MARK: - user Input notification


    @objc func userNameFieldDidChange(_ textField: UITextField)
         {
           if let text = textField.text {
               loginviewmodel.userName = text
           }
         }

    @objc func passwordFieldDidChange(_ textField: UITextField)
       {
           if let text = textField.text {
               loginviewmodel.password = text
          }
       }

    // MARK: - LoginViewModel Delegate
    func canSubmitStatusDidChange(_ viewModel: LoginViewModel, status: Bool) {

        // Enable or disable login button to allow user to submit input
    }

}

ViewModel:

import Foundation

protocol LoginViewModelViewDelegate: class
{
    func canSubmitStatusDidChange(_ viewModel: LoginViewModel, status: Bool)
}

class LoginViewModel {

    weak var viewDelegate: LoginViewModelViewDelegate?


    fileprivate var passwordIsValidFormat: Bool = false
    fileprivate var userNameIsValidFormat: Bool = false

    /// Submit
    var canSubmit: Bool {
        return userNameIsValidFormat && passwordIsValidFormat
    }


    /// Email
    var userName: String = "" {
        didSet {
            if oldValue != userName {

                let oldCanSubmit = canSubmit
                userNameIsValidFormat = validateUserNameAsEmailFormat(userName)
                if canSubmit != oldCanSubmit {
                   viewDelegate?.canSubmitStatusDidChange(self, status: canSubmit)
                }
            }
        }
    }



    /// Password
    var password: String = "" {
        didSet {
            if oldValue != password {
                let oldCanSubmit = canSubmit
                passwordIsValidFormat = validatePasswordFormat(password)
                if canSubmit != oldCanSubmit {
                    viewDelegate?.canSubmitStatusDidChange(self, status: canSubmit)
                }
            }
        }
    }

    func performLogin() {
        // perform Login and you can add anather delegate to notify View with error Message of login thow error
    }




    fileprivate func validateUserNameAsEmailFormat(_ userName: String) -> Bool
    {
        let REGEX: String
        REGEX = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,32}"
        return NSPredicate(format: "SELF MATCHES %@", REGEX).evaluate(with: userName)
    }


    /// Validate password is at least 6 characters
    fileprivate func validatePasswordFormat(_ password: String) -> Bool
    {
        let trimmedString = password.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
        return trimmedString.count > 8
    }


}