【问题标题】:Which would be the better way to pass values to viewmodel from viewcontroller这将是从视图控制器将值传递给视图模型的更好方法
【发布时间】:2018-06-01 10:55:12
【问题描述】:

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

以下是使用 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).")
    }

}

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

  • 文本直接作为函数参数传递的第一种情况

  • 第二种情况,通过文本委托方法传递文本

这里的首选方式是什么?

【问题讨论】:

    标签: ios swift mvvm parameter-passing uitextfielddelegate


    【解决方案1】:

    首选是使用delegate method

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

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

    一键登录

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

      没有委托的第一种方法

      不会进行验证,只是在用户按下登录后我们会通知用户错误消息,LoginView Model 在用户输入时对 ViewController 没有任何信息,viewModel 仅在用户按下登录按钮时才知道信息

    代理的第二种方法

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

    视图控制器:

    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
        }
    
    }
    

    视图模型:

    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
        }
    
    
    }
    

    【讨论】:

    • 我在想一个场景:假设我有一个模块说转账。该模块包括 2 个视图控制器:VCA 和 VCB。 VCA 接受接收者的电子邮件、金额等输入,VCB 接受用户密码来完成转账。对于两个 VC,我使用通过 segue 共享的单个 VM(验证两个 VC 的输入)。现在所有的委托功能都需要在两个 VC 中实现,这是不必要的。那么在使用协议和委托时,在 VC 之间传递 VM 对象不是首选吗?
    【解决方案2】:

    我更喜欢第二种情况

    loginviewmodel.performLogin()
    

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

    【讨论】:

      【解决方案3】:

      请使用这个:

      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).")
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-07-23
        • 1970-01-01
        • 2013-11-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-11
        相关资源
        最近更新 更多