day08&09-通知机制(QQ聊天界面&QQ好友列表)

来源:互联网 发布:在线dns检测优化 编辑:程序博客网 时间:2024/05/19 05:34

前言

掌握:
0>图片拉伸:确定可拉伸、平铺的的小矩形inset。确定图片可调整的小矩形--resizableImageWithCapInsets:

1》通知的发布
2》通知的监听
3》通知的移除

零、通知和代理的选择

1》共同点:都能饿按此对象间的通信
2》不同点:代理是一对一关系;通知是多对多的关系(1个对象能告诉N个对象发生了什么事情,1个对象能得知N个对象发生了什么事情)

一、通知中心(NSNotificationCenter)
每个应用程序都有一个通知中心:专门负责协助不同对象间的消息通信
1.任何一个对象都可以向通知中心发布通知(NSNotification),来描述自己在做什么事情。
其他感兴趣的监听器(Observer)可以申请在某个特定通知发布时(或者某个特定的对象发布通知时),收到这个通知。

这里写图片描述

二、通知(NSNotification)

1.一个完整的通知通常包含三个属性:通知名称name、通知的发布者、一些额外的信息UserInfo
2.初始化一个通知对象
3.发布通知(通知中心NSNotificationCenter 提供了相应的方法来帮助发布通知)

三、注册通知监听器
NSNotificationCenter 提供了相应方法来注册一个监听通知的监听器(observer)

- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject;/**observer:监听器,即谁要接收这个通知;aSelector:收到通知后,回调监听器的这个方法,并且把通知对象当做参数传入;aName:通知的名称。如果为nil,那么无论通知的名称是什么,监听器都能收到这个通知;anObject:通知的发布者。如果为anObject和aName都为nil,监听器都收到所有的通知
- (id)addObserverForName:(NSString*)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification* note))block;/**name:通知的名称;obj:通知发布者;block:收到对应的通知时,会回调这个block;queue:决定了block在哪个操作队列中执行,如果传nil,默认在当前操作队列中同步执行;

四、取消注册通知监听器
通知中心不会retain监听器对象,在通知中心注册过的对象,必须在该对象释放前取消注册;否则,当相应的通知再次出现时,通知中心仍然会向该监听器发送消息,可能会导致应用的崩溃。— 尤其再iOS8,7 系统经常发生
1。NSNotificationCenter 提供了相应的方法来取消注册observer。

- (void)removeObserver:(id)observer;- (void)removeObserver:(id)observer name:(NSString *)aName object:(id)anObject;//一般在监听器销毁之前取消注册(如在监听器中加入下列代码):- (void)dealloc{  //[super dealloc];  非ARC中需要调用此句   [[NSNotificationCenter defaultCenter] removeObserver:self];}

ARC的介绍

/ ARC 是编译器特性,而不是IOS运行时特性,它也不类似于其他语言的垃圾收集器(GC)**

////  Person.m//  08-Strong&weak&assign////  Created by devzkn on 2/28/16.//  Copyright © 2016 devzkn. All rights reserved.//#import "Person.h"@implementation Person/** ARC 是编译器特性,而不是IOS运行时特性,它也不类似于其他语言的垃圾收集器(GC) 1)ARC的规则:只要还有一个变量指向对象,对象就会保持在内存中-- 默认所有的实例变量和局部变量都是strong指针,因为它们能保持对象的生命。 2)OC中有强参照strong和弱参照weak。--weak型的指针变量仍然可以指向一个对象,但不属于这个对象的拥有者;weak指针主要用于父子关系,即父亲拥有一个儿子的strong指针,儿子需使用weak指针指向父亲。 典型的例子: 你的ViewControl通过strong指针(self.view)拥有一个UITableView,UITableView的dataSource和delegate都是weak指针,指向你的viewControl @property(null_resettable, nonatomic,strong) UIView *view; @property (nonatomic, weak, nullable) id <UITableViewDelegate> delegate; 3)在ARC(代码的一种静态分析工具)中dealloc 主要用于调试,判断对象被释放。可以用来管理一些资源, *** an implementation of dealloc, do not invoke the superclass’s implementation 不能调用[super dealloc] *** 在ARC下父类的dealloc同样由编译器自动完成,不能用来释放实例变量;4)在ARC中考虑的是对象之间的关联,也就是那个对象拥有哪个对象。--无论何时你创建一个对象时,都要考虑谁该拥有它,以及这个对象需要存活多久 5)ARC只能工作于OC。 如果应用了core foundation,malloc()、free(),此时还是需要你来手动管理 */- (void) dealloc{    NSLog(@"Person %@被释放  %s ",self.name,__FUNCTION__);}@end

五、UIDevice 通知
UIDevice类提高了一个单例对象,它代表着设备;通过它可以获取一些设备相关的信息(batteryLevel、batteryState、model、systemVersion)
获取单例对象 Collapse source
1
•通过[UIDevice currentDevice]可以获取这个单粒对象
1.UIDevice对象会不间断的发布一些通知

六、键盘通知

我们经常需要在键盘弹出或者隐藏的时候做一些特定的操作,因此需要监听键盘的状态
1、键盘状态改变的时候,系统会发出一些特色的通知

//1. 注册Observer[center addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];

2.系统发出键盘通知的时候,会附带一下跟键盘有关的额外字典信息
3. 通知处理示例

/** 处理键盘通知 处理好view的frame和键盘frame的关系--移动view */- (void)keyboardWillChangeFrame: (NSNotification *) notification{    NSLog(@"%@",notification.userInfo);    //修改UIWindow的背景颜色    //    UIWindow  *keyWindow =[UIApplication sharedApplication].keyWindow;    [self.view.window setBackgroundColor:self.tableView.backgroundColor];    //键盘的弹出完成时的frame    UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 227}, {320, 253}}";    //键盘隐藏结束之后的frame   UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 480}, {320, 253}}";    CGRect keyBoardFrame  = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];    //2.动画平移view控件   // UIKeyboardAnimationDurationUserInfoKey = "0.25";    //1>获取动画的持续时间    CGFloat keyBoardAnimationDuration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue];    //2>计算平移的y值    CGFloat keyBoardY =  keyBoardFrame.origin.y ;//键盘的实时y值    CGFloat y = keyBoardY- KScreenHeight;//平移的y值    [UIView animateWithDuration:keyBoardAnimationDuration animations:^{        [self.view setTransform:CGAffineTransformMakeTranslation(0, y)];//设置平移的x、y值    }];}#pragma mark - scrollView 的代理方法- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{    NSLog(@"%s",__func__);    //关闭键盘,此时view的frame要还原    [self.view endEditing:YES];}

正文

一、QQ界面的消息布局
1、消息按钮的背景图片ImageView、titleLabel,以及button本身之间的大小关系处理

这里写图片描述

这里写图片描述

这里写图片描述

#if 1- (void)setTextViewFrame{    //文本消息的大小    CGRect textFrame = [self.message.text boundingRectWithSize:CGSizeMake(KScreenWidth*0.5, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:KtextFont} context:nil];    textFrame.origin.x = (self.message.type == HSMessageModelKevin) ? ( KScreenWidth-CGRectGetWidth(self.iconViewFrame)-KPading*2- CGRectGetWidth(textFrame)-KButtonPading*2):  (KPading*2+CGRectGetWidth(self.iconViewFrame));    textFrame.origin.y =CGRectGetMinY(self.iconViewFrame)+KPading;////加个KPading可以让相同时间的cell更好看    //button 的大小    CGRect buttonFrane = textFrame;    buttonFrane.size.width += KButtonPading*2;    buttonFrane.size.height += KButtonPading*2;    _textViewFrame = buttonFrane;}#endif
[_textView setContentEdgeInsets:UIEdgeInsetsMake(KButtonPading, KButtonPading, KButtonPading, KButtonPading)];
#pragma mark - 获取可拉伸图片--确定填充“拉伸之后的空白区域”的小矩形- (UIImage *)resizableImageWithImage:(UIImage *) image{    CGFloat widthForTopORBottom = image.size.width*0.5f -1;//inset的top、bottom    CGFloat heightForLeftORRight = image.size.height*0.5f -1;//inset的left、right    UIImage *resizableImage = [image resizableImageWithCapInsets:UIEdgeInsetsMake(heightForLeftORRight, widthForTopORBottom, heightForLeftORRight, widthForTopORBottom)];    return resizableImage;}

二、键盘处理

  [center addObserver:self selector:@selector(keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];//监听键盘通知#pragma mark - scrollView 的代理方法- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{    NSLog(@"%s",__func__);    //关闭键盘    [self.view endEditing:YES];}
/** 键盘弹出的frame2016-03-28 09:41:27.498 20160525-QQinterface[842:16465] NSConcreteNotification 0x7b274c00 {name = UIKeyboardDidChangeFrameNotification; userInfo = {    UIKeyboardAnimationCurveUserInfoKey = 7;    UIKeyboardAnimationDurationUserInfoKey = "0.25";    UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 253}}";    UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 606.5}";    UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 353.5}";    UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 480}, {320, 253}}";    UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 227}, {320, 253}}";    UIKeyboardIsLocalUserInfoKey = 1;}} *//** 关闭键盘时候的frame信息*/2016-03-28 09:41:30.775 20160525-QQinterface[842:16465] -[ViewController scrollViewWillBeginDragging:]2016-03-28 09:41:31.287 20160525-QQinterface[842:16465] NSConcreteNotification 0x7b21cae0 {name = UIKeyboardDidChangeFrameNotification; userInfo = {    UIKeyboardAnimationCurveUserInfoKey = 7;    UIKeyboardAnimationDurationUserInfoKey = "0.25";    UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 253}}";    UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 353.5}";    UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 606.5}";    UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 227}, {320, 253}}";    UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 480}, {320, 253}}";    UIKeyboardIsLocalUserInfoKey = 1;}}
b 、处理键盘的属性--谁负责唤起键盘,谁去修改键盘属性信息
UIKeyboardWillChangeFrameNotification object:nil];    //2.处理textFieldView的输入控件    self.textFieldView.leftView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 8, 0)];    self.textFieldView.leftViewMode = UITextFieldViewModeWhileEditing;    //3.键盘信息处理原则:谁触发键盘弹出的,谁去负责处理键盘的属性    [self.textFieldView setReturnKeyType:UIReturnKeySend];    [self.textFieldView setEnablesReturnKeyAutomatically:YES];// indicating whether the Return key is automatically enabled when the user is entering text

三、自动回复的实现

#pragma mark - textFieldView delegate方法/** 处理文本提交的信息,修改数据模型,刷新tableview */- (BOOL)textFieldShouldReturn:(UITextField *)textField{    NSString *fieldText = textField.text;//保存信息,避免文本控件的信息的清除    //1.修改模型数据    [self addMessageFrames:fieldText type:HSMessageModelKevin];    //2.关闭键盘//    [self.textFieldView endEditing:YES];    //3.设置自动回复--从autoReplay.plist进行匹配,找到对应的回复信息    [self autoReplay:fieldText];    return YES;}/**  自动回复*/- (void)autoReplay: (NSString *) fieldText{    if (fieldText.length == 0) {        return;    }    NSString *repplayText = @"系统正忙。。。。";//默认回复    for (int i =0; i < fieldText.length; i++) {        NSString *subString = [fieldText substringWithRange:NSMakeRange(i, 1)];        NSLog(@"%@",subString);        if (self.autoReplayDict[subString]) {            repplayText = self.autoReplayDict[subString];            break;        }    }    [self addMessageFrames:repplayText type:HSMessageModelLydia];}#pragma mark - 数据模型的构建方法- (void) addMessageFrames:(NSString *)text type:(HSMessageModelType)type{    if (0 == text.length) {        return;    }    //1> 或者上一个frame模型数据    HSMessageFrameModel *previousFrame = [self.messageFrames lastObject];    HSMessageModel *message = [[HSMessageModel alloc]init];    [message setText:text];//消息内容    [message setTime:@"2:00"];//消息发送时间    [message setIsHidenTimeWithPreviousMessage:previousFrame.message currentMessage:message];    //设置是否显示时间属性    [message setType:type];//消息的发送者    [self.messageFrames addObject:[HSMessageFrameModel messageFrameMessage:message]];//添加到frame模型数组    //2.数据刷新    NSIndexPath *indexpath = [NSIndexPath indexPathForItem:(self.messageFrames.count-1) inSection:0];    [self.tableView insertRowsAtIndexPaths:@[indexpath] withRowAnimation:UITableViewRowAnimationAutomatic];    //tableView 进行上滚一行    [self.tableView scrollToRowAtIndexPath:indexpath atScrollPosition:UITableViewScrollPositionTop animated:YES];//UITableViewScrollPosition 滚动方向    //3.清空输入框的内容    [self.textFieldView setText:nil];}

QQ好友列表的分组数据刷新的实现方式

1、代理
2、通知

 (void)viewDidLoad {    [super viewDidLoad];    /**     使用通知实现分组数据的加载     */    //注册通知    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(headerViewForclickNotification:) name:@"headerViewForclickNotification" object:nil];}- (void)dealloc{    //移除通知    [[NSNotificationCenter defaultCenter] removeObserver:self];    NSLog(@"%s",__func__);}
#if 1- (void)headerViewForclickNotification:(NSNotification *)notification{    //刷新分组数据    NSIndexSet *set = [NSIndexSet indexSetWithIndex:[notification.object section]];    [self.tableView reloadSections:(set) withRowAnimation:UITableViewRowAnimationAutomatic];}#endif
 (void) titleButtonViewClick: (HSHeaderView *) headerView{    //当分组的cell个数为0 的时候,即使合上,设置一个展开、合上标志属性,以便进行操作方法:(CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{    [self.friendsGroup setIsOpen:!self.friendsGroup.isOpen];    NSLog(@"%d",self.friendsGroup.isOpen);    //方式一、通知代理//    //先判断代理对象是否实现了代理方法//    if ([self.delegate respondsToSelector:@selector(headerViewForclick:)]) {//        [self.delegate headerViewForclick:self];////    }//    //方式二、block//    if (self.block) {//        self.block(self);//执行block//    }    //方式三、通知    //发布通知    [[NSNotificationCenter defaultCenter] postNotificationName:@"headerViewForclickNotification" object:self];}

3、block

/**  使用typeblock实现消息的传递*/typedef void(^HSHeaderViewBlockForClick)(id);//定义block,常常使用copy@property (nonatomic,copy) HSHeaderViewBlockForClick block;//属性定义/** 执行自己的block*/#pragma mark - titleButtonViewClick事件的处理/** 合并cell,展开cell的触发处理 */- (void) titleButtonViewClick: (HSHeaderView *) headerView{    //当分组的cell个数为0 的时候,即使合上,设置一个展开、合上标志属性,以便进行操作方法:(CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{    [self.friendsGroup setIsOpen:!self.friendsGroup.isOpen];    //方式一、通知代理//    //先判断代理对象是否实现了代理方法//    if ([self.delegate respondsToSelector:@selector(headerViewForclick:)]) {//        [self.delegate headerViewForclick:self];//    }    //方式二、block    if (self.block) {        self.block(self);//执行block    }}
#pragma mark - tableView datasource 协议方法#if 1/** 自定义UITableViewHeaderFooterView 关于控件没显示的经验小结: 1》父控件的frame 2》当前控件的frame 3》当前控件的hidden属性 4》当前控件的alpha<= 0.01 */- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{    HSFriendsGroup *friendsGroupModel = self.fridendsGroupArray[section];    HSHeaderView *headerView = [HSHeaderView tableHeaderViewWithFriendsGroup:friendsGroupModel TableView:tableView];    //设置代理对象//    [HeaderView setDelegate:self];    //设置block,定义具体实现    [headerView setBlock:^(HSHeaderView * view ){        //刷新分组数据        NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:section];        [tableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic];    }];    [headerView setSection:section];    return headerView;}#endif
三、QQ好友列表的分组headView的图片平移、旋转处理
#pragma mark - setTransform 处理//Tells the view that its superview changed. 当前的控件加载到父控件(tableview)的时候调用- (void)didMoveToSuperview{    NSLog(@"%s",__func__);    //2.对button的imageView的image进行平移、翻滚--先加载数据,在设置平移    CGFloat angle = (self.friendsGroup.isOpen) ? M_PI_2 :0;    [self.titleButtonView.imageView setTransform:CGAffineTransformMakeRotation(angle)];}//对imageView进行内容排列对齐方式、是否剪切超出部分进行设置  [_titleButtonView.imageView setContentMode:UIViewContentModeCenter];  [_titleButtonView.imageView setClipsToBounds:NO];//subviews are confined to the bounds of the view. 不剪切超出的部分

总结

零、 调试技巧

1.po 关键字的使用--打印内存对象

(lldb) po notificationNSConcreteNotification 0x10010eb20 {name = zhengaiwang; object = <HSMatchmakingCompany: 0x100206170>; userInfo = {    info = "....";    title = "\U65b0\U6765\U4e86\U4e00\U6279\U7f8e\U5973";}}
清空cell的颜色-》使用tableView的颜色  Collapse source12[cell setBackgroundColor:[UIColor clearColor]];[self.tableView setBackgroundColor:[UIColor colorWithRed:221/255.0 green:221/255.0 blue:221/255.0 alpha:1]];

二、问题分析
1.关于控件没显示的经验小结:
1》父控件的frame
2》当前控件的frame
3》当前控件的hidden属性
4》当前控件的alpha<= 0.01

原创粉丝点击