使MVC再次出色!

使用泛型,协议和扩展摆脱大型视图控制器

本文的副本也可在此处获得

Model-View-Controller是一种非常常见的软件结构,用于在Apple生态系统中创建应用程序。

尽管MVC是一个简单的概念,但是开发人员经常会错过使用它并将其转换为MVC的想法。 代表“ Massive View Controller”结构。

在这篇文章中,我们将看到如何通过使用一些简单的技术(如泛型,协议,扩展,便捷的初始化程序等)使MVC再次变得出色,让我们开始吧!

我们的应用程序是一个非常简单的应用程序,只有一个屏幕:登录屏幕,用户可以在其中输入其电子邮件和密码,并且该应用程序将在控制台中将其打印出来。

首先,我们将摆脱所有邪恶的源头,情节提要😈

尽管Apple提倡Storyboard作为为其生态系统开发用户界面的标准方法,但Storyboard仍然存在以下主要问题:

  • 性能下降和编译时间。
  • 它们不是git友好的,由于其XML性质,它们使团队工作更加艰苦。
  • 故事板在运行时失败,而不是在编译时失败,这使它们成为大量未知错误和问题的来源。
  • 和更多 …

就个人而言,每次在项目中使用情节提要或Xib文件时,我都会遇到可伸缩性问题。
但是,当我尝试以编程方式编写我的UI代码时,视图控制器变得更大,更难维护,从而无法实现与Massive View Controller对抗的全部目的defeat

1.子类化UIViewUIViewController
2.将所有UI和布局代码从视图控制器中移开。
3.使用扩展来组织视图控制器
4.使用便捷的初始化程序在一行中初始化和设置公共UI元素。

子类化UIView

我们将创建一个名为View的新基类,此子类将从现在开始使用,而不是UIView

 类视图:UIView {覆盖init(frame:CGRect){ 
super.init(frame:框架)
setViews()
layoutViews()
}是否需要初始化?(编码器aDecoder:NSCoder){
super.init(编码器:aDecoder)
setViews()
layoutViews()
} ///在这里设置您的视图及其子视图。
func setViews(){
backgroundColor = .white
} ///在此处布置子视图。
func layoutViews(){}}

子类化UIViewController:

与使用View一样,我们将创建另一个名为ViewController的新基类,从现在开始将继承并使用它,而不是UIViewController

 类ViewController :UIViewController { 

覆盖func loadView(){
视图= V()
} var customView:V {
返回视图为! V
}}

一起登录示例

LoginView.swift

 协议LoginViewDelegate:类{ 
func loginView(_视图:LoginView,didTapLoginButton按钮:UIButton)}

类LoginView:查看{

弱var委托:LoginViewDelegate?var emailAddress:字符串{
让文字= emailTextField.text
返回文本吗?.trimmingCharacters(in:.whitespacesAndNewlines)?? ”
}

var密码:字符串{
返回passwordTextField.text? ”
}

私人懒惰变量emailTextField:UITextField = {
让textField = UITextField()
textField.placeholder =“电子邮件地址”
textField.keyboardType = .emailAddress
返回textField
}()

私人懒惰的var passwordTextField:UITextField = {
让textField = UITextField()
textField.placeholder =“密码”
textField.isSecureTextEntry = true
返回textField
}()

私人懒惰var loginButton:UIButton = {
let button = UIButton(类型:.system)
button.setTitle(“ Login”,用于:.normal)
返回按钮
}()覆盖func setViews(){
super.setViews()addSubview(emailTextField)
addSubview(passwordTextField)
addSubview(loginButton)
loginButton.addTarget(self,action:#selector(didTapLoginButton(_ :)),for:.touchUpInside)
}覆盖f​​unc layoutViews(){
//在此处布置子视图,请考虑使用SnapKit,
太神奇了!
}

}

//标记:-动作
私人扩展名LoginView {@objc func didTapLoginButton(_按钮:UIButton){
委托?.loginView(自己,didTapLoginButton:按钮)
}

}

LoginViewController.swift

 类LoginViewController:ViewController  {覆盖func viewDidLoad(){ 
super.viewDidLoad()
customView.delegate =自我
}

}

//标记:-LoginViewDelegate
扩展名LoginViewController:LoginViewDelegate {func loginView(_视图:LoginView,didTapLoginButton按钮:UIButton){
print(“电子邮件地址:\(customView.emailAddress)”)
print(“密码:\(customView.password)”)
}

}

奖金

使用便捷的初始化程序停止一次又一次地写相同的代码:

 扩展UITextField {便利init( 
占位符:字符串,
keyboardType:UIKeyboardType = .default,
isSecureTextEntry:Bool = false){

self.init()

self.placeholder =占位符
self.keyboardType =键盘类型
self.isSecureTextEntry = isSecureTextEntry
}

}

扩展UIButton {便利init(
类型:UIButtonType = .system,
标题:字符串?,
图片:UIImage?){

self.init(type:type)self.setTitle(标题,用于:.normal)
self.setImage(image,for:.normal)
}

}

新的LoginView

 协议LoginViewDelegate:类{ 
func loginView(_视图:LoginView,didTapLoginButton按钮:UIButton)
}类LoginView:视图{弱var委托:LoginViewDelegate? var emailAddress:字符串{
返回emailTextField.text?.trimmingCharacters(in:.whitespacesAndNewlines)?? ”
} var密码:字符串{
返回passwordTextField.text? ”
}私人懒惰变量emailTextField:UITextField = {
返回UITextField(占位符:“电子邮件地址”,keyboardType:.emailAddress)
}()私有惰性var passwordTextField:UITextField = {
返回UITextField(占位符:“密码”,isSecureTextEntry:true)
}()私有惰性var loginButton:UIButton = {
返回UIButton(标题:“登录”,图像:无)
}()覆盖func setViews(){
super.setViews()
addSubview(emailTextField)
addSubview(passwordTextField)
addSubview(loginButton)
loginButton.addTarget(self,action:#selector(didTapLoginButton(_ :)),for:.touchUpInside)
}覆盖f​​unc layoutViews(){...}

}

结论

1.保持布局代码远离视图控制器
2.使用private可以防止视图控制器无法访问您的UI代码,除非有很好的理由不这样做。
3.创建基本的UIViewUIViewController子类,并使用泛型和协议,以使该死的viewDidLoad方法保持干净!
4.使用便捷的初始化程序来初始化UI元素,并将其属性设置为一行。

然后去哪儿?

  • 继续第二部分 ,我们将在此基础上处理键盘事件。
  • 请参阅Github上的示例Xcode项目