这节课主要讲解:
- View Controller的生命周期
- NotificationCenter的相关知识
- 组件UITextView的简单使用
并通过Demo展示
ViewController的声明周期
ViewController 的生命周期可以看作一系列函数,ViewController是UIViewController的子类。
生命周期的开始是创建,创建后会涉及到:
- viewDidLoad()
- viewWillLayoutSubviews()
- viewDidLayoutSubviews()
- viewWillAppear()
- viewDidAppear()
- viewWillDisappear()
- viewDidDisappear()
- didReceiveMemoryWaring()
接下来详细介绍这几个生命周期:
viewDidLoad()
在viewDidLoad()之前会执行创建的动作,但是我们一般不会在创建中执行操作。
创建完成之后,outlets被设置好。
viewDidLoad()只会被调用一次,是用来初始化controller非常好的位置。
需要注意的是此时view的几何结构(geometry)还没完成,还无法确定App是运行在6寸的iPhone上还是10寸的iPad上。所以不要在viewDidLoad()中初始化和尺寸、几何结构相关的操作。
viewWillLayoutSubviews()
当view的几何结构发生改变时(比如屏幕从竖着变成横着),(ios7及以上)在展示之前会调用viewWillLayoutSubviews()
viewDidLayoutSubviews()
viewWillAppear()
view展现在屏幕之前,会调用viewWillAppear()函数。
当使用多MVC模型时,会有多个view,此时某个view的viewWillAppear()可能会被调用多次,所以只执行一次的初始化代码不要放在这里,而应该放在viewDidLoad()。
基于这个理解,有些操作需要放入viewWillAppear()执行,比如Model中的数据在view不可见时被更改了,当view再次展现的时候,需要同步Model和UI。
另外,和viewDidLoad()相对的,这里已经完成了view几何结构的初始化,因此可以执行和几何结构相关的一些操作了。
在生命周期viewWillAppear
中,必须调用[super viewWillAppear:animated]
,调用位置无关紧要,你可以在开始调用也可以在结束时调用。
viewWillDisappear
也是一样。
viewDidAppear()
view展现在屏幕上之后,调用viewDidAppear()
viewWillDisappear()
view即将消失时调用viewWillDisappear(),此时最好停止使用非必要的资源,比如动画等等。
1 | - (void)viewWillDisappear:(BOOL)animated |
viewDidDisappear()
view消失以后,调用viewDidDisappear()
didReceiveMemoryWaring()
内存不够了,可能是因为你的App,也可能是手机上的其他App造成的。
这是你应该试图清理内存。
为什么是我要清理内存?因为操作系统有权限kill应用,如果内存不够了,而你的App又占用了很多内存,很有可能被iOS杀死。
awakeFromNib()
(说实话,没完全听懂,先记下来,以后用到再详细研究一下吧。)
这个方法会发送给storyboard中绘制出来的所有对象,包括controller
如果进入到Controller的init方法,就一定也会进入到awakeFromNib
。
尽可能不要将代码放在awakeFromNib
中。
1 | - (void)setup {...}; // do something which can't wait until viewDidLoad |
组件UITextView简介
组件UITextView类似于UILable,但是可以展示多行,具有可选可编辑可滚动等特点。
NotificationCenter相关知识
接下来讲一讲NSNotification
Notifications也就是之前在MVC中称为radio station的概念。
NSNotificationcenter
通过
[NSNotificationCenter defaultCenter]
获取默认Notification Center。如果想监听这个radio station,那么发送如下消息:
1
2
3
4- (void)addObserver:(id)observer // 需要接收通知的对象
selector:(SEL)methodToInvokeIfSomethingHappens
name:(NSString *)name // station的名称
object:(id)sender; // 你感兴趣的变更,如果填写nil,标识任何人发生变更,都会通知你。指定某个sender,可以只接收指定对象发来的通知。当有广播的时候,observer会被通知:
1
2
3
4
5- (void)methodToInvokeIfSomethingHappens:(NSNotification *)notification {
notification.name; // 传递的station的名称
notification.object; // 给你发送通知的对象
notification.userInfo; // 有关发生了什么的特定信息
}结束后记得关闭radio station
1
2
3[center removeObserver:self]; //关闭所有radio station
// 或者
[center removeObserver:self name:UIContentSizeCategoryDidChangeNotification object:nil]; //关闭指定radio station如果离开堆栈时,没有关闭radio station,notification center可能会继续发送notification,并导致App崩溃!
因为NSNotificationCenter有一个指向你的unsafe retained指针。
关闭radio station最好的位置是在MVC离开屏幕的时候,实在不行,可以在dealloc中关闭,dealloc会在离开堆栈之前调用:
1
2
3
4- (void)dealloc {
// be careful in this method! can't access properties! you are almost gone from heap!
[[NSNotificationCenter defaultcenter] removeObserver:self];
}
关于NotificationCenter,一个常用的例子是字体的变化。当用户在系统设置中改变字体大小时,可以利用NotificationCenter使App中的字体也跟着变化。
1 | NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; |
Demo
目的:了解attributed text(富文本)、生命周期以及Notification。
新建App,在storageBoard中加入:
- TextView
- label
- 6 buttons
如下图:
为TextView和Label添加property:
1
2
3
4@interface ViewController()
@property (weak, nonatomic) IBOutlet UITextView *body;
@property (weak, nonatomic) IBOutlet UILabel *headline;
@end为buttons添加三个函数
四个色块的button共用一个函数,函数的作用是:在textView中选中一些文字,点击色块button后,选中的文字变成色块对应的颜色
1
2
3- (IBAction)changeBodySelectionColorToMatchBackgroundOfButton:(UIButton *)sender {
[self.body.textStorage addAttribute:NSForegroundColorAttributeName value:sender.backgroundColor range:self.body.selectedRange];
}outline按钮对应的函数,作用是给选中文字添加红色描边。
1
2
3- (IBAction)outlineBodySelection:(id)sender {
[self.body.textStorage addAttributes:@{ NSStrokeWidthAttributeName : @-3, NSStrokeColorAttributeName: [UIColor redColor] } range:self.body.selectedRange];
}添加描边需要添加两个属性,一个用来设置描边的宽度,一个用来设置描边的颜色。
NSStrokeWidthAttributeName
用来设置描边的宽度,设置为整数会让字体变成中空,设置为负数会保持字体填充。NSStrokeColorAttributeName
用来设置描边的颜色。unOutline按钮对应的按钮,作用是删除字体的描边。
1
2
3- (IBAction)unOutlineBodySelection:(id)sender {
[self.body.textStorage removeAttribute:NSStrokeWidthAttributeName range:self.body.selectedRange];
}直接利用
removeAttribute
删除NSStrokeWidthAttributeName
属性,颜色就不用管了,没有宽度颜色也就无法显示了。
设置outline按钮的字体描边
这样产不多就是这节课要介绍的关于富文本的知识点了。接下来加入一些生命周期的元素。
现在想要让outline按钮的文字变成描边的,我们可不想在设置一个按钮来让outline按钮outline,而是想一开始它就是描边的。
这个时候就用到生命周期了,可以在
viewDidLoad()
函数中实现:先为button添加outlet,
@property (weak, nonatomic) IBOutlet UIButton *outline;
,然后在viewDidLoad()
函数中编写代码:1
2
3
4
5
6
7
8
9
10- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSMutableAttributedString *title = [[NSMutableAttributedString alloc] initWithString:self.outline.currentTitle];
[title setAttributes:@ {
NSStrokeWidthAttributeName : @3,
NSStrokeColorAttributeName : self.outline.tintColor}
range:NSMakeRange(0, [title length])];
[self.outline setAttributedTitle:title forState:UIControlStateNormal];
}利用NSNotificationCenter让App中字体跟随系统字体,用户改变系统字体时,App中字体也随之变动。
首先实现一个将字体设置为系统对应字体的方法:
1
2
3
4- (void)usePreferredFonts {
self.body.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
self.headline.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
}并将其包装一层:
1
2
3- (void)preferredFontsChanged:(NSNotification *)notification {
[self usePreferredFonts];
}在生命周期
viewWillAppear()
中添加NotificationCenter1
2
3
4
5
6
7- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(preferredFontsChanged:)
name:UIContentSizeCategoryDidChangeNotification
object:nil];
}然后记得关闭radio station
1
2
3
4
5
6
7- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// [[NSNotificationCenter defaultCenter] removeObserver:self]; // 这种方法也可以,删除所有的notification,但不够优雅,尽量使用下面的方法,指定具体的notificationCenter
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIContentSizeCategoryDidChangeNotification
object:nil];
}最后要考虑一种情况,NotificationCenter在view消失时被关闭,如果在这之后用户修改了系统的字体,那么view再次appear时,App字体会和系统不一致。因此,需要在
viewWillAppear
中,调用[self usePreferredFonts]
来同步外部世界的字体。