1.自定义视图
1> 概述
定义视图:系统标准UI之外,自己组合而出的新的视图。
定义视图的优点:
iOS提供了很多UI组件,借助它们我们可以实现不同的功能。尽管如此,实际开发中,我们还需要自定义视图。积累自己的代码库,方便开发。自己封装的视图,能像系统UI控件一样,用于别的项目中,能大大降低开发成本,提高开发效率。
高质量代码的特点:可复用,可移植,精炼等。(高内聚,低耦合)
2> 自定义视图步骤(以登录框为例(LTView))
根据需求的不同,自定义视图继承的类也有所不同。一般自定义的视图会继承于UIView。以下是自定义视图的要点:
① 创建一个UIView子类: @interface LTView : UIView
② 在类的初始化方法中添加子视图。
// 添加视图
- (void)addAllViews
{
// leftLabel
self.leftLabel = [[UILabel alloc] initWithFrame:CGRectMake(kLabelX, kLabelY, , )];
// 文本对齐
self.leftLabel.textAlignment = NSTextAlignmentCenter;
// 添加到自定义视图
[self addSubview:self.leftLabel]; // rightTextField
self.rightTextField = [[UITextField alloc] initWithFrame:
CGRectMake(CGRectGetMaxX(self.leftLabel.frame) + kSpace,
CGRectGetMinY(self.leftLabel.frame),
self.frame.size.width - CGRectGetWidth(self.leftLabel.frame) - kSpace - kLabelX * ,
CGRectGetHeight(self.leftLabel.frame))];
// 设置边框样式
self.rightTextField.borderStyle = UITextBorderStyleRoundedRect;
// 添加到自定义视图
[self addSubview:self.rightTextField];
}
③ 类的.h文件提供一些接口(方法),便于外界操作子视图。
#import <UIKit/UIKit.h> @interface LTView : UIView // .h文件声明属性,为了方便和外界通信
@property (nonatomic, strong) UILabel *leftLabel;
@property (nonatomic, strong) UITextField *rightTextField; @end
④ 此时的 LTView 就变成了一个具有 label 和 textField 的视图了。
3> LTView在AppDelegate.m中的具体使用
// 用户名
LTView *userLTView = [[LTView alloc] initWithFrame:CGRectMake(, , self.window.frame.size.width - , )]; // 设置属性
userLTView.backgroundColor = [UIColor lightGrayColor];
userLTView.leftLabel.text = @"用户名";
userLTView.leftLabel.backgroundColor = [UIColor redColor];
userLTView.rightTextField.placeholder = @"请输入用户名";
userLTView.rightTextField.backgroundColor = [UIColor redColor]; // 设置代理
userLTView.rightTextField.delegate = self; // 添加父视图
[self.window addSubview:userLTView]; // 密码
ZF_LTView *pwLTview = [[ZF_LTView alloc] initWithFrame:
CGRectMake(CGRectGetMinX(userLTView.frame),
CGRectGetMaxY(userLTView.frame) + ,
CGRectGetWidth(userLTView.frame),
CGRectGetHeight(userLTView.frame))]; // 设置属性
pwLTview.backgroundColor = [UIColor lightGrayColor];
pwLTview.leftLabel.text = @"密码";
pwLTview.rightTextField.placeholder = @"请输入密码";
pwLTview.rightTextField.secureTextEntry = YES; // 添加到父视图
[self.window addSubview:pwLTview];
效果图
2.视图控制器
1> 概述
视图控制器是应用程序数据和视图之间的重要桥梁,每个iOS应用程序只显示一个用户界面,显示的内容是由控制器或一组视图控制器协调管理。所以,视图控制器提供了一个基本的框架来构建应用程序。
UIViewController是所有视图控制器的父类。
iOS提供了许多内置的视图控制器,以支持标准的用户界面部分,比如导航控制器(UINavigationController),标签控制器 (UITabBarController), 表视图控制器(UITableViewController)等。
2> 视图控制器的功能
- 控制视图大小变换、布局视图、响应事件。
- 检测以及处理内存警告。
- 检测以及处理屏幕旋转。
- 检测视图的切换。
- 实现模块独立,提高复用性。
3> 视图控制器的使用
① 定义UIViewController的子类:
@interface LoginViewController:UIViewController
② 在APPDelegate里创建视图控制器对象,作为window的根视图控制器:
// 创建window
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = [UIColor redColor];
[self.window makeKeyAndVisible];
// 设置根视图控制器
self.window.rootViewController = [[LoginViewController alloc] init];
③ 在viewDidLoad方法中使用默认创建好的视图对象view:
#pragma mark 视图加载完毕
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor grayColor];
NSLog(@"%s %d", __FUNCTION__, __LINE__);
// Do any additional setup after loading the view.
}
4> 视图控制器生命周期
viewDidLoad --> viewWillAppear --> viewDidAppear --> viewWillDisappear --> viewDidDisappear
@implementation ViewController // 视图已经加载
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib. NSLog(@"1 %s__%d", __FUNCTION__, __LINE__);
} // 视图将要出现
- (void)viewWillAppear:(BOOL)animated {
NSLog(@"2 %s__%d", __FUNCTION__, __LINE__);
}
// 视图将要消失
- (void)viewWillDisappear:(BOOL)animated {
NSLog(@"3 %s__%d", __FUNCTION__, __LINE__);
}
// 视图已经出现
- (void)viewDidAppear:(BOOL)animated {
NSLog(@"4 %s__%d", __FUNCTION__, __LINE__);
}
// 视图已经消失
- (void)viewDidDisappear:(BOOL)animated {
NSLog(@"5 %s__%d", __FUNCTION__, __LINE__);
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} @end
3.视图控制器指示视图
设置步骤
自定义视图类继承UIView。在初始化方法中添加子视图控件。
// 重写initWithFrame
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor yellowColor]; [self addAllViews];
}
return self;
} // 添加视图
- (void)addAllViews
{
/**
* 布局: userLabel userTextField loginButton
* 事件: 代理事件 按钮点击事件 (controller实现)
* @return nil
*/ // 布局userLabel
self.userLabel = [[UILabel alloc] initWithFrame:CGRectMake(, , , )];
self.userLabel.text = @"用户名";
self.userLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:self.userLabel]; // 布局userTextField
self.userTextField = [[UITextField alloc] initWithFrame:CGRectMake(CGRectGetMaxX(self.userLabel.frame) + , CGRectGetMinY(self.userLabel.frame), , )];
self.userTextField.borderStyle = UITextBorderStyleRoundedRect;
self.userTextField.placeholder = @"请输入用户名";
[self addSubview:self.userTextField]; // 布局loginButton
self.loginButton = [UIButton buttonWithType:UIButtonTypeSystem];
self.loginButton.backgroundColor = [UIColor cyanColor];
self.loginButton.frame = CGRectMake(, CGRectGetMaxY(self.userTextField.frame) + kSpace, CGRectGetWidth(self.frame) - , );
[self.loginButton setTitle:@"登 录" forState:UIControlStateNormal]; self.loginButton.tintColor = [UIColor whiteColor];
self.loginButton.layer.cornerRadius = ; [self addSubview:self.loginButton];
}
重写controller的loadView方法。创建自定义视图对象,并指定为controller的view。(注:loadView方法在控制器的view为nil的时候被调用,用于以编程的方式创建view的时候用到。loadView是使用代码生成视图的时候,当视图第一次载入的时候调用的方法,用于使用(写)代码来实现控件。)
// 加载视图
// 使用控制器指定自定义view(也就是替换控制器的view)
// self.view没有被创建对象,节省内存
- (void)loadView
{
// 初始化自定义视图
self.loginView = [[LoginView alloc] initWithFrame:[UIScreen mainScreen].bounds];
// 使用自定义视图替换控制器视图
self.view = self.loginView;
}
将子视图控件对象设置为自定义视图类的属性,在viewDidLoad方法中进行设置:添加action、设置delegate等等。
// 处理事件一般都是在这个函数中实现
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view. // 处理事件 // 1. 代理事件
self.loginView.userTextField.delegate = self; // 2.按钮点击事件
[self.loginView.loginButton addTarget:self action:@selector(loginButtonClick) forControlEvents:UIControlEventTouchUpInside];
}
在controller中添加按钮点击事件实现和代理方法的实现。
#pragma mark loginButtonClick Method (实现按钮点击方法) - (void)loginButtonClick
{
NSLog(@"别点我...");
SecondViewController *secondVC = [[SecondViewController alloc] init]; [self presentViewController:secondVC animated:YES completion:nil]; } #pragma mark UITextFieldDelegate Method (代理方法实现) // 点击return回收键盘
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
} // 触摸屏幕回收键盘
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.view endEditing:YES];
}
4.MVC
1> 概述
UIViewController是MVC设计模式的核心。
MVC是一个框架级的设计模式。
M是Model,主要用于建立数据模型(即数据的结构)。
V是View,我们能看到的所有控件都是view,view主要的功能是展示数据
C是控制器,主要是控制M和V的通信。
2> iOS中的MVC
5.屏幕旋转
1> 检测屏幕旋转
视图控制器本身能检测到屏幕的旋转,如果要处理屏幕旋转,需要重写几个方法:
supportedInterfaceOrientations(设置设备支持旋转的方向,如果不添加,视图控制器将无法检测屏幕的旋转)。
willRotateToInterfaceOrientation:duration:(暂停音乐、关闭视图交互等)。
willAnimateRotationToInterfaceOrientation:duration:(添加自定义动画等)。
didRotateFromInterfaceOrientation:(播放音乐、打开视图交互等)。
#pragma mark 检测屏幕旋转
// 屏幕所支持的样式
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
// 支持所有的方向
return UIInterfaceOrientationMaskAll;
}
2> 视图处理
注意视图控制器会自动调整view的大小以适应屏幕旋转,bounds被修改,触发view的layoutSubviews方法。
view重写layoutSubviews方法,根据设备方向,重新布局。
[UIApplication sharedApplication].statusBarOrientation提供设备当前方向。
// 布局子视图:当屏幕旋转的时候,可以重新布局子视图的位置
- (void)layoutSubviews
{
// 如果是竖屏(UIInterfaceOrientationPortrait)的话,保持原来的尺寸
if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortrait) {
self.loginButton.frame = CGRectMake(, CGRectGetMaxY(self.userTextField.frame) + kSpace, CGRectGetWidth(self.frame) - , );
} else { // 否则横屏改变位置
self.loginButton.frame = CGRectMake(, CGRectGetMaxY(self.userTextField.frame) + kSpace, , );
}
}
6.内存警告
1> 概述
内存警告来源:手机内存80M,程序运行过程中内存接近80M时程序会为每一个视图控制器发送内存警告消息。
如何处理:
① 控制器能监测内存警告,以便我们避免内存不够引起的crash。
② 在定义的controller子类中重写didReceiveMemoryWarning方法。
③ 释放暂时不使用的资源(self.view及view的子视图例如数据对象、图 像)。
2> 代码
// 当接收内存警告的时候都会走这个方法
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
NSLog(@"%s__%d", __FUNCTION__, __LINE__);
}