Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

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

$
0
0

前言

掌握:
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

作者:u011018979 发表于2017/6/27 19:13:02 原文链接
阅读:16 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>