控制器,放轻松! 我们取出UIView中的代码

你有一个大的UIViewController吗? 对许多人来说,是的。 一方面,它与数据一起工作,另一方面-与接口一起工作。

在数百篇有关体系结构的文章中描述了将逻辑与接口分离的任务:MVP,MVVM,VIPER。 它们解决了数据流的问题,但没有回答如何使用界面的问题:在一个地方仍然保留了元素的创建,布局,配置,输入和动画处理。

让我们将视图与控制器分开,看看loadView()如何帮助我们。



iOS的应用程序界面是UIView层次结构。 每个view的任务:创建元素,自定义,安排位置,设置动画。 从UIView: addSubview(), drawRect(), layoutSubviews().类中的方法可以看出UIView: addSubview(), drawRect(), layoutSubviews().

如果查看UIViewController类的方法,则可以看到它管理view:加载,响应加载屏幕和用户操作并显示新屏幕。 通常,应该在UIView的代码是我们在UIViewController子类中编写的,这使其变得太大。 分开吧

loadView()


UIViewController的生命周期从loadView()开始。 简化的实现如下所示:

 // CustomViewController.swift func loadView() { self.view = UIView() } 

我们可以重写该方法并指定我们的类。

super.loadView()不需要被调用!

 // CustomViewController.swift override func loadView() { self.view = CustomView() } 

CustomView.swift实现
 // CustomView.swift final class CustomView { let square: UIView = UIView() init() { super.init() square.backgroundColor = .red addSubview(square) } } 


控制器将加载CustomView,将其添加到层次结构中,公开.frame.view属性将是我们需要的类:

 // CustomViewController.swift print(view) // CustomView 

但是,尽管编译器不知道该类,但认为存在正常的UIView 。 让我们使用类型转换功能修复此问题:

 // CustomViewController.swift func view() -> CustomView { return self.view as! CustomView } 

现在您可以看到CustomView变量:

 // CustomViewController.swift func viewDidLoad() { super.viewDidLoad() view().square //  } 

简化关联类型
Ruslan Kavetsky建议使用协议扩展来消除代码重复:

 protocol ViewSpecificController { associatedtype RootView: UIView } extension ViewSpecificController where Self: UIViewController { func view() -> RootView { return self.view as! RootView } } 

对于每个新控制器,您只需通过typealias指定其UIView的协议和子类:

 // CustomViewController.swift final class CustomViewController: UIViewController, ViewSpecificController { typealias RootView = CustomView func viewDidLoad() { super.viewDidLoad() view().square //  } } 

UIView子类中的代码


创建和配置控件


字体,颜色,常量和层次结构可以直接在CustomView构造函数中设置:

 // CustomView.swift init() { super.init() backgroundColor = .lightGray addSubview(square) } 

layoutSubviews()


手动布局的最佳位置是layoutSubviews()方法。 每次更改view大小时都会调用它,因此您可以依靠bounds大小来进行正确的计算:

 // CustomView.swift override func layoutSubviews() { super.layoutSubviews() square.frame = CGRect(x: 0, y: 0: width: 200, height: 200) square.center = CGPoint(x: bounds.width / 2, y: bounds.height / 2) } 

私人控制,公共财产


如果有时间,那么我将property控件设为私有,但是我通过“知识领域”中的公共变量或函数来管理它们。 一个简单的例子:

 // CustomView.swift private let square = UIView() var squarePositionIsValid: Bool { didSet { square.backgroundColor = squarePositionIsValid? .green : .red } } func moveSquare(to newCenter: CGPoint) { square.center = newCenter } 

封装的优点:内部逻辑隐藏在接口后面。 例如,对象的有效性可以由区域的颜色而不是正方形的颜色指示,但是控制器将不了解任何信息。

viewDidLoad()中还剩下什么?


如果使用Interface Builder,则viewDidLoad()通常viewDidLoad()空。 如果在代码中创建view ,则需要通过target-action模式绑定它们的动作,添加UIGestureRecognizer或绑定委托。

可通过Interface Builder进行自定义


可以通过Interface Builder(以下称为IB)配置用于view的子类。

您需要选择view对象(而不是控制器)并设置其类。 不必编写自己的loadView() ,控制器将自己完成。 但是UIView仍然必须UIView类型。



UIView中的IBOutlet


如果在view内部选择控件,则助手编辑器将识别UIView类,并将其作为“自动”模式下的第二个文件提供。 因此您可以将IBOutlet转移到view



如果不起作用
手动打开CustomView类,编写IBOutlet 。 现在,您可以拖动标记并将鼠标悬停在IB中的元素上。



如果使用代码创建接口,则在init()之后可以访问所有对象,但是在使用IB时,只有在使用awakeFromNib()方法从UIStoryboard加载接口之后,才出现对IBOutlet访问:

 // CustomView.swift func awakeFromNib() { super.awakeFromNib() square.layer.cornerRadius = 8 } 

UIViewController中的IBAction


以我的口味,控制器应保留所有用户操作。 从标准:

  • 控件的目标动作
  • UIViewController委托实现
  • 区块执行
  • Notification反应

在这种情况下, UIViewController仅控制接口。 与业务逻辑相关的所有内容都应从控制器中取出,但这是一个选择:MVP,VIPER等。

目标c


在Objective-C中,您可以完全替换UIView类型。 为此,用所需的类声明属性,重写settergetter ,指定该类:

 // CustomViewController.m @interface CustomViewController @property (nonatomic) CustomView *customView; @end @implementation - (void)setView:(CustomView *)view{ [super setView:view]; } - (CustomView *)view { return (CustomView *)super.view; } @end 

结束


在GitHub上的示例中,您可以查看一个简单任务的类分离:正方形的颜色取决于其位置(绿色区域为绿色,外部为红色)。

屏幕越复杂,效果越好:减少控制器,将代码转移到其位置。 代码只是移植到view ,但是封装使交互和读取代码变得容易。 有时, view可以与其他控制器一起重用。 例如,iPhone和iPad的不同控制器以自己的方式对键盘的外观做出反应,但这不会更改view代码。

每当团队欢迎简化并开始实践时,我在不同的项目中和与不同的人一起使用此代码。 我希望你也喜欢。 所有简单的UIViewController

Source: https://habr.com/ru/post/zh-CN432718/


All Articles