这节课主要讲解:
- 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()中添加NotificationCenter- 1 
 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]来同步外部世界的字体。