您的位置:首页 > 其它

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: