zoukankan      html  css  js  c++  java
  • iOS开发-综合UI案例-*

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

    按模块分(各个模块里分为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]];
    }
    }
  • 相关阅读:
    和老外交流最常用1000句口语 (一)
    flash自定义右键菜单
    和老外交流最常用1000句口语 (二)
    EBS默认的登录账户和密码
    实例13. 库存补充操作——最小最大计划(MinMax Planning)
    EBS R12常用数据表
    Oracle 软件的行业划分 和 Oracle 公司内部职业划分
    在Org Parameter设置Subinventory Account
    物流(Logistics)的概念
    实例12. 库存补充操作——看板补充(Kanban Replenishment)
  • 原文地址:https://www.cnblogs.com/marshall-yin/p/4776066.html
Copyright © 2011-2022 走看看