今天給大家提供一種在iOS開發中View層的一種優化思路。
在iOS開發中,不管你是用純代碼布局,還是用Storyboard布局,都免不了在Controller中加入一些界面相關的邏輯,隨著業務邏輯復雜度的增加,Controller里的代碼會越來越多,不方便閱讀,還不方便維護,這時候除了把一些業務邏輯分出去之外,還可以把界面相關的代碼優化一把。
優化主要借助Controller中的loadView來實現,對于這個方法,官方文檔里描述的很清楚,這里不再過多描述。我們主要是在loadView中把我們自己定義的View賦給Controller的view屬性,然后把所有界面相關的代碼都在我們自己定義的View中去管理。
畫板.png
這里我使用App的登錄注冊功能做簡單分析,界面如上圖所示,圖一是登錄界面,圖二是注冊界面,其中圖一的布局是用Storyoard做的,圖二是用純代碼配合Masonry寫的,先說圖二純代碼寫的吧,首先我們需要寫一個RegistView,相關代碼如下:
@interface RegistView : UIView
@property (nonatomic, strong) UITextField *userNameTextField;
@property (nonatomic, strong) UITextField *userPswdTextField;
@property (nonatomic, strong) UITextField *againPswdTextField;
@property (nonatomic, strong) UIButton *registButton;
@property (nonatomic, copy) void (^registCallBack)(NSString *, NSString *, NSString *);
@end
@implementation RegistView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self loadSubviews];
}
return self;
}
- (void)loadSubviews {
self.backgroundColor = [UIColor whiteColor];
[self addSubview:self.userNameTextField];
[self addSubview:self.userPswdTextField];
[self addSubview:self.againPswdTextField];
[self addSubview:self.registButton];
[self.userNameTextField mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.mas_left).offset(58);
make.right.equalTo(self.mas_right).offset(-58);
make.top.equalTo(self.mas_safeAreaLayoutGuideTop).offset(49);
make.height.mas_equalTo(34);
}];
[self.userPswdTextField mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.userNameTextField);
make.top.equalTo(self.userNameTextField.mas_bottom).offset(22);
make.height.mas_equalTo(34);
}];
[self.againPswdTextField mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.userNameTextField);
make.top.equalTo(self.userPswdTextField.mas_bottom).offset(22);
make.height.mas_equalTo(34);
}];
[self.registButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.userNameTextField);
make.top.equalTo(self.againPswdTextField.mas_bottom).offset(22);
make.height.mas_equalTo(50);
}];
}
#pragma mark - Action
- (void)registAction {
if (self.registCallBack) {
self.registCallBack(self.userNameTextField.text, self.userPswdTextField.text, self.againPswdTextField.text);
}
}
#pragma mark - Get
- (UITextField *)userNameTextField {
if (!_userNameTextField) {
_userNameTextField = [UITextField new];
_userNameTextField.borderStyle = UITextBorderStyleRoundedRect;
_userNameTextField.placeholder = @"點擊輸入用戶名";
}
return _userNameTextField;
}
- (UITextField *)userPswdTextField {
if (!_userPswdTextField) {
_userPswdTextField = [UITextField new];
_userPswdTextField.borderStyle = UITextBorderStyleRoundedRect;
_userPswdTextField.placeholder = @"點擊輸入密碼";
_userPswdTextField.secureTextEntry = YES;
}
return _userPswdTextField;
}
- (UITextField *)againPswdTextField {
if (!_againPswdTextField) {
_againPswdTextField = [UITextField new];
_againPswdTextField.borderStyle = UITextBorderStyleRoundedRect;
_againPswdTextField.placeholder = @"點擊再次輸入密碼";
_againPswdTextField.secureTextEntry = YES;
}
return _againPswdTextField;
}
- (UIButton *)registButton {
if (!_registButton) {
_registButton = [UIButton buttonWithType:UIButtonTypeSystem];
[_registButton setTitle:@"注冊" forState:UIControlStateNormal];
[_registButton addTarget:self action:@selector(registAction) forControlEvents:UIControlEventTouchUpInside];
}
return _registButton;
}
其次是在Controller里面loadView方法里面給view屬性賦值,代碼如下:
@interface RegistViewController ()
@property (nonatomic, strong) RegistView *mainView;
@end
@implementation RegistViewController
- (void)loadView {
self.mainView = [[RegistView alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.view = self.mainView;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self configViews];
}
- (void)dealloc {
NSLog(@"%s", __func__);
}
#pragma mark -
- (void)configViews {
self.title = @"注冊";
__weak typeof(self) weakSelf = self;
self.mainView.registCallBack = ^(NSString * _Nonnull userName, NSString * _Nonnull userPswd, NSString * _Nonnull userAgainPswd) {
if (nil != weakSelf) {
}
NSLog(@"%@ %@ %@", userName, userPswd, userAgainPswd);
};
}
@end
這樣我們就順利的把Controller里面對關于View的東西都優雅的拿出來了。
如果你是Storyboard愛好者,同樣的也有辦法去處理,以圖一為例,首先我們要在Storyboard里面完成LoginView的布局,如圖:WX20191222-161005@2x.png
其中我們把Storyboard中Controller的view關聯到了我們本地創建的LoginView上,對相應的一些空間也做了關聯,相關代碼如下:
@interface LoginView : UIView
@property (weak, nonatomic) IBOutlet UITextField *userNameTextField;
@property (weak, nonatomic) IBOutlet UITextField *userPswdTextField;
@property (weak, nonatomic) IBOutlet UIButton *loginButton;
@property (weak, nonatomic) IBOutlet UIButton *registButton;
@end
@implementation LoginView
-(void)awakeFromNib {
[super awakeFromNib];
NSLog(@"%s", __func__);
// TODO: 操作一些樣式相關、狀態相關的
}
@end
而在Controller里面我們需要對view做一個類型轉換,然后才能使用,相關代碼如下:
@interface LoginViewController ()
@property (nonatomic, weak) LoginView *mainView;
@end
@implementation LoginViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = @"登錄";
}
#pragma mark - Action
- (IBAction)loginAction:(id)sender {
NSString *userName = self.mainView.userNameTextField.text;
NSString *userPswd = self.mainView.userPswdTextField.text;
NSLog(@"%@ %@", userName, userPswd);
// TODO: 處理登錄的邏輯
}
- (IBAction)registAction:(id)sender {
RegistViewController *vc = [RegistViewController new];
[self.navigationController pushViewController:vc animated:YES];
}
#pragma mark - View
- (LoginView *)mainView {
return (LoginView *)self.view;
}
@end
這樣,我們就優雅的完成了對View層的拆分。點擊下載demo
如果大家有什么問題,或者發現了有什么不對的地方,再或者有更好的方法,歡迎留言。