一、搭建基本框架
规范:一般有两种。
按模块分(各个模块里分为MVC)
按MVC分
1.自定义TabBar
1>新建一个MJTabBar,继承自UIView
2>新建一个MJTabBarController,来到storyboard,将Class设置为MJTabBarController
3>来到viewDidLoad将TabBar换成自定义的TabBar
//1.移除系统自带的TabBar [self.tabBar removeFromSuperview]; //2.添加自己的TabBar MJTabBar *myTabBar = [[MJTabBar alloc] init]; myTabBar.frame = self.tabBar.frame; [self.view addSubview:myTabBar]; //3.添加5个按钮 for(int i = 0; i<5; i++) { //创建按钮 MJTabBarButton *button = [MJTabBarButton buttonWithType:UIButtonTypeCusTom]; //设置按钮的背景图片 NSString *name = [NSStirng stringWithFormat:@"TabBar%d", i + 1]; [button setBackgroundImage:[UIImage imageNamed:name] forState:UIControlStateNormal]; NSString *selectedName = [NSStirng stringWithFormat:@"TabBar%dSel", i + 1]; [button setBackgroundImage:[UIImage imageNamed:selectedName] forState:UIControlStateSelected]; //设置frame CGFloat buttonY = 0; CGFloat buttonW = myTabBar.frame.size.width * 0.2; CGFloat buttonH = myTabBar.frame.size.height; CGFloat buttonX = i * buttonW; button.frame = CGRectMake(buttonX,buttonY,buttonW,buttonH); button.selected = YES; //添加 [myTabBar addSubview:button]; //监听 [button addTarget:self action:@selector(buttonClick:) forControlEvents:UIControlEventTouchUpInside]; //默认选中第0个位置的按钮 if(i == 0){ [self buttonClick:button]; } } }
4>添加一个按钮属性用来记录当前选中的按钮
@property(nonatomic,weak) UIButton *selecedButton;
5>实现监听按钮点击的方法
-(void)buttonClick:(UIButton *)button { //1.让当前选中的按钮取消选中 self.selectedButton.selected = NO; //2.让新点击的按钮选中 button.selected = YES; //3.新点击的按钮就成为了”当前选中的按钮“ self.selectedButton = button; }
二、切换子控制器按钮高亮处理
需要达到的效果:一点击选项卡(手还没松开)就会切换到相应界面,并且已经选中的选项卡再点击也不会变为高亮状态。
1>给每一个导航控制器连接一个子控制器,并在添加按钮时的for循环中给按钮绑定tag
button.tag = i;
2>在按钮点击的方法中根据tag切换子控制器
-(void)buttonClick:(UIButton *)button { ...... //4.切换子控制器 self.selectedIndex = button.tag; }
3>一点击选项卡(手还没松开)就会切换到相应界面
将监听的事件改为UIControlEventTouchDown
4>当按钮变为高亮状态时系统会调用一个方法[button hightlighted:YES];,并且在这个方法里面做一些复杂的操作,导致高亮状态图片不能立即显示(而是要等手松开才显示)。所以我们应该新建一个UIButton的子类,然后重写这个方法
//只要覆盖了这个方法,按钮就不存在高亮状态 -(void)setHighlighted:(BOOL)highlighted { }
三、TabBar的封装方法---第一种封装方法
1.重写initWithFrame:方法,在里面创建按钮(init方法会自动调用这个方法)
//UI控件:建议重写initWithFrame:方法来做一些初始化的操作 -(id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if(self) { for(int i = 0; i<5; i++) { //创建按钮 MJTabBarButton *button = [MJTabBarButton buttonWithType:UIButtonTypeCusTom]; //设置按钮的背景图片 NSString *name = [NSStirng stringWithFormat:@"TabBar%d", i + 1]; [button setBackgroundImage:[UIImage imageNamed:name] forState:UIControlStateNormal]; NSString *selectedName = [NSStirng stringWithFormat:@"TabBar%dSel", i + 1]; [button setBackgroundImage:[UIImage imageNamed:selectedName] forState:UIControlStateSelected]; button.selected = YES; //添加 [self addSubview:button]; //监听 [button addTarget:self action:@selector(buttonClick:) forControlEvents:UIControlEventTouchUpInside]; //默认选中第0个位置的按钮 if(i == 0){ [self buttonClick:button]; } }
2.在layoutSubviews方法里设置frame
-(void)layoutSubviews
{
[super layoutSubviews];
//先拿到按钮
for(int i = 0; i<5; i++) {
MJTabBarButton *button = self.subviews[i];
//设置frame
CGFloat buttonY = 0;
CGFloat buttonW = self.frame.size.width * 0.2;
CGFloat buttonH = self.frame.size.height;
CGFloat buttonX = i * buttonW;
button.frame = CGRectMake(buttonX,buttonY,buttonW,buttonH);
}
}
3.将buttonClick:方法剪切到MyTabBar中
-(void)buttonClick:(UIButton *)button { //0.通知代理 if([self.delegate respondsToSelector:@selector(tabBar:didSelectButtonFrom:to:)]){ [self.delegate tabBar:self didSelectButtonFrom: self.selectedButton.tag to:button.tag]; //1.让当前选中的按钮取消选中 self.selectedButton.selected = NO; //2.让新点击的按钮选中 button.selected = YES; //3.新点击的按钮就成为了”当前选中的按钮“ self.selectedButton = button; } }
4. 因为myTabBar不能直接切换子控制器,所以委托控制器作为代理切换子控制器
1>新建协议并提供一个方法用来通知代理选项卡被点击了
2>在buttonClick:方法通知代理
3>控制器实现代理方法
{ //1.选中最新的控制器 self.selectedIndex = to; }
5.控制器一下子就轻松了
{ //1.移除系统自带的TabBar [self.tabBar removeFromSuperview]; //2.添加自己的TabBar MJTabBar *myTabBar = [[MJTabBar alloc] init]; myTabBar.delegate = self; myTabBar.frame = self.tabBar.frame; [self.view addSubview:myTabBar]; }
6.来到MJTabBar,将initWithFrame:方法中的一大串抽出来
-(id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if(self) { [self setupButtons]; } return self; }
//用来初始化按钮 -(void)setupButtons { for(int i = 0; i<5; i++) { //创建按钮 MJTabBarButton *button = [MJTabBarButton buttonWithType:UIButtonTypeCusTom]; //设置图片 NSString *name = [NSStirng stringWithFormat:@"TabBar%d", i + 1]; [button setBackgroundImage:[UIImage imageNamed:name] forState:UIControlStateNormal]; NSString *selectedName = [NSStirng stringWithFormat:@"TabBar%dSel", i + 1]; [button setBackgroundImage:[UIImage imageNamed:selectedName] forState:UIControlStateSelected]; button.selected = YES; //添加 [self addSubview:button]; //监听 [button addTarget:self action:@selector(buttonClick:) forControlEvents:UIControlEventTouchUpInside]; //默认选中第0个位置的按钮 if(i == 0){ [self buttonClick:button]; }
四、TabBar的封装方法---第二种封装方法
问题:按钮的数量不能写死,按钮有多少个应该取决于TabBarController,TabBarController有多少个子控制器就有多少个按钮。要添加多少个按钮是由控制器来决定,但是有个问题,按钮的代码还是得封装到TabBar里面,因为TabBar内部用的是UITabBarButton还是MJTabBarButton,只有TabBar才知道,控制器不用关心里面用的什么控件,但是添加多少个按钮却由控制器来决定,这就矛盾了。
解决方案:
1.TabBar提供一个方法用来让别人添加一个内部的按钮
-(void)addTabButtonWithName:(NSString *)name selName:(NSString *)selName{ //创建按钮 MJTabBarButton *button = [MJTabBarButton buttonWithType:UIButtonTypeCusTom]; //设置按钮的背景图片 [button setBackgroundImage:[UIImage imageNamed:name] forState:UIControlStateNormal]; [button setBackgroundImage:[UIImage imageNamed:selName] forState:UIControlStateSelected]; button.selected = YES; //添加 [self addSubview:button]; //监听 [button addTarget:self action:@selector(buttonClick:) forControlEvents:UIControlEventTouchDown]; //默认选中第0个位置的按钮 if(self.subviews.count == 1){ [self buttonClick:button]; } }
PS:控制器拿到TabBar后,只需要调用这个方法,传图片名给它,TabBar就能添加按钮了,究竟里面用了什么技术,控制器不用关心。
2.在layoutSubviews方法里设置frame和绑定tag
-(void)layoutSubviews { [super layoutSubviews]; int count = self.subviews.count; for(int i = 0; i<count; i++) { MJTabBarButton *button = self.subviews[i]; //绑定tag button.tag = i; //设置frame CGFloat buttonY = 0; CGFloat buttonW = self.frame.size.width / count; CGFloat buttonH = self.frame.size.height; CGFloat buttonX = i * buttonW; button.frame = CGRectMake(buttonX,buttonY,buttonW,buttonH); } }
3.来到控制器viewDidLoad
-(void)viewDidLoad { [super viewDidLoad]; //1.将自己的TabBar添加到系统的tabBar上 MJTabBar *myTabBar = [[MJTabBar alloc] init]; myTabBar.delegate = self; myTabBar.frame = self.tabBar.bounds; [self.tabBar addSubview:myTabBar]; //2.添加对应个数的按钮 for(int i = 0; i<self.viewControllers.count; i++) { NSString *name = [NSString stringWithFormat:@"TabBar%d",i + 1]; NSString *selName = [NSString stringWithFormat:@"TabBar%dSel",i + 1]; [myTabBar addTabButtonWithName:name selName:selName]; } }
PS:如果做类似的界面,可以直接拿过去用,在控制器中设置图片即可重用。
五、自定义导航控制器(为了每次push的时候都自动选Hide Bottom Bar on Push)
1.自定义导航控制器
1>新建一个导航控制器,并将所有导航控制器的Class修改为这个自定义控制器
2>重写自定义导航控制器的push方法
-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated { viewController.hideBottomBarWhenPushed = YES; [super pushViewController:viewController animated:animated]; }
六、设置导航栏的主题
1.设置导航栏的主题
//系统在第一次使用这个类的时候调用(一个类只会调用一次) //以后只需要做一次的操作在initialize方法里做 -(void)initialize { UINavigationBar *navBar = [UINavigationBar appearance]; //设置背景图片 [navBar setBackgroundImage: [UIImage imageNamed:@"NavBar64"]forBarMetrics:UIBarMetricsDefault]; //设置导航栏标题文字颜色 NSMutableDictionary *attrs = [NSMutableDictionary dictionary]; attrs[NSForegroundColorAttributeName] = [UIColor whiteColor]; [navBar setTitleTextAttributes:attrs]; }
2.将状态栏文字设置为白色
直接在application里设置(因为不知道是哪个控制器在管,直接在application里设置一劳永逸)
1>先在plist文件里添加一条View controller-based status bar appearence NO
2>在MJAppDelegate.m的didFinishLaunchingWithOptions:方法中
{ //设置状态栏的样式 application.statusBarStyle = UIStatusBarStyleLightContent; }
PS:如果图标有玻璃质感,那么应该勾选提前渲染。这样就不会有反光效果了。
七、自定义导航栏标题按钮
1.导航条拖一个Button(不能用BarButtonItem同时表示图片和文字)
2.功能实现:点击导航条的标题+△ 出现下拉菜单
1>自定义一个MJTitleButton(系统自带按钮图片在文字左边,不符合要求)
2>添加一条属性titleFont用来记录字体
//从文件中解析一个对象的时候就会调用这个方法 -(void)initWithCoder:(NSCoder *)decoder { if(self = [super initWithCoder:decoder]) { [self setup]; } return self; } //通过代码创建的时候就会调用 -(id)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { [self setup]; } return self; } //初始化 -(void)setup { self.titleFont = [UIFont systemFontOfSize:14]; self.titleLabel.font = self.titleFont; //让内部的图标居中 self.imageView.contentMode = UIViewContentModeCenter; }
PS:以后自定义View,以上步骤为标配(大公司规范),这样无论你通过文件创建还是代码创建,都会初始化。
3>实现方法设置控件的frame
//控制器内部label的frame //contentRect是按钮自己的宽高 -(CGRect)titleRectForContentRect:(CGRect)contentRect { CGFloat titleX = 0; CGFloat titleY = 0; NSDictionary *attrs = @{NSFontAttributeName:self.titleFont}; CGFloat titleW = [self.currentTitle boundingRectWithSize:CGSizeMake(MAXFLOAT,MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size.width; CGFloat titleH = contentRect.size.height; return CGRectMake(titleX,titleY,titleW,titleH); } //控制器内部imageView的frame -(CGRect)imageRectForContentRect:(CGRect)contentRect { CGFloat imageX = contentRect.size.width - imageW; CGFloat imageY = 0; CGFloat imageW = 30; CGFloat imageH = contentRect.size.height; }
3.监听MJTitleButton
1>新建一个控制器MJBuyViewController
2>拖线
3>实现方法
-(titleClick):(UIButton *)sender { //1.按钮旋转 [UIView animateWithDuration:0.25 animatons:
{ sender.imageView.transform = CGAffineTransformMakeRotation(-M_PI); }]; //2.添加UIView UIView *temp = [UIView alloc] init]; temp.frame = CGRectMake(10,10,100,30); [self.view addSubview:temp]; }
知识补充
initWithCoder:从文件中(通过代码则不调用这个方法)解析一个对象的时候就会调用这个方法
awakeFromNib:当一个对象从xib或storyboard中加载完毕后,就会调用一次
调用顺序:initWithCoder ---> titleRectForContentRect --> awakeFromNib
PS:所以在initWithCoder中事先设置titleFont的值,titleRectForContentRect就不会崩溃了。
八、设置按钮的背景
1.问题:如何拉伸imageView的图片
答:右边有个拉伸选项,可以填写X,Y,W,H(取值范围均为0到1)
X表示左边需要保护(不拉伸)的部分,Y表示需要保护(不拉伸)的部分,都写0.5
Width和Height::需要拉伸的像素点,写0则默认为1个像素
2.问题:如何拉伸按钮的图片
答:通过代码
1>新建一个控制器MJLoginViewController,改Class
2>拖线添加登录按钮(Outlet)
3>在viewDidLoad中(将之前封装好的拉伸文件直接拖进来调用即可)
-(void)viewDidLoad
{
[super viewDidLoad];
[self.loginBtn setBackgroundImage:
[UIImage resizableImage:@"RedButton"]
forState:UIControlStateNormal];
[self.loginBtn setBackgroundImage:
[UIImage resizableImage:@"RedButtonPressed"]
forState:UIControlStateHighlighted];
}
PS:现在点击按钮,按钮会变灰而不是变成设置的图片,将按钮的类型改为Custom即可。
九、设置UIBarButtonItem样式
1.来到导航栏控制器实现文件
-(void)initialize { ..... //设置返回键的箭头颜色 navBar.tintColor = [UIColor whiteColor]; // 设置BarButtonItem的主题 UIBarBUtton Item *item = [UIBarButtonItem appearance]; //设置文字颜色 NSMutableDictionary *itemAttrs = [NSMutableDictionary dictionary]; itemAttrs[NSForegroundColorAttributeName] = [UIColor whiteColor]; [navBar setTitleTextAttributes:itemAttrs]; [item setTitleTextAttributes: forState:UIControlStateNormal]; [item setTitleTextAttributes: forState:UIControlStateNormal]; }
十、设置01-设置界面的做法
1.1>新建一个UITableViewController的子类
2>监听设置按钮(Action)并实现方法
-(IBAction)setting { MJSettingViewController *settingVc = [[MJSettingViewController alloc] init]; [self.navigationController pushViewController:settingVc animated:YES]; }
2.来到ViewDidLoad中
//导航栏标题 self.title = @"设置";
3.重写init方法和initWithStyle方法(将Style改为Grouped)
-(id)init { return [super initWithStyle:UITableViewGrouped]; } -(id)initWithStyle:(UITableViewStyle)style { return [super initWithStyle:UITableViewGrouped]; }
知识补充:plist的缺点
1.不能将字符串转为代码。所以如果点击某一行要执行某段代码的时候,plist无能为力
2.plist中的类名如果写错,不会报错。
所以我们用模型
3.新建一个类MJSettingItem(每个Cell对应一个MJSettingItem模型)
1>添加icon属性和title属性
2>提供一个类方法快速返回一个MJSettingItem
+(instancetype)itemWithIcon:(NSString *)icon title:(NSString *)title; //无需跳转 //以下属性方法放到MJSettingArrowItem.m中 //点击这行cell需要跳转的控制器 @property(nonatomic,copy)Class destVcClass; +(instancetype)itemWithIcon:(NSString *)icon title:(NSString *)title destVcClass:(Class)destVcClass { MJSettingArrowItem *item = [[self alloc] init]; item.icon = icon; item.title = title; item.destVcClass = destVcClass; return item; }
4.添加一个数据属性并懒加载
@property(nonatomic,strong)NSMutableArray *data; -(NSArray *)data { if(_data == nil) { _data = [NSMutableArray array]; //0组 MJSettingItem *item00 = [MJSettingItem itemWithIcon:@"MorePush" title:@"推送和提醒" destVcClass:[MJPushNoticeViewController class]]; MJSettingItem *item01 = [MJSettingItem itemWithIcon:@"handShake" title:@"推送和提醒" destVcClass:[MJPushNoticeViewController class]]; NSArray *array0 = @[item00,item01]; //1组 MJSettingItem *item10 = [MJSettingItem itemWithIcon:@"IDInfo" title:@"推送和提醒" destVcClass:[MJPushNoticeViewController class]]; NSArray *array1 = @[item10]; [ _data addObejct:array0]; [ _data addObejct:array1]; } return _data; }
5.实现数据源方法和代理方法
#pragma mark - 数据源方法 第一个数据源方法:numberOfSectionsInTableView:方法 return self.data.count; 第二个数据源方法:numberOfRowsInSection:方法 NSArray *subdata = self.data[section]; return subdata.count; 第三个数据源方法:cellForAtIndexPath:方法 { //1.创建cell static NSString *ID = @"setting"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; if (cell == nil){ cell = [UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID]; } //2.设置cell的数据 MJSettingItem *item = self.data[indexPath.section][indexPath.row]; cell.imageView.image = [UIImage imageNamed:item.icon]; cell.textLabel.text = item.title; return cell; }
#pargma mark - 代理方法 -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { MJSettingItem *item = self.data[indexPath.section][indexPath.row]; UIViewController *vc = [[item.destVcClass alloc] init]; [self.navigationController pushViewController:vc animated:YES]; }
十一、设置02-cell的封装
Group模型
1>头部标题
2>尾部标题
3>Item
1.新建一个普通类MJSettingGroup
1>添加属性:头部标题header,尾部标题footer,数组items(存放着这组所有行的模型数据,数组中都是MJSettingItem对象)
2>修改懒加载
-(NSArray *)data { if(_data == nil) { _data = [NSMutableArray array]; //0组 MJSettingItem *item00 = [MJSettingItem itemWithIcon:@"MorePush" title:@"推送和提醒" destVcClass:[MJPushNoticeViewController class]]; MJSettingItem *item01 = [MJSettingItem itemWithIcon:@"handShake" title:@"推送和提醒" destVcClass:[MJPushNoticeViewController class]]; MJSettingGroup *group0 = [[MJSettingGroup alloc] init]; group0.items = @[item00,item01]; group0.header = group0.footer = [_data addObejct:group0]; //1组 MJSettingItem *item10 = [MJSettingItem itemWithIcon:@"MorePush" title:@"推送和提醒2" destVcClass:[MJPushNoticeViewController class]]; MJSettingGroup *group1 = [[MJSettingGroup alloc] init]; group0.items = @[item10]; group0.header = [_data addObejct:group1]; } return _data; }
3>修改数据源方法和代理方法
#pragma mark - 数据源方法 第一个数据源方法:numberOfSectionsInTableView:方法 return self.data.count; 第二个数据源方法:numberOfRowsInSection:方法 NSArray *subdata = self.data[section]; return group.items.count; 第三个数据源方法:cellForAtIndexPath:方法 { //1.创建cell static NSString *ID = @"setting"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; if (cell == nil){ cell = [UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID]; } //2.设置cell的数据 MJSettingGroup *group = self.data[indexPath.section]; MJSettingItem *item = group.items[indexPath.row]; cell.imageView.image = [UIImage imageNamed:item.icon]; cell.textLabel.text = item.title; return cell; }
#pragma mark - 代理方法 -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { MJSettingGroup *group = self.data[indexPath.section]; MJSettingItem *item = group.items[indexPath.row]; if ([item isKindOfClass:[MJSettingArrowItem class]]) { MJSettingArrowItem *arrowItem = (MJSettingArrowItem *)item; //如果没有需要跳转的控制器 if(arrowItem.destVcClass == nil) return; UIViewController *vc = [[item.destVcClass alloc] init]; [self.navigationController pushViewController:vc animated:YES]; }
4>实现头部标题和尾部标题方法
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { MJSettingGroup *group = self.data[section]; return group.header; } -(NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section { MJSettingGroup *group = self.data[section]; return group.footer; }
2.封装cell
1>新建一个Cell
2>一个Cell绑定一个Item(来到cell的头文件)
@class MJSettingItem; @property(nonatomic,strong)MJSettingItem item;
3>提供一个类方法快速返回cell
+(instancetype)cellWithTableView:(UITableView)tableView { static NSString *ID = @"cell"; MJSettingCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; if (cell == nil){ cell = [MJSettingCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID]; } return cell; }
4>来到cell的实现文件重写setter方法(在这里将item的属性赋值给Cell的子控件)
#import "MJSettingItem.h" - (void)setItem:(MJSettingItem *)item { _item = item; //给属性赋值(属性默认为nil) self.imageView.image = [UIImage imageNamed:item.icon]; self.textLabel.text = item.title; }
3.设置cell旁边的箭头或开关(用继承的方式)
1>新建一个MJSettingSwitchItem,继承自MJSettingItem,再新建一个MJSettingArrowItem,继承自MJSettingItem
PS:这样如果cell右边是开关,就创建第一种,如果是箭头,就创建第二种
2>来到MJSettingViewController.m,导入前面2个头文件
-(NSArray *)data { if(_data == nil) { _data = [NSMutableArray array]; //0组 [self setupGroup0]; //1组 [self setupGroup1]; } return _data; } //第0组的数据 -(void)setupGroup0{ //左边写MJSettingItem,右边写MJSettingArrowItem ,这样以后右边改什么都不要紧。这是多态的好处 MJSettingItem *pushNotice = [MJSettingArrowItem itemWithIcon:@"MorePush" title:@"推送和提醒" destVcClass:[MJPushNoticeViewController class]]; MJSettingItem *handShake = [MJSettingSwitchItem itemWithIcon:@"handShake" title:@"摇一摇机选"]; MJSettingItem *soundEffect = [MJSettingSwitchItem itemWithIcon:@"sound_Effect" title:@"声音效果"]; MJSettingGroup *group = [[MJSettingGroup alloc] init]; group.items = @[pushNotice,handShake,soundEffect]; [self.data addObejct:group]; } //第1组的数据 -(void)setupGroup1{ MJSettingItem *update = [MJSettingItem itemWithIcon:@"MoreUpdate" title:@"检查新版本"]; MJSettingItem *help = [MJSettingArrowItem itemWithIcon:@"MoreHelp" title:@"帮助" destVcClass:[MJPushNoticeViewController class]]; MJSettingItem *share = [MJSettingArrowItem itemWithIcon:@"share" title:@"分享" destVcClass:[MJPushNoticeViewController class]]; MJSettingItem *viewMsg = [MJSettingArrowItem itemWithIcon:@"viewMsg" title:@"查看消息" destVcClass:[MJPushNoticeViewController class]]; MJSettingItem *product = [MJSettingArrowItem itemWithIcon:@"product" title:@"产品推荐" destVcClass:[MJProductViewController class]]; MJSettingItem *about = [MJSettingArrowItem itemWithIcon:@"about" title:@"关于" destVcClass:[MJPushNoticeViewController class]]; MJSettingGroup *group = [[MJSettingGroup alloc] init]; group.items = @[update,help,share,viewMsg,product,about]; [self.data addObejct:group]; }
3>来到cell的实现文件重写setter方法
-(void)setItem:(MJSettingItem *)item { _item = item; //1. 设置数据 [self setupData]; //2.设置右边的内容(箭头或开关) [self setupRightContent]; } //设置数据 -(void)setupData { self.imageView.image = [UIImage imageNamed:self.item.icon]; self.textLabel.text = self.item.title; } //设置右边的内容(箭头或开关) -(void)setupRightContent { if([self.item isKindOfClass:[MJSettingArrowItem class]]) { // 箭头 self.accessoryView = [UIImageView alloc] initWithImage:[UIImage imageNamed:@"CellArrow"]]; } else if([self.item isKindOfClass:[MJSettingSwitchItem class]]) { //开关 self.accessoryView = [[UISwitch alloc] init]; } else { self.accessoryView = nil; //如果不写这个循环利用时会出现问题。 } }
4>setupRightContent的调用频率很高,每次都会重新创建,而且每次创建的都是一样的,所以将其懒加载
//特例:这2个UI控件用strong而不是weak @property(nonatomic,strong)UIImage *arrowView; @property(nonatomic,strong)UISwitch *switchView; -(UIImageView *)arrowView { if(_arrowView == nil) { _arrowView = [UIImageView alloc] initWithImage:[UIImage imageNamed:@"CellArrow']]; } return _arrowView; } -(UIImageView *)switchView { if(_switchView == nil) { _switchView = [UISwitch alloc] init]; } return _switchView; }
5>修改setupRightContent方法
//设置右边的内容(箭头或开关) -(void)setupRightContent { if([self.item isKindOfClass:[MJSettingArrowItem class]]) { // 箭头 self.accessoryView = self.arrowView; } else if([self.item isKindOfClass:[MJSettingSwitchItem class]]) { //开关 self.accessoryView = self.switchView; self.selectionStyle = UITableViewCellSelectionStyleNone; } else { self.accessoryView = nil; //如果不写这个循环利用时会出现问题。 } }
4.检查新版本
1>在MJSettingItem中添加一条属性
typedef void(^MJSettingItemOption)(); //点击那个cell需要做什么事情 @property(nonatomic,copy)MJSettingItemOption option;
2>来到data的懒加载中
-(NSArray *)data { ...... //1组 MJSettingItem *update = [MJSettingItem itemWithIcon:@"MoreUpdate" title:@"检查新版本"]; //如果点击cell想做一些事情,直接给option属性赋值即可。 update.option = ^{ //弹框提示 [MBProgressHUD showMessage:@"检查新版本中..."]; //几秒后消失(模拟发送网络请求) dispatch_after{dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)),dispatch_get_main_queue(),^{ //移除HUD [MBProgressHUD hideHUD]; //提醒有没有新版本 [MBProgressHUD showerror:@"没有新版本"]; } MJSettingItem *help = [MJSettingArrowItem itemWithIcon:@"MoreHelp" title:@"帮助" destVcClass:[MJPushNoticeViewController class]]; MJSettingGroup *group1 = [[MJSettingGroup alloc] init]; group0.items = @[update,help]; [_data addObejct:group1]; }
3>来到didSelectRowAtIndexPath:方法
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { //1.取消选中这行 [TableView deselectRowAtIndexPath:indexPath animated:YES]; //2.模型数据 MJSettingGroup *group = self.data[indexPath.section]; MJSettingItem *item = group.items[indexPath.row]; if (item.option) { //如果block有值(点击这个cell,有特定的操作需要执行) item.option( ); //调用block } else if ([item isKindOfClass:[MJSettingArrowItem class]]) { MJSettingArrowItem *arrowItem = (MJSettingArrowItem *)item; //如果没有需要跳转的控制器 if(arrowItem.destVcClass == nil) return; UIViewController *vc = [[item.destVcClass alloc] init]; vc.title = arrowItem.title; [self.navigationController pushViewController:vc animated:YES]; }
十二、设置03-产品推荐-UICollectionView的基本使用(使用方法和UITableView非常相似)
1.新建一个UICollectionViewController的子类
2.在viewDidLoad中注册cell(告诉collectionView将来创建怎样的cell)
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReusableIdentifier:MJProductCellID]; //如果用xib创建cell,不能用上面的方法注册cell,应该用下面的方法 UINib *nib = [UINib nibWithName:@"MJProdcutCell" bundle:nil]; [self.collectionView registerNib:nib forCellWithReuseIdentifier:MJProductCellID ]; //设置collectionView的背景色 self.collectionView.backgroundColor = [UIColor whiteColor];
3.实现数据源方法和代理方法
#pragma mark - 数据源方法 - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return self.products.count; } -(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { //1.获得cell MJProductCell *cell = [collectionView dequeueReusableCellWithIdentifier:MJProductCellID forIndexPath:indexPath]; //2.给cell传递模型数据 cell.product = self.products[indexPath.item]; return cell; } //设置cell的数据 cell.imageView.image = [UIImage imageNamed:item.icon]; cell.textLabel.text = item.title; return cell; }
4.重写init方法
-(id)init { //1.流水布局 UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout alloc] init]; //2.每个cell的尺寸 layout.itemSize = CGSizeMake(100,100); //3.设置cell之间的水平间距 layout.minimumInteritemSpacing = 0; //4.设置cell之间的垂直间距 layout.minimumLineSpacing = 10; //5.设置四周的内边距 layout.sectionInset = UIEdgeInsetsMake(layout.minimumLineSpacing,0,0,0); return [super initWithCollectionViewLayout:layout]; }
十三、设置04-产品推荐-自定义UICollectionViewCell
0.事先准备好了一个JSON文件
1.新建一个模型,并添加属性(title,icon)
2.提供一个构造方法和类方法快速返回一个模型对象(在这个方法里创建并赋值)
-(instancetype)iinitWithDict:(NSDictionary *)dict { if(self = [super init] ) { self.icon = dict[@"icon"]; self.title = dict:[@"title"]; } return self; } +(instancetype)productWithDict:(NSDictionary *)dict { return [[self alloc] initWithDict:dict]; }
3.来到控制器,添加一个数组属性用来装所有的模型数据并懒加载
//JSON对象转模型 @property(nonotomic,strong)NSArray *products; -(NSArray *)products { if(_products == nil) { //JSON文件的路径 NSString *path = [[NSBundle mainBundle] pathForResource:@"products.json" ofType:nil]; //加载JSON文件 NSData *data = [NSData dataWithContentsOfFile:path]; //将JSON数据转为NSArray或者NSDictionary NSArray *dictArray = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableContainers error:nil]; //将字典转成模型 NSMutableArray *productArray = [NSMutableArray array]; for(NSDictionary *dict in dataArray){ MJProduct *p = [MJProduct productWithDict:dict]; [productArray addObject:p]; _products = productArray; //将可变数组赋值给属性 } return _products; }
3.自定义CollectionViewCell
1>新建一个UICollectionViewCell的子类MJProduct
2>新建一个xib文件,拖一个CollectionViewCell.拖一个imageView控件,一个Label控件。填写循环利用标识product。
3>来到Cell的头文件,添加一个模型数据属性(一个cell绑定一个模型)
@class MJProduct; @property(nonatomic,strong) MJProduct *product;
4>来到cell的实现文件重写setter方法(在这里将product的属性赋值给Cell的子控件)
PS:在此之前先拖线拿到Cell的子控件
#import "MJProduct.h" - (void)setproduct:(MJProduct *)product { _product = product; //将product保存一份,将来可能用的上 self.iconView.image = [UIImage imageNamed:product.icon]; self.nameLabel.text = product.title; }
4.图标设置为圆角
来到MJProductCell.m,在awakeFromNib方法中设置
{ self.iconView.layer.cornerRadius = 8; self.iconView.clipsToBounds = YES; }
5.实现代理方法
#pragma mark - 代理方法 -(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { MJProduct *p = self.products[indexPath.item]; }
十四、设置05-抽取控制器父类(因为推送和提醒的界面和设置的界面很相似,所以可以利用设置界面的代码)
0.新建一个MJPushNoticeViewController,继承自UITableViewController
1>来到MJSettingViewController.m,将推送和提醒的destVc改为MJSettingViewController
2>来到MJPushNoticeViewController,将MJSettingViewController.m的代码全部复制过来,稍作修改
3>这样一来,MJPushNoticeViewController和MJSettingViewController很多地方就一样了(只有数据不同)。这样不好。应该抽一个父类出来。
1.新建一个控制器MJBaseSettingViewController,作为2个类的父类
1>把相同的东西放到父类,不同的东西放到子类执行
2>在子类的viewDidLoad中加载数据即可(调用setupGroup0等方法)
2.开奖推送设置:新建一个MJBaseSettingViewController的子类,添加数据即可
其他界面同理。跟玩一样的。
十五、设置06-帮助(加载网页)
1.新建一个类MJHelpViewController,继承自MJBaseSettingViewController
1>新建一个控制器MJHtmlViewController,继承自UIViewController
2>来到MJHtmlViewController
//在这个方法中将WebView修改为控制器View -(void)loadView { self.view = [[UIWebView alloc] init]; } -(void)viewDidLoad { [super viewDidLoad]; //设置标题 self.title = self.html.title; UIWebView *webView = (UIWebView *)self.view; webView.delegate = self; //创建URL NSURL *url = [NSBundle mainBundle] URLForResource:self.html.htmlwithExtension:nil]; //创建请求 NSURLRequest *request = [NSURLRequest requestWithURL:url]; //发送请求加载网页 [webView loadRequest:request]; //设置左上角的关闭按钮 self.navigtionItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"关闭" style:UIBarButtonItemStylePlain target:self action:@selector(close)]; } -(void)close { [self dismissVieControllerAnimated:YES completion:nil]; }
2.加载网页JSON
1>新建一个模型并添加属性(title,html)
2>提供构造方法和类方法快速返回一个模型对象
-(instancetype)initWithDict:(NSDictionary *)dict { if(self = [super init] ) { self.title = dict[@"title"]; self.html = dict:[@"html"]; self.ID = dict:[@"id"]; } return self; } +(instancetype)htmlWithDict:(NSDictionary *)dict { return [[self alloc] initWithDict:dict]; }
3>添加一个数组属性用来装模型并懒加载
@property(nonatomic,strong) NSMutableArray *htmls; -(NSArray *)htmls { if(_htmls == nil) { //JSON文件的路径 NSString *path = [[NSBundle mainBundle] pathForResource:@"help.json"ofType:nil]; //加载JSON文件 NSData *data = [NSData dataWithContentsOfFile:path]; //将JSON数据转为NSArray或者NSDictionary NSArray *dictArray = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableContainers error:nil]; //将字典转成模型 NSMutableArray *htmlArray = [NSMutableArray array]; for(NSDictionary *dict in dictArray){ MJHtml *html = [MJHtml htmlWithDict:dict]; [htmlArray addObject:html]; _htmls = htmlArray; //将可变数组赋值给属性 } return _htmls; }
3.来到MJHelpViewController的viewDidLoad
-(void)viewDidLoad
{
[super viewDidLoad]; //1.创建所有的item NSMutableArray *items = [NSMutableArray array]; for(MJHtml *html in self.htmls) { MJSettingItem *item = [MJSettingArrowItem itemWithTitle:html.title destVcClass:nil]; [items addObejct:item]; } //2.创建组 MJSettingGroup *group = [[MJSettingGroup alloc] init]; group.items = items; [self.data addObject:group]; } 4.来到MJHtmlViewController.h,添加一个html模型数据,用来接收html模型 @property(nonatomic,strong)MJHtml *html; 5.MJHelpViewController重写didSelectRow方法 { MJHtmlViewController *htmlVc = [[MJHtmlViewController alloc] init]; //将模型传给htmlVc htmlVc.html = self.htmls[indexPath.row]; MJNavigationController *nav = [[MJNavigationController alloc] initWithRootViewController:htmlVc]; [self presentViewController:nav animated:YES completion:nil]; }
6.实现web的代理方法
//网页加载完毕的时候调用 -(void)webViewDidFinishLoad:(UIWebView *)webView { //跳到id对应的网页标签 NSString *js = [NSString stringWithFormat:@"window.location.href = '#%@';", self.html.ID]; [webView stringByEvaluatingJavaScriptFromString:js]; }
十六、关于
1.新建一个控制器MJAboutViewController,继承自MJBaseSettingViewController
2.添加一个strong的webView属性
3.来到viewDidLoad
-(void)viewDidLoad
{ [super viewDidLoad]; self.webView = [[UIWebView alloc] init]; //1.具体数据 MJSettingViewController *mark = [MJSettingArrowItem itemWithTitle:@"评分支持" destVcClass:nil]; mark.option = ^{ NSString *appid = @"xxx"; NSString *str = [NSString stringWithFormat:@"itms-apps://itunes.apple.com/cn/app/id%@?mt=8", appid]; NSURL *url = [NSURL URLWithString:str]; [UIApplication sharedApplication] openURL:url]; }; MJSettingViewController *call = [MJSettingArrowItem itemWithTitle:@"客服电话" destVcClass:nil]; call.subtitle = @"10010" call.option = ^{ //创建一个UIWebView来加载URL,拨完后自动回到原应用 //PS:这个WebView不是用来展示的,是用来打开拨电话软件的。 [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"tel://10010"]]]; } MJSettingGroup *group = [[MJSettingGroup alloc] init]; group.items = @[mark,call]; [self.data addObejct:group]; //header(搞一个xib描述即可) }
4.来到MJSettingItem,添加一个子标题属性subtitle
来到MJSettingCell,在setupData方法中加入如下代码
self.detailTextLabel.text = self.item.subtitle;
十七、存储开关状态(BOOL值)
1.来到MJSettingCell监听开关
-(UIImageView *)switchView { if(_switchView == nil) { _switchView = [UISwitch alloc] init]; [_switchView addTarget:self action:@selector(switchStateChange) forControlEvents:UIControlEventsValueChange]; } return _switchView; } // 用来监听开关状态的改变 -(void)switchStateChange { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; //一旦改变,就拿到开关最新的状态跟开关左边的文字做Key存起来 [defaults setBool: self.switchView.isOn forKey:self.item.title]; [defaults synchronize]; }
2.下一次传Item的时候,来到setItem方法-->来到setupRightContent方法-->拿到开关-->开关是开还是关取决于对应的Item的title对应的Bool值,以此设置开关的状态
-(void)setupRightContent { ...... NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults setBool: self.switchView.isOn forKey:self.item.title]; ...... };
十八、短信分享和邮件分享
1.新建一个MJShareViewController,继承自UIBaseSettingViewController
2.添加2个Item,sms和mail
3.短信分享
1>#import <MessageUI/MessageUI.h>
2>遵守协议<MFMessageComposeViewControllerDelegate>
3>
4>实现代理方法
//点击取消按钮会自动调用
4.邮件分享(如果是真机则不需要判断能否发邮件)
1>#import <MessageUI/MessageUI.h>
2>遵守协议<MFMailComposeViewControllerDelegate>
3>
4>实现代理方法
十九、打开其他应用
0.在MJProductViewController中添加一个scheme属性(打开应用所用的url)和一个url属性(下载应用的url)还有一个identifier
1.来到MJProductViewController的代理方法didSelectItemAtIndexPath
{ MJProdcut *p = self.products[indexPath.item]; NSURL *customUrl = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@",p.scheme, p.identifier]]; UIApplication *app = [UIApplication sharedApplication]; if ([app canOpenURL:customUrl]) { //有安装此应用 //打开应用 [app openURL:customUrl]; } else { //没有安装应用 //打开appstore [app openURL:[NSURL URLWithString:p.url]]; } }