01

一、包装为导航控制器

  1. UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];

二、自定义tabbar,布局子控件,添加加号按钮

  1. /**
  2. * 布局子控件
  3. */
  4. - (void)layoutSubviews
  5. {
  6. [super layoutSubviews];
  7.  
  8. // 设置发布按钮的位置
  9. self.publishButton.center = CGPointMake(self.frame.size.width * 0.5, self.frame.size.height * 0.5);
  10.  
  11. // 按钮索引
  12. int index = ;
  13.  
  14. // 按钮的尺寸
  15. CGFloat tabBarButtonW = self.frame.size.width / ;
  16. CGFloat tabBarButtonH = self.frame.size.height;
  17. CGFloat tabBarButtonY = ;
  18.  
  19. // 设置4个TabBarButton的frame
  20. for (UIView *tabBarButton in self.subviews) {
  21. // if (![tabBarButton isKindOfClass:NSClassFromString(@"UITabBarButton")]) continue;
  22. if (![NSStringFromClass(tabBarButton.class) isEqualToString:@"UITabBarButton"]) continue;
  23.  
  24. // 计算按钮的X值
  25. CGFloat tabBarButtonX = index * tabBarButtonW;
  26. if (index >= ) { // 给后面2个button增加一个宽度的X值
  27. tabBarButtonX += tabBarButtonW;
  28. }
  29.  
  30. // 设置按钮的frame
  31. tabBarButton.frame = CGRectMake(tabBarButtonX, tabBarButtonY, tabBarButtonW, tabBarButtonH);
  32.  
  33. // 增加索引
  34. index++;
  35. }
  36. }

三、设置导航控制器的UIBarButtonItem,封装到UIBarButtonItem的分类

  1. + (instancetype)itemWithImage:(NSString *)image highImage:(NSString *)highImage target:(id)target action:(SEL)action
  2. {
  3. UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
  4. [button setBackgroundImage:[UIImage imageNamed:image] forState:UIControlStateNormal];
  5. [button setBackgroundImage:[UIImage imageNamed:highImage] forState:UIControlStateHighlighted];
  6. [button sizeToFit];
  7. [button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
  8.  
  9. return [[self alloc] initWithCustomView:button];
  10. }

那么设置UIBarButtonItem时

  1. // 导航栏右边的内容
  2. UIBarButtonItem *moonItem = [UIBarButtonItem itemWithImage:@"mine-moon-icon" highImage:@"mine-moon-icon-click" target:self action:@selector(moonClick)];
  3. UIBarButtonItem *settingItem = [UIBarButtonItem itemWithImage:@"mine-setting-icon" highImage:@"mine-setting-icon-click" target:self action:@selector(settingClick)];
  4. self.navigationItem.rightBarButtonItems = @[settingItem, moonItem];

四、隐藏tabbar

  1. CHGSettingViewController *setting = [[CHGSettingViewController alloc] init];
  2. setting.hidesBottomBarWhenPushed = YES; // 当push这个控制器时,会自动隐藏底部的工具条
  3. [self.navigationController pushViewController:setting animated:YES];

五、自定义导航控制器,拦截pushViewController:animated方法统一修改push的控制器的返回键

  1. - (void)viewDidLoad {
  2. [super viewDidLoad];
  3.  
  4. self.interactivePopGestureRecognizer.delegate = self;
  5. }
  6.  
  7. /**
  8. * 拦截所有push进来的子控制器
  9. * @param viewController 每一次push进来的子控制器
  10. */
  11. - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
  12. {
  13. // if (不是第一个push进来的子控制器) {
  14. if (self.childViewControllers.count >= ) {
  15. // 左上角的返回
  16. UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
  17. [backButton setTitle:@"返回" forState:UIControlStateNormal];
  18. [backButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
  19. [backButton setTitleColor:[UIColor redColor] forState:UIControlStateHighlighted];
  20. [backButton setImage:[UIImage imageNamed:@"navigationButtonReturn"] forState:UIControlStateNormal];
  21. [backButton setImage:[UIImage imageNamed:@"navigationButtonReturnClick"] forState:UIControlStateHighlighted];
  22.  
  23. // button.size = CGSizeMake(70, 30);
  24. // 让按钮内部的所有内容左对齐 按钮独特的一个属性 这样可以可以做到调整按钮大小 并且内容左对齐
  25. // button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
  26.  
  27. // 自动计算按钮大小
  28. [backButton sizeToFit];
  29.  
  30. [backButton addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside];
  31. backButton.contentEdgeInsets = UIEdgeInsetsMake(, -, , );
  32. viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
  33. viewController.hidesBottomBarWhenPushed = YES; // 隐藏底部的工具条
  34. }
  35.  
  36. // super的push方法一定要写到最后面
  37. // 一旦调用super的pushViewController方法,就会创建子控制器viewController的view
  38. // 也就会调用viewController的viewDidLoad方法
  39. [super pushViewController:viewController animated:animated];
  40. }
  41.  
  42. - (void)back
  43. {
  44. [self popViewControllerAnimated:YES];
  45. }
  46.  
  47. #pragma mark - <UIGestureRecognizerDelegate>
  48. /**
  49. * 每当用户触发[返回手势]时都会调用一次这个方法
  50. * 返回值:返回YES,手势有效; 返回NO,手势失效
  51. */
  52. - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
  53. {
  54. // 如果当前显示的是第一个子控制器,就应该禁止掉[返回手势]
  55. // if (self.childViewControllers.count == 1) return NO;
  56. // return YES;
  57. return self.childViewControllers.count > ;
  58. }

02

补充知识

## 零、颜色须知
- 1> 每一种颜色都是由N个颜色通道组成
- 2> 常见的颜色通道
- 1) A:alpha 透明度
- 2) R:red 红色
- 3) G:green 绿色
- 4) B:blue 蓝色
- 3> 常见颜色
* 白色:全部通道满值
* 黑色:全部通道都是0(透明度除外)
* 灰色:RGB通道的值一样

## 一、32bit颜色
1> 颜色组成
- 1) 由ARGB四个颜色通道组成
- 2) 每一个颜色通道都占据8bit
- 3) 每一个颜色通道的取值范围是[0, 255] [0x00, 0xff] [0b00000000, 0b11111111]

2> 颜色的表示格式
- 1) 16进制格式(HEX格式)
* 绿色 #ff00ff00
* 黄色 #ffffff00
* 白色 #ffffffff
* 黑色 #ff000000

2) ARGB格式
* 绿色 255,0,255,0
* 黄色 255,255,255,0
* 白色 255,255,255,255
* 黑色 255,0,0,0

## 二、24bit颜色
1> 颜色组成
- 1) 由RGB三个颜色通道组成
- 2) 每一个颜色通道都占据8bit
- 3) 每一个颜色通道的取值范围是[0, 255] [0x00, 0xff] [0b00000000, 0b11111111]

2> 颜色的表示格式
- 1) 16进制格式(HEX格式)
* 绿色 #00ff00
* 黄色 #ffff00
* 白色 #ffffff
* 黑色 #000000

- 2) RGB格式
* 绿色 0,255,0
* 黄色 255,255,0
* 白色 255,255,255
* 黑色 0,0,0

## 三、12bit颜色
1> 颜色组成
- 1) 由RGB三个颜色通道组成
- 2) 每一个颜色通道都占据4bit
- 3) 每一个颜色通道的取值范围是[0, 15] [0x0, 0xf] [0b0000, 0b1111]

2> 颜色的表示格式
- 1) 16进制格式(HEX格式)
* 绿色 #0f0
* 黄色 #ff0
* 白色 #fff
* 黑色 #000

- 2) RGB格式
* 绿色 15
* 黄色 15,15,0
* 白色 15,15,15
* 黑色 0,0,0

## 其它
- 红色的表示方式
- \#ffff0000
- \#ff0000
- \#f00

一、三种设置圆角的方法

  1. self.loginButton.layer.cornerRadius = ;
  2. self.loginButton.layer.masksToBounds = YES;
  3.  
  4. self.loginButton.layer.cornerRadius = ;
  5. self.loginButton.clipsToBounds = YES;

第三种方法:

  1. // [self.loginButton setValue:@5 forKeyPath:@"layer.cornerRadius"];
  2. // [self.loginButton setValue:@YES forKeyPath:@"layer.masksToBounds"];

在sb中利用KVC设置上面这两个值

二、修改状态栏样式

// iOS7之前修改状态栏样式
[UIApplication sharedApplication].statusBarStyle;

// iOS7开始由控制器来修改状态栏样式
/**
* 让状态栏样式为白色
*/
- (UIStatusBarStyle)preferredStatusBarStyle
{
  return UIStatusBarStyleLightContent;
}

三、富文本用法

富文本用法1 - 不可变的属性文字

  1. NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
  2. attrs[NSForegroundColorAttributeName] = [UIColor grayColor];
  3. attrs[NSUnderlineStyleAttributeName] = @;
  4. attrs[NSUnderlineColorAttributeName] = [UIColor redColor];
  5. self.attributedPlaceholder = [[NSAttributedString alloc] initWithString:self.placeholder attributes:attrs];

富文本用法2 - 可变的属性文字

  1. NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:self.placeholder];
  2. [string addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(, )];
  3. [string addAttribute:NSForegroundColorAttributeName value:[UIColor greenColor] range:NSMakeRange(, )];
  4. [string addAttribute:NSFontAttributeName value:[UIFont boldSystemFontOfSize:] range:NSMakeRange(, )];
  5. self.attributedPlaceholder = string;

富文本用法3 - 图文混排

  1. NSMutableAttributedString *string = [[NSMutableAttributedString alloc] init];
  2.  
  3. // 第二段:图片
  4. NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
  5. attachment.image = [UIImage imageNamed:@"login_close_icon"];
  6. attachment.bounds = CGRectMake(, , , );
  7. NSAttributedString *subtring2 = [NSAttributedString attributedStringWithAttachment:attachment];
  8. [string appendAttributedString:subtring2];
  9.  
  10. // 第一段:placeholder
  11. NSAttributedString *substring1 = [[NSAttributedString alloc] initWithString:self.placeholder];
  12. [string appendAttributedString:substring1];
  13.  
  14. // 第三段:哈哈
  15. NSAttributedString *substring3 = [[NSAttributedString alloc] initWithString:@"哈哈"];
  16. [string appendAttributedString:substring3];
  17.  
  18. self.attributedPlaceholder = string;

四、textField的设置

文本框的属性设置

  1. // 文本框的光标颜色
  2. self.tintColor = [UIColor whiteColor];
  3. // 文字颜色
  4. self.textColor = [UIColor whiteColor];
  5. // 设置带有属性的占位文字(富文本)
  6. self.attributedPlaceholder = [[NSAttributedString alloc] initWithString:self.placeholder attributes:@{NSForegroundColorAttributeName : [UIColor grayColor]}];

占位文字位置设置方法一

  1. // 占位文字画在哪个位置
  2. CGPoint point;
  3. point.x = ;
  4. point.y = (self.height - self.font.lineHeight) * 0.5;
  5.  
  6. // 文字属性
  7. NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
  8. attrs[NSForegroundColorAttributeName] = [UIColor redColor];
  9. attrs[NSFontAttributeName] = self.font;
  10. [self.placeholder drawAtPoint:point withAttributes:attrs];

占位文字位置设置方法二

  1. // 占位文字画在哪个矩形框里面
  2. CGRect placeholderRect = self.bounds;
  3. placeholderRect.origin.y = (self.height - self.font.lineHeight) * 0.5;
  4.  
  5. // 文字属性
  6. NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
  7. attrs[NSForegroundColorAttributeName] = [UIColor redColor];
  8. attrs[NSFontAttributeName] = self.font;
  9. [self.placeholder drawInRect:placeholderRect withAttributes:attrs];

五、运行时(Runtime)

1.什么是运行时(Runtime)?
* 运行时是苹果提供的纯C语言的开发库(运行时是一种非常牛逼、开发中经常用到的底层技术)

2.运行时的作用?
* 能获得某个类的所有成员变量
* 能获得某个类的所有属性
* 能获得某个类的所有方法
* 交换方法实现
* 能动态添加一个成员变量
* 能动态添加一个属性
* 能动态添加一个方法

3、获得一个类的所有属性的方法(以UITextField为例)

  1. // 成员变量的数量
  2. unsigned int outCount = ;
  3.  
  4. // 获得所有的成员变量
  5. Ivar *ivars = class_copyIvarList([UITextField class], &outCount);
  6.  
  7. // 遍历所有的成员变量
  8. for (int i = ; i<outCount; i++) {
  9. // 取出i位置对应的成员变量
  10. Ivar ivar = ivars[i];
  11. // 获得成员变量的名字
  12. NSLog(@"%s", ivar_getName(ivar));
  13. }
  14.  
  15. // 如果函数名中包含了copy\new\retain\create等字眼,那么这个函数返回的数据就需要手动释放
  16. free(ivars);

作用:
当你不确定这个类中有哪些属性,可以查看,而且利用kvc可以给一些隐藏起来的私有成员变量赋值

  1. // 设置占位文字颜色
  2. [self setValue:[UIColor grayColor] forKeyPath:@"placeholderLabel.textColor"];

六、监听textfield的编辑状态

方法一:

  1. // 通过addTarget:-》监听文本框的开始和结束编辑
  2. [self addTarget:self action:@selector(beginEditing) forControlEvents:UIControlEventEditingDidBegin];
  3. [self addTarget:self action:@selector(endEditing) forControlEvents:UIControlEventEditingDidEnd];

方法二:
设置自己为代理

  1. // 这种做法不推荐,因为delegate属性很容易被外界覆盖
  2. self.delegate = self;
  3.  
  4. #pragma mark - <UITextFieldDelegate>
  5. - (void)textFieldDidBeginEditing:(UITextField *)textField
  6. {
  7. [self setValue:[UIColor whiteColor] forKeyPath:@"placeholderLabel.textColor"];
  8. }
  9.  
  10. - (void)textFieldDidEndEditing:(UITextField *)textField
  11. {
  12. [self setValue:[UIColor grayColor] forKeyPath:@"placeholderLabel.textColor"];
  13. }

方法三:

通过通知->监听文本框的开始和结束编辑

  1. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(beginEditing) name:UITextFieldTextDidBeginEditingNotification object:self];
  2. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(endEditing) name:UITextFieldTextDidEndEditingNotification object:self];
  3.  
  4. - (void)dealloc
  5. {
  6. [[NSNotificationCenter defaultCenter] removeObserver:self];
  7. }

方法四

巧用

becomeFirstResponder

resignFirstResponder

  1. #define CHGPlaceholderColorKey @"placeholderLabel.textColor"
  2. // 默认的占位文字颜色
  3. #define CHGPlaceholderDefaultColor [UIColor grayColor]
  4. // 聚焦的占位文字颜色
  5. #define CHGPlaceholderFocusColor [UIColor whiteColor]
  6.  
  7. // 弹出当前文本框对应的键盘时调用
  8. - (BOOL)becomeFirstResponder
  9. {
  10. [self setValue:CHGPlaceholderFocusColor forKeyPath:CHGPlaceholderColorKey];
  11. return [super becomeFirstResponder];
  12. }
  13.  
  14. // 隐藏当前文本框对应的键盘时调用
  15. - (BOOL)resignFirstResponder
  16. {
  17. [self setValue:CHGPlaceholderDefaultColor forKeyPath:CHGPlaceholderColorKey];
  18. return [super resignFirstResponder];
  19. }

03

精华的推荐标签界面的处理

  - 显示推荐标签数据

  -请求细节处理

  -圆角图片

  -封装圆角头像

  -全局常量、变量(const、extern、static)

一、设置分割线


1、去除系统自带的,在xib中自定义高度为1的view作为分割线(改变透明度调节)(比较麻烦 这里不用)
2、在系统设置完cell的frame后,再手动修改cell的frame(重写setframe方法)

  1. // 去掉系统自带的分割线
  2. self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  3.  
  4. /**
  5. * 重写这个方法的目的:拦截cell的frame设置
  6. */
  7. - (void)setFrame:(CGRect)frame
  8. {
  9. frame.size.height -= ;
  10. // frame.origin.x = 5;
  11. // frame.size.width -= 2 * frame.origin.x;
  12. [super setFrame:frame];
  13. }

注意:重写setframe  setbounds可以避免外界修改控件的尺寸,从而达到保留你设置的尺寸的目的(如果重写形变方法 也可以在次修改属性)

设置分割线方法二错误示例

二、请求数据


1、如果对数据的结构不清楚,建议将数据写在plist文件里看

  1. // 将服务器的数据写成plist。方便查看数据结构
  2. [responseObject writeToFile:@"/Users/chg/Desktop/tag.plist" atomically:YES];

2、管理者最好用属性保存,便于管理task

3、各种请求失败时最好做判断处理

  1. // 如果是取消了任务,就不算请求失败,就直接返回
  2. if (error.code == NSURLErrorCancelled) return;
  3.  
  4. if (error.code == NSURLErrorTimedOut) {
  5. // 关闭弹框
  6. [SVProgressHUD showErrorWithStatus:@"加载标签数据超时,请稍后再试!"];
  7. } else {
  8. // 关闭弹框
  9. [SVProgressHUD showErrorWithStatus:@"加载标签数据失败"];
  10. }

4、控制器消亡时停止请求

  1. - (void)dealloc
  2. {
  3. // 停止请求
  4. [self.manager invalidateSessionCancelingTasks:YES];
  5.  
  6. // [self.manager.downloadTasks makeObjectsPerformSelector:@selector(cancel)];
  7. // [self.manager.dataTasks makeObjectsPerformSelector:@selector(cancel)];
  8. // [self.manager.uploadTasks makeObjectsPerformSelector:@selector(cancel)];
  9.  
  10. // [self.manager.tasks makeObjectsPerformSelector:@selector(cancel)];
  11.  
  12. // for (NSURLSessionTask *task in self.manager.tasks) {
  13. // [task cancel];
  14. // }
  15.  
  16. [SVProgressHUD dismiss];
  17. }

三、项目期间遇到的问题


1、debug中的宏不能全部小写
2、sb中,IBOutlet内部会有一个隐藏强引用,系统会在恰当的时候释放

IBOutlet数组中的顺序和连线顺序有关

3、AFN框架中如何防止控制器被强引用

四、cell里头像设置为圆角图片


1、使用图层直接设置(会出现卡顿现象 不推荐)

  1. - (void)awakeFromNib
  2. {
  3. // 如果使用过于频繁,可能会导致拖拽起来的感觉比较卡
  4. // self.imageListView.layer.cornerRadius = self.imageListView.width * 0.5;
  5. // self.imageListView.layer.masksToBounds = YES;
  6. }

2、使用SDWebImage框架,先裁剪,再设置图片,封装到UIImage分类

  1. - (instancetype)circleImage
  2. {
  3. // 开启图形上下文
  4. UIGraphicsBeginImageContext(self.size);
  5.  
  6. // 获得上下文
  7. CGContextRef ctx = UIGraphicsGetCurrentContext();
  8.  
  9. // 矩形框
  10. CGRect rect = CGRectMake(, , self.size.width, self.size.height);
  11.  
  12. // 添加一个圆
  13. CGContextAddEllipseInRect(ctx, rect);
  14.  
  15. // 裁剪(裁剪成刚才添加的图形形状)
  16. CGContextClip(ctx);
  17.  
  18. // 往圆上面画一张图片
  19. [self drawInRect:rect];
  20.  
  21. // 获得上下文中的图片
  22. UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  23.  
  24. // 关闭图形上下文
  25. UIGraphicsEndImageContext();
  26.  
  27. return image;
  28. }
  29.  
  30. + (instancetype)circleImageNamed:(NSString *)name
  31. {
  32. return [[self imageNamed:name] circleImage];
  33. }

3、根据项目需求更改,封装到UIImageVIew分类,从而达到只改一处整个项目所有头像一起改变风格(圆形 方形)

  1. - (void)setHeader:(NSString *)url
  2. {
  3. [self setCircleHeader:url];
  4. }
  5.  
  6. // 方形
  7. - (void)setRectHeader:(NSString *)url
  8. {
  9. [self sd_setImageWithURL:[NSURL URLWithString:url] placeholderImage:[UIImage imageNamed:@"defaultUserIcon"]];
  10. }
  11.  
  12. // 圆形
  13. - (void)setCircleHeader:(NSString *)url
  14. {
  15. XMGWeakSelf;
  16. UIImage *placeholder = [[UIImage imageNamed:@"defaultUserIcon"] circleImage];
  17. [self sd_setImageWithURL:[NSURL URLWithString:url] placeholderImage:placeholder completed:
  18. ^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
  19. // 如果图片下载失败,就不做任何处理,按照默认的做法:会显示占位图片
  20. if (image == nil) return;
  21.  
  22. weakSelf.image = [image circleImage];
  23. }];
  24. }

那么在项目中下载设置头像时

  1. // 设置头像
  2. [self.imageListView setHeader:tagModel.image_list];

五、代码风格(写法)问题,宏与全局变量的选择


1、cell的循环利用标识 设置为全局常量 或者 宏
2、请求路径 相同的路径抽取到PCH中
3、以上两处,需要注意宏与全局变量、常量被(const、extern、static)修饰的三种方式
1)宏不允许改动,数据是固定的(优点:安全),但是宏可以替换,每一个用到宏的地方都是临时开辟的存储空间(缺点:浪费内存)
2)全局变量的数据允许改动(缺点:不安全),但是全局变量只开辟一块存储空间(优点:优化内存)
3)根据分析折中选择,可以在全局变量前添加const修饰(改为常量,不允许改动)
4)const: 只修饰右边的内容,被修饰的内容都是常量,都是不能再修改的
5)extern:可以引用全局变量(也可以引用函数):其他文件中可以利用extern引用一个全局变量,并且变量可再修改
6)static:
被static修饰全局变量\常量 为静态变量,仅限于当前文件使用,也就是说作用域被改变了
被static修饰局部变量只会占用一块内存,在整个程序运行过程都不会销毁,只会初始化一次,也就是说声明周期被改变了

04

一、“我的”界面


1、界面分析:分组样式的tableView,在tableview的footView上添加多个按钮

  1. // 设置为分组样式:
  2. [self setupChildVc:[[CHGMeViewController alloc] initWithStyle:UITableViewStyleGrouped] title:@"我" image:@"tabBar_me_icon" selectedImage:@"tabBar_me_click_icon"];
  3.  
  4. // 设置footer
  5. self.tableView.tableFooterView = [[CHGMeFooter alloc] init];

2、调整tableView位置,设置tableView的内边距可以让cell和footView一起往上移(tableView.contentInset)

  1. self.tableView.sectionHeaderHeight = ;
  2. self.tableView.sectionFooterHeight = XMGCommonMargin;
  3. // 设置内边距(-25代表:所有内容往上移动25)
  4. self.tableView.contentInset = UIEdgeInsetsMake(XMGCommonMargin - , , , );

3、自定义cell 在initWithStyle:reuseIdentifier中自定义cell文字颜色,背景图片等等是谁的事就交给谁去做,MVC的基本思想:

  1. - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
  2. {
  3. if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
  4. // 设置右边的标识为箭头
  5. self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
  6. // 设置textlabel为深灰色
  7. self.textLabel.textColor = [UIColor darkGrayColor];
  8.  
  9. // 设置背景图片
  10. self.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"mainCellBackground"]];
  11. }
  12. return self;
  13. }

4、重写latoutSubviews  微调cell控件的frame(cell的lable、imageView)

  1. - (void)layoutSubviews
  2. {
  3. [super layoutSubviews];
  4.  
  5. if (self.imageView.image == nil) return;
  6.  
  7. // 调整imageView
  8. self.imageView.y = CHGCommonMargin * 0.5;
  9. self.imageView.height = self.contentView.height - * self.imageView.y;
  10. self.imageView.width = self.imageView.height;
  11.  
  12. // 调整Label
  13. // self.textLabel.x = self.imageView.x + self.imageView.width + XMGCommonMargin;
  14. self.textLabel.x = CGRectGetMaxX(self.imageView.frame) + CHGCommonMargin;
  15.  
  16. // CGRectGetMaxX(self.imageView.frame) == self.imageView.x + self.imageView.width
  17. // CGRectGetMinX(self.imageView.frame) == self.imageView.x
  18. // CGRectGetMidX(self.imageView.frame) == self.imageView.x + self.imageView.width * 0.5
  19. // CGRectGetMidX(self.imageView.frame) == self.imageView.centerX
  20. }

5、自定义footView,在initWithFrame:方法中请求数据,布局footView的子控件,添加按钮,添加点击监听

  1. 。。。
  2. [button addTarget:self action:@selector(buttonClick:) forControlEvents:UIControlEventTouchUpInside];
  3. [self addSubview:button];
  4. // 设置模型数据
  5. button.square = squares[i];
  6. 。。。

6、自定义方块button,布局button子控件,设置数据,设置按钮背景图片时注意按钮状态,(使用UIButton+WebCache框架)

  1. @implementation CHGSquareButton
  2.  
  3. - (instancetype)initWithFrame:(CGRect)frame
  4. {
  5. if (self = [super initWithFrame:frame]) {
  6. self.titleLabel.textAlignment = NSTextAlignmentCenter;
  7. self.titleLabel.font = [UIFont systemFontOfSize:];
  8. [self setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
  9. [self setBackgroundImage:[UIImage imageNamed:@"mainCellBackground"] forState:UIControlStateNormal];
  10. }
  11. return self;
  12. }
  13.  
  14. - (void)layoutSubviews
  15. {
  16. [super layoutSubviews];
  17.  
  18. self.imageView.width = self.width * 0.5;
  19. self.imageView.height = self.imageView.width;
  20. self.imageView.y = self.height * 0.1;
  21. self.imageView.centerX = self.width * 0.5;
  22.  
  23. self.titleLabel.width = self.width;
  24. self.titleLabel.y = CGRectGetMaxY(self.imageView.frame);
  25. self.titleLabel.x = ;
  26. self.titleLabel.height = self.height - self.titleLabel.y;
  27. }
  28.  
  29. - (void)setSquare:(XMGSquare *)square
  30. {
  31. _square = square;
  32.  
  33. // 数据
  34. [self setTitle:square.name forState:UIControlStateNormal];
  35. // 设置按钮的image
  36. [self sd_setImageWithURL:[NSURL URLWithString:square.icon] forState:UIControlStateNormal];
  37. }
  38. @end

7、设置footView高度,再次将footView设置为tableView的footView(或者设置contensize最大y为foot的高度)
如不再次设置会导致footView高度为0 那么按钮已经超出footView的边框范围 按钮不能响应点击事件

  1. // 设置footer的高度(方法一)
  2. self.height = CGRectGetMaxY(button.frame);
  3.  
  4. // 设置footer的高度(方法二)
  5. NSUInteger rowsCount = count / colsCount;
  6. if (count % colsCount) { // 不能整除,行数+1
  7. rowsCount++;
  8. }
  9. // 用下面的数学公式也能快速计算出行数
  10. // NSUInteger rowsCount = (count + colsCount - 1) / colsCount;
  11. self.height = rowsCount * buttonH;

有了高度后 就可以重新设置tableFooterView(或者设置contensize最大y为foot的高度)

  1. UITableView *tableView = (UITableView *)self.superview;
  2.  
  3. // tableView.tableFooterView = self;
  4. tableView.contentSize = CGSizeMake(, CGRectGetMaxY(self.frame));

拓展知识:

1个控件不能响应点击事件,原因可能有:

1> userInteractionEnabled = NO;

2> enabled = NO;

3> 父控件的userInteractionEnabled = NO;

4> 父控件的enabled = NO;

5> 控件已经超出父控件的边框范围(此处按钮不能点击的原因)

8、设置方块按钮之间的分割线
1)自定义添加宽高为1的view
2)设置frame,往左减一,往上减一


3)按钮背景图片本身就带有分割线

9、设置点击按钮时的URL(3种方法)

1)遍历footView所有子控件  根据index赋值

  1. // 计算被点击按钮在子控件数组的位置
  2. // NSUInteger index = [self.subviews indexOfObject:button];
  3. // XMGSquare *square = self.squares[index];

2)给button绑定tag,设置数据

  1. // XMGSquare *square = self.squares[button.tag];

3)一一绑定,在button模型添加数据模型属性,每一个按钮对应一个对象数据

  1. // 设置模型数据
  2. button.square = squares[i];

10、http开头的需要跳转到网页界面(百思有些URL是内部处理的,无法访问 所以在这里做个处理)
拿到当前选中的控制器 进行跳转

  1. if ([button.square.url hasPrefix:@"http"]) {
  2. CHGWebViewController *webVc = [[CHGWebViewController alloc] init];
  3.  
  4. // 取出当前选中的导航控制器
  5. UITabBarController *rootVc = (UITabBarController *)self.window.rootViewController;
  6. UINavigationController *nav = (UINavigationController *)rootVc.selectedViewController;
  7. [nav pushViewController:webVc animated:YES];
  8. // [UIApplication sharedApplication].keyWindow;
  9. // [[rootVc.childViewControllers lastObject] pushViewController:webVc animated:YES];
  10. // CHGLog(@"%@", rootVc.selectedViewController);
  11. }

11、网页界面
实现代理来监听当前页面是否能返回或前进

  1. // 根据url加载网页
  2. [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.square.url]]];
  3.  
  4. #pragma mark - <UIWebViewDelegate>
  5. - (void)webViewDidFinishLoad:(UIWebView *)webView
  6. {
  7. self.backItem.enabled = webView.canGoBack;
  8. self.forwardItem.enabled = webView.canGoForward;
  9. }

12、设置界面(清除缓存)
1)计算文件大小 (多种方法)
获得文件夹路径
遍历文件夹 计算每一个文件的大小 累加所有文件大小

// 手机上的磁盘缓存 == 从网络上下载的数据 + 写入的数据
// 手机上的磁盘缓存的数据类型 == 图片 + 多媒体文件

方法一

  1. - (void)getSize
  2. {
  3. // 总大小
  4. NSInteger size = ;
  5.  
  6. // 文件路径
  7. // NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
  8. // NSString *file = [caches stringByAppendingPathComponent:@"default"];
  9. NSString *file = @"/Users/chg/Desktop";
  10.  
  11. // 文件管理者
  12. NSFileManager *mgr = [NSFileManager defaultManager];
  13.  
  14. // 获得文件夹中的所有内容
  15. NSDirectoryEnumerator *enumerator = [mgr enumeratorAtPath:file];
  16. for (NSString *subpath in enumerator) {
  17. // 获得全路径
  18. NSString *fullSubpath = [file stringByAppendingPathComponent:subpath];
  19. // 获得文件属性
  20. NSDictionary *attrs = [mgr attributesOfItemAtPath:fullSubpath error:nil];
  21. // size += [attrs[NSFileSize] integerValue];
  22. size += attrs.fileSize;
  23. }
  24.  
  25. CHGLog(@"%@ %f", file, size / 1000.0 / 1000.0);
  26. }

方法二

  1. - (void)getSize2
  2. {
  3. // 总大小
  4. NSInteger size = ;
  5.  
  6. // 文件路径
  7. NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
  8. NSString *file = [caches stringByAppendingPathComponent:@"default"];
  9.  
  10. // 文件管理者
  11. NSFileManager *mgr = [NSFileManager defaultManager];
  12.  
  13. // 获得文件夹中的所有内容
  14. // NSArray *contents = [mgr contentsOfDirectoryAtPath:file error:nil];
  15. NSArray *subpaths = [mgr subpathsAtPath:file];
  16. for (NSString *subpath in subpaths) {
  17. // 获得全路径
  18. NSString *fullSubpath = [file stringByAppendingPathComponent:subpath];
  19. // 获得文件属性
  20. NSDictionary *attrs = [mgr attributesOfItemAtPath:fullSubpath error:nil];
  21. // size += [attrs[NSFileSize] integerValue];
  22. size += attrs.fileSize;
  23. }
  24.  
  25. CHGLog(@"%@ %f", file, size / 1000.0 / 1000.0);
  26. }

封装NSString分类 传入文件(夹)路径快速获取该文件(夹)大小

.h文件

  1. #import <Foundation/Foundation.h>
  2.  
  3. @interface NSString (CHGExtension)
  4. // 计算文件(夹)大小
  5. - (NSInteger)fileSize;
  6. @end

.m

  1. #import "NSString+CHGExtension.h"
  2.  
  3. @implementation NSString (CHGExtension)
  4.  
  5. // 判断一个路径是文件夹 or 文件
  6. // [[mgr attributesOfItemAtPath:self error:nil].fileType isEqualToString:NSFileTypeDirectory];
  7.  
  8. - (NSInteger)fileSize
  9. {
  10. // 文件管理者
  11. NSFileManager *mgr = [NSFileManager defaultManager];
  12. // 是否为文件夹
  13. BOOL isDirectory = NO;
  14. // 这个路径是否存在
  15. BOOL exists = [mgr fileExistsAtPath:self isDirectory:&isDirectory];
  16. // 路径不存在
  17. if (exists == NO) return ;
  18.  
  19. if (isDirectory) { // 文件夹
  20. // 总大小
  21. NSInteger size = ;
  22. // 获得文件夹中的所有内容
  23. NSDirectoryEnumerator *enumerator = [mgr enumeratorAtPath:self];
  24. for (NSString *subpath in enumerator) {
  25. // 获得全路径
  26. NSString *fullSubpath = [self stringByAppendingPathComponent:subpath];
  27. // 获得文件属性
  28. size += [mgr attributesOfItemAtPath:fullSubpath error:nil].fileSize;
  29. }
  30. return size;
  31. } else { // 文件
  32. return [mgr attributesOfItemAtPath:self error:nil].fileSize;
  33. }
  34. }
  35. @end

2)实现数据源方法,自定义cell,添加菊花标识等等,添加cell的对象方法,选中cell时调用该方法,实现子线程计算大小、清除缓存(直接干掉缓存文件夹)主线程更新UI
(注意:此处计算文件时禁止cell的点击事件,清除文件时也不允许用户交互,缓存清除完之后干掉菊花等细节)

  1. // 禁止点击事件
  2. self.userInteractionEnabled = NO;
  3.  
  4. // 右边显示圈圈
  5. UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
  6. [loadingView startAnimating];
  7. self.accessoryView = loadingView;
  8.  
  9. // 计算大小
  10. [[[NSOperationQueue alloc] init] addOperationWithBlock:^{
  11. // 计算缓存大小
  12. NSInteger size = CHGCacheFile.fileSize;
  13. CGFloat unit = 1000.0;
  14. NSString *sizeText = nil;
  15. if (size >= unit * unit * unit) { // >= 1GB
  16. sizeText = [NSString stringWithFormat:@"%.1fGB", size / unit / unit / unit];
  17. } else if (size >= unit * unit) { // >= 1MB
  18. sizeText = [NSString stringWithFormat:@"%.1fMB", size / unit / unit];
  19. } else if (size >= unit) { // >= 1KB
  20. sizeText = [NSString stringWithFormat:@"%.1fKB", size / unit];
  21. } else { // >= 0B
  22. sizeText = [NSString stringWithFormat:@"%zdB", size];
  23. }
  24. NSString *text = [NSString stringWithFormat:@"%@(%@)", CHGDefaultText, sizeText];
  25.  
  26. // 回到主线程
  27. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  28. self.textLabel.text = text;
  29. self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
  30. self.accessoryView = nil;
  31. // 允许点击事件
  32. self.userInteractionEnabled = YES;
  33. }];
  34. }];
  35.  
  36. // 清除缓存
  37. - (void)clearCache
  38. {
  39. [SVProgressHUD showWithStatus:@"正在清除缓存" maskType:SVProgressHUDMaskTypeBlack];
  40.  
  41. [[[NSOperationQueue alloc] init] addOperationWithBlock:^{
  42. [[NSFileManager defaultManager] removeItemAtPath:XMGCacheFile error:nil];
  43.  
  44. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  45. [SVProgressHUD showSuccessWithStatus:@"清除成功"];
  46.  
  47. self.textLabel.text = XMGDefaultText;
  48.  
  49. // 禁止点击事件
  50. self.userInteractionEnabled = NO;
  51. }];
  52. }];
  53. }

3)在代理方法中,选中cell时,取消cell的选中状态,调用cell清除缓存(clearCache)的方法

  1. #pragma mark - <代理>
  2. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  3. {
  4. // 取消选中
  5. [tableView deselectRowAtIndexPath:indexPath animated:YES];
  6.  
  7. // 清除缓存
  8. CHGClearCacheCell *cell = (CHGClearCacheCell *)[tableView cellForRowAtIndexPath:indexPath];
  9.  
  10. [cell clearCache];
  11. }

4)扩展多组多行的情况(循环利用)
如果清除缓存的cell(独特、唯一)和其他的cell都不太一样,那么注册两种标识分别区分cell类型 防止循环利用

  1. [self.tableView registerClass:[CHGClearCacheCell class] forCellReuseIdentifier:CHGClearCacheCellId];
  2. [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:CHGOtherCellId];

5)新特新处理:当菊花离开屏幕时系统会自动停止动画(ios9),再给cell添加一个对象方法,用于判断当前是菊花还是箭头,如果是菊花,继续转,不是就return

  1. - (void)updateStatus
  2. {
  3. if (self.accessoryView == nil) return;
  4.  
  5. // 让圈圈继续旋转
  6. UIActivityIndicatorView *loadingView = (UIActivityIndicatorView *)self.accessoryView;
  7. [loadingView startAnimating];
  8. }

13、运行时
1)获取成员变量的类型


应用场景:
字典转模型时判断类型

2)获取属性

3)获取方法交换等等 (具体可以参考曹理鹏博客 )

项目的代码我已经上传到 https://github.com/chglog 欢迎下载

下载后运行可能会报错,你可以先将我的cocoapods清除,重新集成框架,就可以运行了

iOS开发——项目篇—高仿百思不得姐的更多相关文章

  1. iOS开发——项目篇—高仿百思不得姐 05——发布界面、发表文字界面、重识 bounds、frame、scrollView

    加号界面(发布模块) 一.点击加号modal出发布模块,创建控件,布局控件1)使用xib加载view,如果在viewDidLoad创建控件并设置frame 那么self.view 的宽高 拿到的是xi ...

  2. iOS开发:一个高仿美团的团购ipad客户端的设计和实现(功能:根据拼音进行检索并展示数据,离线缓存团购数据,浏览记录与收藏记录的批量删除等)

    大致花了一个月时间,利用各种空闲时间,将这个客户端实现了,在这里主要是想记录下,设计的大体思路以及实现过程中遇到的坑...... 这个项目的github地址:https://github.com/wz ...

  3. iOS开发拓展篇——如何把项目托管到GitHub

    iOS开发拓展篇——如何把项目托管到GitHub 说明:本文主要介绍如何把一个OC项目托管到Github,重操作轻理论. 第一步:先注册一个Github的账号,这是必须的 注册地址:Github官网注 ...

  4. iOS开发UI篇—CAlayer(创建图层)

    iOS开发UI篇—CAlayer(创建图层) 一.添加一个图层 添加图层的步骤: 1.创建layer 2.设置layer的属性(设置了颜色,bounds才能显示出来) 3.将layer添加到界面上(控 ...

  5. iOS开发网络篇—简单介绍ASI框架的使用

    iOS开发网络篇—简单介绍ASI框架的使用 说明:本文主要介绍网络编程中常用框架ASI的简单使用. 一.ASI简单介绍 ASI:全称是ASIHTTPRequest,外号“HTTP终结者”,功能十分强大 ...

  6. iOS开发数据库篇—SQLite简单介绍

    iOS开发数据库篇—SQLite简单介绍 一.离线缓存 在项目开发中,通常都需要对数据进行离线缓存的处理,如新闻数据的离线缓存等. 说明:离线缓存一般都是把数据保存到项目的沙盒中.有以下几种方式 (1 ...

  7. iOS开发UI篇—iOS开发中三种简单的动画设置

    iOS开发UI篇—iOS开发中三种简单的动画设置 [在ios开发中,动画是廉价的] 一.首尾式动画 代码示例: // beginAnimations表示此后的代码要“参与到”动画中 [UIView b ...

  8. iOS开发UI篇—模仿ipad版QQ空间登录界面

    iOS开发UI篇—模仿ipad版QQ空间登录界面 一.实现和步骤 1.一般ipad项目在命名的时候可以加一个HD,标明为高清版 2.设置项目的文件结构,分为home和login两个部分 3.登陆界面的 ...

  9. 【转】 iOS开发数据库篇—SQLite简单介绍

    开始学SQLite啦, 原文: http://www.cnblogs.com/wendingding/p/3868893.html iOS开发数据库篇—SQLite简单介绍 一.离线缓存 在项目开发中 ...

随机推荐

  1. BZOJ1804: [Ioi2007]Flood 洪水

    把点按坐标排序,每次找出最小的点,一定在最外层,再顺着把最外层的边删掉,经过了两次的边不会被冲毁. 其实不难写,但是写了很久. #include<bits/stdc++.h> #defin ...

  2. Java工厂设计模式

    程序在接口和子类之间加入一个过渡类,通过此过渡类端取得接口的实例化对象,一般都会称这个过渡端为工厂类 //=============================================== ...

  3. C++ Pointer-to-Member Selector

    http://www.codeguru.com/cpp/cpp/article.php/c17401/C-Tutorial-PointertoMember-Function.htm https://m ...

  4. clearInterval,setInterval,clearTimeout,setTimeout

    setInterval("f()",1000)  每隔1秒就执行一次f() clearInterval   关闭clearInterval setTimeout("f() ...

  5. JavaScript数据操作--原始值和引用值的操作本质

    我的一句话总结:原始值不管是变量赋值还是函数传递都不会改变原值,引用值不管是变量赋值还是函数传递,如果新变量重新赋值,则不会影响原引用值,如新变量是直接操作,就会影响原引用值. 首先明确,值和类型是两 ...

  6. 数据库操作事务IsolationLevel 枚举

      成员名称 说明   Chaos 无法覆盖隔离级别更高的事务中的挂起的更改.   ReadCommitted 在正在读取数据时保持共享锁,以避免脏读,但是在事务结束之前可以更改数据,从而导致不可重复 ...

  7. C# 向IQueryable添加一个Include扩展方法

    using System; using System.Data.Objects; using System.Linq; namespace OutOfMemory.Codes { /// <su ...

  8. JS原型链理解

    1. 每个对象都有原型属性(__proto__)2. 对象的原型(__proto__)指向其构造函数(Class)的prototype属性3. 构造函数(Class)的prototype属性本身也是一 ...

  9. 将excel文件中的数据导入到mysql

    ·在你的表格中增加一列,利用excel的公式自动生成sql语句,具体方法如下:          1)增加一列(假设是D列)          2)在第一行的D列,就是D1中输入公式:=CONCATE ...

  10. 缺陷跟踪系统Mantis Bug Tracker

    缺陷管理平台Mantis,也做MantisBT,全称Mantis Bug Tracker. 项目在github的地址:https://github.com/mantisbt/mantisbt Mant ...