随手整理3

30.通过代码自定cell,模型封装


1.提供两个模型, 一个用于存放数据, 一个用于存放每一个控件的fram值
第一个模型:   MJMessage(数据模型,用于存放从Plist文件加载的数据)
  typedef enum {
      MJMessageTypeMe = 0, // 自己发的
      MJMessageTypeOther   // 别人发的
   } MJMessageType;
    @property (nonatomic, copy) NSString *text;//聊天内容
    @property (nonatomic, copy) NSString *time;//发送时间
    @property (nonatomic, assign) MJMessageType type;//信息的类型
    @property (nonatomic, assign) BOOL hideTime;//是否隐藏时间
   //提供两个方法用于加载从plist中读取的字典,转换成模型
   + (instancetype)messageWithDict:(NSDictionary *)dict;
   - (instancetype)initWithDict:(NSDictionary *)dict;
   + (instancetype)messageWithDict:(NSDictionary *)dict
   {
    return [[self alloc] initWithDict:dict];
   }


   - (instancetype)initWithDict:(NSDictionary *)dict
   {
    if (self = [super init]) {//先初始化父类
        [self setValuesForKeysWithDictionary:dict];//通过KVC的方式,把字典转为模型
    }
    return self;
   }


第二个模型:MJMessageFrame(里面存入一个模型,然后通过模型计算出每个控件的frame)


@property (nonatomic, assign, readonly) CGRect iconF;//头像的frame
@property (nonatomic, assign, readonly) CGRect timeF;//时间的frame
@property (nonatomic, assign, readonly) CGRect textF;//正文的frame
@property (nonatomic, assign, readonly) CGFloat cellHeight;//cell的高度
@property (nonatomic, strong) MJMessage *message;//数据模型


重写MJMessage *message;的set方法,在set方法中, 计算每一个frame
- (void)setMessage:(MJMessage *)message
{
    _message = message;
    CGFloat padding = 10; // 间距
    CGFloat screenW = [UIScreen mainScreen].bounds.size.width;// 屏幕的宽度
    if (message.hideTime == NO) { // 显示时间
        CGFloat timeX = 0;
        CGFloat timeY = 0;
        CGFloat timeW = screenW;
        CGFloat timeH = 40;
        _timeF = CGRectMake(timeX, timeY, timeW, timeH);
    }
    
    // 2.头像
    CGFloat iconY = CGRectGetMaxY(_timeF) + padding;
    CGFloat iconW = 40;
    CGFloat iconH = 40;
    CGFloat iconX;
    if (message.type == MJMessageTypeOther) {// 别人发的
        iconX = padding;
    } else { // 自己的发的
        iconX = screenW - padding - iconW;
    }
    _iconF = CGRectMake(iconX, iconY, iconW, iconH);
    
    // 3.正文
    CGFloat textY = iconY;
    // 文字计算的最大尺寸
    CGSize textMaxSize = CGSizeMake(200, MAXFLOAT);
    // 文字计算出来的真实尺寸(按钮内部label的尺寸)
    CGSize textRealSize = [message.text sizeWithFont:MJTextFont maxSize:textMaxSize];
    // 按钮最终的真实尺寸
    CGSize textBtnSize = CGSizeMake(textRealSize.width + MJTextPadding * 2, textRealSize.height + MJTextPadding * 2);
    CGFloat textX;
    if (message.type == MJMessageTypeOther) {// 别人发的
        textX = CGRectGetMaxX(_iconF) + padding;
    } else {// 自己的发的
        textX = iconX - padding - textBtnSize.width;
    }
    _textF = (CGRect){{textX, textY}, textBtnSize};
    // 4.cell的高度
    CGFloat textMaxY = CGRectGetMaxY(_textF);
    CGFloat iconMaxY = CGRectGetMaxY(_iconF);
    _cellHeight = MAX(textMaxY, iconMaxY) + padding;
}

 1.新建一个类(MJMessageCell),继承自UITableViewCel
   在该类中提供一个类方法, 用于创建一个可循环利用的自定义cell
   再拥有一个MJMessageFrame模型, 用于设置每个Cell中的控件位置.
   + (instancetype)cellWithTableView:(UITableView *)tableView;
   @property (nonatomic, strong) MJMessageFrame *messageFrame;
  
   + (instancetype)cellWithTableView:(UITableView *)tableView
   {
    static NSString *ID = @"message";//创建一个全局的标识,用于从缓存中取出相同标识的是可循环利用Cell
    MJMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];//通过标识到缓存区中查看有没有可循环利用的cell
    if (cell == nil) {//如果为只的话,那么就自己重创建一个带有标识的自定义的cell
        cell = [[MJMessageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
    }
    return cell;//返回自定的cell
   }
  
  在构造方法中初始化cell中的每一个控件
  - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
  {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        // 子控件的创建和初始化
        // 1.时间
        UILabel *timeView = [[UILabel alloc] init];
        timeView.textAlignment = NSTextAlignmentCenter;
        timeView.textColor = [UIColor grayColor];
        timeView.font = [UIFont systemFontOfSize:13];
        [self.contentView addSubview:timeView];
        self.timeView = timeView;
        // 2.头像
        UIImageView *iconView = [[UIImageView alloc] init];
        [self.contentView addSubview:iconView];
        self.iconView = iconView;
        // 3.正文
        UIButton *textView = [[UIButton alloc] init];
        textView.titleLabel.numberOfLines = 0; // 自动换行
        textView.titleLabel.font = MJTextFont;
        textView.contentEdgeInsets = UIEdgeInsetsMake(MJTextPadding, MJTextPadding, MJTextPadding, MJTextPadding);
        [textView setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
        [self.contentView addSubview:textView];
        self.textView = textView;
        // 4.设置cell的背景色
        self.backgroundColor = [UIColor clearColor];
      }
    return self;
   }
  
 重写MJMessageFrame *messageFrame的set方法, 在set的方法中,给每一个控件设置数据,和frame
   - (void)setMessageFrame:(MJMessageFrame *)messageFrame
{
    _messageFrame = messageFrame;把传进的messageFrame赋值给本类的messageFrame属性
    MJMessage *message = messageFrame.message;//获得每个messageFrame模型,所对应的数据
    // 1.时间
    self.timeView.text = message.time;
    self.timeView.frame = messageFrame.timeF;
    // 2.头像
    NSString *icon = (message.type == MJMessageTypeMe) ? @"me" : @"other";
    self.iconView.image = [UIImage imageNamed:icon];
    self.iconView.frame = messageFrame.iconF;
    // 3.正文
    [self.textView setTitle:message.text forState:UIControlStateNormal];
    self.textView.frame = messageFrame.textF;
    
    // 4.正文的背景
    if (message.type == MJMessageTypeMe) { // 自己发的,蓝色
        [self.textView setBackgroundImage:[UIImage resizableImage:@"chat_send_nor"] forState:UIControlStateNormal];
    } else { // 别人发的,白色
        [self.textView setBackgroundImage:[UIImage resizableImage:@"chat_recive_nor"] forState:UIControlStateNormal];
    }
}

在控制器当中
@interface MJViewController () <UITableViewDataSource, UITableViewDelegate, UITableViewDelegate, UITextFieldDelegate>//遵守TableView相应的协议
@property (weak, nonatomic) IBOutlet UITableView *tableView;//UITableView属性
@property (nonatomic, strong) NSMutableArray *messageFrames;//存放所有的messageFrames模型
@property (weak, nonatomic) IBOutlet UITextField *inputView;
@property (nonatomic, strong) NSDictionary *autoreply;
@end

重写NSMutableArray *messageFrames;的get方法, 延迟加载
- (NSMutableArray *)messageFrames
{
    if (_messageFrames == nil) {//第一次加载时调用
        NSArray *dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"messages.plist" ofType:nil]];//从Plist文件中加载数据
        NSMutableArray *mfArray = [NSMutableArray array];//定义一个临时数组用来保存临时保存每一个messageFrames模型
        for (NSDictionary *dict in dictArray) {//遍历从plist文件中读出的数组
            MJMessage *msg = [MJMessage messageWithDict:dict];//传入字典给MJMessage中的每个属性赋值
            MJMessageFrame *lastMf = [mfArray lastObject]; // 取出上一个模型
            MJMessage *lastMsg = lastMf.message;//取出上一个frame模型的数据模型
            msg.hideTime = [msg.time isEqualToString:lastMsg.time];// 判断两个消息的时间是否一致            
            MJMessageFrame *mf = [[MJMessageFrame alloc] init];//frame模型
            mf.message = msg;//给frame模型中的数据模型赋值,就会调用frame中(MJMessage *)message的set方法,计算每一个控件的frame值
            [mfArray addObject:mf];// 把每一个frame添加到临时数组中
        }
        _messageFrames = mfArray;把临时数组组赋值给NSMutableArray *messageFrames;
    }
    return _messageFrames;//返回
}


控制器中
#pragma mark - 数据源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.messageFrames.count;//总共有多少行数据
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath//每行显示什么样的数据 
{
    MJMessageCell *cell = [MJMessageCell cellWithTableView:tableView];//调用MJMessageCell的类方法创建一个自定义的可循环利用的cell
    cell.messageFrame = self.messageFrames[indexPath.row];// 2.给cell传递模型,就会调会(MJMessageFrame *)messageFrame的set方法,给cell内部的每一个子控设置Frame
    return cell;// 3.返回cell
}


#pragma mark - 代理方法
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    MJMessageFrame *mf = self.messageFrames[indexPath.row];//取出当前的frame,
    return mf.cellHeight;//反回当前行的高度
}


31.监听键盘的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];

- (void)keyboardWillChangeFrame:(NSNotification *)note//当键盘改变了frame(位置和尺寸)的时候调用
{
    self.view.window.backgroundColor = self.tableView.backgroundColor;// 设置窗口的颜色
    CGFloat duration = [note.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];//取出键盘动画的时间
    CGRect keyboardFrame = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];//取得键盘最后的frame
    CGFloat transformY = keyboardFrame.origin.y - self.view.frame.size.height;//计算控制器的view需要平移的距离
    [UIView animateWithDuration:duration animations:^{// 3.执行动画
        self.view.transform = CGAffineTransformMakeTranslation(0, transformY);让控制的View平移
    }];
}
监听一个通知时, 必须在在dealloc中注销该通知,谨防对象销毁时,还向该对象发通知,造成野指针异常
- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

32.点击了return按钮(键盘最右下角的按钮)就会调用,使用此方法时, 要尊师UITextFieldDelegate协议,
- (BOOL)textFieldShouldReturn:(UITextField *)textField{   
    [self addMessage:textField.text type:MJMessageTypeMe];// 1.自己发一条消息
    NSString *reply = [self replayWithText:textField.text];// 2.自动回复一条消息
    [self addMessage:reply type:MJMessageTypeOther];    
    self.inputView.text = nil;// 3.清空文字
    return YES;// 返回YES即可
}

32.自动滚动表格到最后一行
 NSIndexPath *lastPath = [NSIndexPath indexPathForRow:self.messageFrames.count - 1 inSection:0];
[self.tableView scrollToRowAtIndexPath:lastPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];

33.格式化日期
    NSDate *now = [NSDate date];
    NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
    fmt.dateFormat = @"HH:mm";
    // NSDate  --->  NSString
    // NSString ---> NSDate
    // fmt.dateFormat = @"yyyy-MM-dd HH:mm:ss";
    // 2014-08-09 15:45:56
    // 09/08/2014  15:45:56
    msg.time = [fmt stringFromDate:now];

34.返回每一组需要显示的头部标题(字符出纳)
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    // 1.创建头部控件
    MJHeaderView *header = [MJHeaderView headerViewWithTableView:tableView];
    header.delegate = self;
    // 2.给header设置数据(给header传递模型)
    header.group = self.groups[section];
    return header;
}

+ (instancetype)headerViewWithTableView:(UITableView *)tableView
{
    static NSString *ID = @"header";
    MJHeaderView *header = [tableView dequeueReusableHeaderFooterViewWithIdentifier:ID];
    if (header == nil) {
        header = [[MJHeaderView alloc] initWithReuseIdentifier:ID];
    }
    return header;
}

- (id)initWithReuseIdentifier:(NSString *)reuseIdentifier//在这个初始化方法中,MJHeaderView的frame\bounds没有值
{
    if (self = [super initWithReuseIdentifier:reuseIdentifier]) {
        // 添加子控件
        nameView.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;//设置按钮的内容左对齐
        nameView.titleEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0);// 设置按钮的内边距
        nameView.contentEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0);
        [nameView addTarget:self action:@selector(nameViewClick) forControlEvents:UIControlEventTouchUpInside];
        nameView.imageView.contentMode = UIViewContentModeCenter;// 设置按钮内部的imageView的内容模式为居中
        nameView.imageView.clipsToBounds = NO;// 超出边框的内容不需要裁剪
    }
    return self;
}

- (void)layoutSubviews//当一个控件的frame发生改变的时候就会调用,一般在这里布局内部的子控件(设置子控件的frame)
{
    [super layoutSubviews];//一定要调用super的方法
    // 1.设置按钮的frame
    self.nameView.frame = self.bounds;
    // 2.设置好友数的frame
    CGFloat countY = 0;
    CGFloat countH = self.frame.size.height;
    CGFloat countW = 150;
    CGFloat countX = self.frame.size.width - 10 - countW;
    self.countView.frame = CGRectMake(countX, countY, countW, countH);
}


- (void)didMoveToSuperview//当一个控件被添加到父控件中就会调用
{
    if (self.group.opened) {
        self.nameView.imageView.transform = CGAffineTransformMakeRotation(M_PI_2);
    } else {
        self.nameView.imageView.transform = CGAffineTransformMakeRotation(0);
    }
}

- (void)willMoveToSuperview:(UIView *)newSuperview//当一个控件即将被添加到父控件中会调用
 {   }

35.Info.plist常见的设置
  建立一个工程后,会在Supporting files文件夹下看到一个“工程名-Info.plist”的文件,该文件对工程做一些运行期的配置,非常重要,不能删除
  在旧版本Xcode创建的工程中,这个配置文件的名字就叫“Info.plist”
  项目中其他Plist文件不能带有“Info”这个字眼,不然会被错认为是传说中非常重要的“Info.plist”
  项目中还有一个InfoPlist.strings的文件,跟Info.plist文件的本地化相关
   常见属性(红色部分是用文本编辑器打开时看到的key)
   Localiztion native development region(CFBundleDevelopmentRegion)-本地化相关
   Bundle display name(CFBundleDisplayName)-程序安装后显示的名称,限制在10-12个字符,如果超出,将被显示缩写名称
   Icon file(CFBundleIconFile)-app图标名称,一般为Icon.png
   Bundle version(CFBundleVersion)-应用程序的版本号,每次往App Store上发布一个新版本时,需要增加这个版本号
   Main storyboard file base name(NSMainStoryboardFile)-主storyboard文件的名称
   Bundle identifier(CFBundleIdentifier)-项目的唯一标识,部署到真机时用到

36.PHC文件
 项目的Supporting files文件夹下面有个“工程名-Prefix.pch”文件,也是一个头文件
 pch头文件的内容能被项目中的其他所有源文件共享和访问
 一般在pch文件中定义一些全局的宏
 在pch文件中添加下列预处理指令,然后在项目中使用Log(…)来输出日志信息,就可以在发布应用的时候,一次性将NSLog语句移除(在调试模式下,才有定义DEBUG)
 #ifdef DEBUG
 #define Log(...) NSLog(__VA_ARGS__)
 #else
 #define Log(...) /* */
 #endif

37.UIApplication
 UIApplication对象是应用程序的象征
 每一个应用都有自己的UIApplication对象,而且是单例的
 通过[UIApplication sharedApplication]可以获得这个单例对象
 一个iOS程序启动后创建的第一个对象就是UIApplication对象
 利用UIApplication对象,能进行一些应用级别的操作

38.UIApplication的常用属性
   @property(nonatomic) NSInteger applicationIconBadgeNumber;//设置应用程序图标右上角的红色提醒数字
   @property(nonatomic,getter=isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible;//设置联网指示器的可见性

39.iOS7中的状态栏
   从iOS7开始,系统提供了2种管理状态栏的方式
   1.通过UIViewController管理(每一个UIViewController都可以拥有自己不同的状态栏)
   2.通过UIApplication管理(一个应用程序的状态栏都由它统一管理)
   在iOS7中,默认情况下,状态栏都是由UIViewController管理的,UIViewController实现下列方法就可以轻松管理状态栏的可见性和样式
   - (UIStatusBarStyle)preferredStatusBarStyle; //状态栏的样式
   - (BOOL)prefersStatusBarHidden; //状态栏的可见性

40.openURL
  UIApplication *app = [UIApplication sharedApplication];
  [app openURL:[NSURL URLWithString:@"tel://10086"]];//打电话
  [app openURL:[NSURL URLWithString:@"sms://10086"]];//发短信
  [app openURL:[NSURL URLWithString:@"mailto://12345@qq.com"]];//发邮件
  [app openURL:[NSURL URLWithString:@"http://ios.itcast.cn"]];//打开一个网页资源

41.UIApplication和delegate
  在app受到干扰时,会产生一些系统事件,这时UIApplication会通知它的delegate对象,让delegate代理来处理这些系统事件
  - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application;// app接收到内存警告时调用
  - (void)applicationDidEnterBackground:(UIApplication *)application;// app进入后台时调用(比如按了home键)
  - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;// app启动完毕时调用
  每次新建完项目,都有个带有“AppDelegate”字眼的类,它就是UIApplication的代理
  AppDelegate默认已经遵守了UIApplicationDelegate协议,已经是UIApplication的代理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值