day08&09-通知机制(QQ聊天界面&QQ好友列表)
2017-06-27 19:13
344 查看
前言
掌握: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]; }
/** 键盘弹出的frame 2016-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 notification NSConcreteNotification 0x10010eb20 {name = zhengaiwang; object = <HSMatchmakingCompany: 0x100206170>; userInfo = { info = "...."; title = "\U65b0\U6765\U4e86\U4e00\U6279\U7f8e\U5973"; }}
清空cell的颜色-》使用tableView的颜色 Collapse source 1 2 [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
相关文章推荐
- ios-day08-01(模仿QQ聊天界面。ios通知的使用(对键盘状态的监听)、枚举和分类的使用、拉伸图片、计算文本字符串的尺寸
- ExpandableListView实现的简单类似QQ好友界面的可展开下拉列表
- 仿QQ聊天界面<一>
- IOS第十天(1:QQ好友列表 ,自定义的headview,代理 ,通知 ,black的使用)
- 【Java】仿真qq尝试:聊天界面 && 响应用户输入
- 仿QQ聊天(3)—好友列表的实现
- QQ聊天布局——利用通知机制实现键盘处理
- iOS-QQ好友列表 iOS 页面间几种传值方式(属性,代理,block,单例,通知)
- QQ好友的列表恢复与聊天记录的恢复
- WPF—QQ界面(五):QQ好友分组列表的效果实现 及 截稿
- 仿QQ好友列表界面的实现
- 遗忘qq密码后找回聊天记录和好友列表
- IOS第九天(3:QQ聊天界面通知的使用)
- 【iOS开发-65】QQ聊天界面案例:自定义cell、图片拉伸处理、NSNotification通知、键盘与视图移动以及输入框左边缩进处理
- 高仿QQ界面 局域网聊天与文件传输平台(演示 & C++源代码)
- 网页跳转QQ聊天界面和添加好友界面
- 观察者模式(Observer)与QQ中的好友登录机制
- 聊天不用找人 把QQ好友安置到桌面
- QQ互动服务|不加好友也可以聊天
- PHP导出QQ好友列表搞定,兴奋中。。。