首页 > 技术文章 > iOS开发-综合UI案例-彩票

marshall-yin 2015-09-01 16:32 原文

一、搭建基本框架
规范:一般有两种。

按模块分(各个模块里分为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]];
}
}

 

推荐阅读