• iOS_20_微博的骨架结构


    最后效果图:






















    BeyondViewController.m

    //
    //  BeyondViewController.m
    //  20_帅哥no微博
    //
    //  Created by beyond on 14-8-3.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    //  这个就是主控制器,分为两块,以下是Dock栏,上面是显示不同的子控制器的view,子控制器最好用导航控制器包装一下,这样子控制器就自带了导航条,左button,标题,右button
    
    /*
     无法点击,或点击 无响应的原因:
     userInteractionEnabled = NO;
     hidden = YES
     alpha <= 0.01
     clearColor  ,view的颜色为透明,不能够被点击
     */
    
    #import "BeyondViewController.h"
    #import "Dock.h"
    #import "DockBtn.h"
    #import "Column.h"
    // 主控制器以下Dock的高度
    #define kDockHeight 44
    @interface BeyondViewController ()
    {
        // 从plist中载入 的栏目对象数组
        NSMutableArray *_columns;
        
        // 主控制器下方的Dock选项栏
        Dock *_dock;
        
        // 记录当前选中的子控制器,目的是将其view从父控制器的view中移除,为加入新的子控制器的view做准备
        UIViewController *_currentChildVC;
    }
    @end
    
    @implementation BeyondViewController
    
    - (BOOL)prefersStatusBarHidden
    {
        return NO;
        
    }
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        // 0.从plist载入 栏目数组,遍历数组,依据字典,生成一个一个栏目对象,存入栏目对象数组中
        _columns = [NSMutableArray array];
        NSBundle *mainBundle = [NSBundle mainBundle];
        NSString *fullPath = [mainBundle pathForResource:@"ColumnList.plist" ofType:nil];
        NSArray *arr = [NSArray arrayWithContentsOfFile:fullPath];
        
        for (NSDictionary *dict in arr) {
            Column *column = [Column columnWithDict:dict];
            [_columns addObject:column];
        }
        // 1.加入dock到主控制器方的下方
        [self addDock];
        
        // 2.一次性创建全部的子控制器,并用导航包装后,加入到当前控制器的childViewControllers
        [self createAllChildViewControllers];
        
        // 3.默认选中第0个控制器
        [self changeChildViewAtIndex:0 andChildVCClassName:@"HomeViewController"];
        
        // 4.一次性设置全局的导航栏上面的颜色主题样式
        [self setGlobalNavigationItemColorTheme];
       
    }
    #pragma mark 加入dock
    - (void)addDock
    {
        // 1.加入dock到主控制器方的下方
        _dock = [[Dock alloc] init];
        
        // 2.监听Dock内部Btn的点击,让控制器成为dock的代理属性,或者,为dock的成员blok赋值
        __unsafe_unretained BeyondViewController *beyond = self;
        _dock.btnClickBlock = ^(DockBtn *btn)
        {
            // 调用自己定义方法,更改子视图,參数1:索引號,參数2:子控制器的类名
            [beyond changeChildViewAtIndex:btn.tag andChildVCClassName:btn.viewControllerClassName];
        };
        
        
        // 3,设置Dock的frame
        _dock.frame = CGRectMake(0, self.view.frame.size.height - kDockHeight, self.view.frame.size.width, kDockHeight);
        log(@"_dock frame--%@",NSStringFromCGRect(_dock.frame));
        // 4,加入dock到主控制器方的view
        [self.view addSubview:_dock];
        
        // 2.遍历column对象数组,批量加入dock里面的DockBtn
        for (Column *column in _columns) {
            [_dock addDockBtnWithIconName:column.columnImgName title:column.columnName viewControllerClassName:column.columnClassName];
        }
        
        // 3.设置dock默认选中第0个
        [_dock setDockBtnClickedAtIndex:0];
        
    }
    // 自己定义方法,更改子视图,參数1:索引號,參数2:子控制器的类名
    - (void)changeChildViewAtIndex:(int)index andChildVCClassName:(NSString *)viewControllerClassName
    {
        log(@"点击了 %@",viewControllerClassName);
        if (self.childViewControllers.count > 0) {
            // 0,先取出新的子控制器,假设 新的子控制器就是当前的这个控制器,直接返回 好吗?
            UIViewController *childVC = [self childViewControllers][index];
            if (childVC == _currentChildVC)  return ;
            // 1,先移除先前的子控制器的view
            [_currentChildVC.view removeFromSuperview];
            
            // 2,加入新的子控制器的view到主控制器的view
            
            childVC.view.frame = CGRectMake(0, 20, 320, 416);
            //log(@"self view --%@",NSStringFromCGRect(self.view.frame));
            //log(@"childVC view --%@",NSStringFromCGRect(childVC.view.frame));
            // 不会反复加入view,由于一旦发现反复加入某个view,就会将它置于最上面,最好是,先移除旧的view,再加入新的view
            [self.view addSubview:childVC.view   ];
            
            // 3,重要,必须更新当前的子控制器,为下次移除做准备
            _currentChildVC = childVC;
        }
    }
    
    #pragma mark 创建全部的子控制器(一共5个,首面,消息,我,广场,很多其它)
    - (void)createAllChildViewControllers
    {
        // 1.遍历栏目对象数组,批量创建全部的子控制器,并用导航控制器包装,最后加入到self childViewControllers数组中保存
        for (Column *column in _columns) {
            Class c = NSClassFromString(column.columnClassName);
            UIViewController *childVC =nil;
            if ([NSStringFromClass(c) isEqualToString:@"MoreViewController"]) {
                // 特别注意:在继承了tableView之后,要想再使用group样式,必须在创建的时候指定样式为group,这儿特别指的是moreViewController
                childVC = [[c alloc]initWithStyle:UITableViewStyleGrouped];
            } else {
                childVC = [[c alloc]init];
            }
            // 设置导航栏的标题
            childVC.navigationItem.title = column.columnName;
            // 重写父类的方法
            [self addChildViewController:childVC];
        }
    }
    #pragma marck - 重写父类的方法
    // 为了在加入子控制器时,全部包装成一个个导航控制器,所以重写addChildViewController方法
    - (void)addChildViewController:(UIViewController *)childVC
    {
        UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:childVC];
        // 将包装成导航控制器的子控制器加入到主控制器中,这样每个子控制器就拥有自己的特有的导航条了
        [super addChildViewController:nav];
    }
    
    
    // 4.一次性设置全局的导航栏上面的颜色主题样式
    - (void)setGlobalNavigationItemColorTheme
    {
        // 1.导航栏
        // 1.1.操作navBar相当操作整个应用中的全部导航栏
        UINavigationBar *navBar = [UINavigationBar appearance];
        
        // 1.2.设置导航栏UINavigationBar的背景图片(拉伸)
        [navBar setBackgroundImage:[UIImage imageStretchedWithName:@"navigationbar_background.png"] forBarMetrics:UIBarMetricsDefault];
        // 1.3.设置状态栏背景,没有效果???
        [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
        
        
        // 1.4.设置导航栏UINavigationBar的Title文字属性,通过字典 设置
        NSMutableDictionary *navigationBarTitleDict = [NSMutableDictionary dictionary];
        // 前景色,即文字的颜色
        [navigationBarTitleDict setObject:[UIColor darkGrayColor] forKey:NSForegroundColorAttributeName];
        // 文字阴影取消,字典中不能放结构体,要用NSValue包装一下
        [navigationBarTitleDict setObject:[NSValue valueWithUIOffset:UIOffsetZero] forKey:NSShadowAttributeName];
        
        
        // 2.导航栏上面的item
        UIBarButtonItem *barBtnItem =[UIBarButtonItem appearance];
        // 2.1.设置背景
        // button正常状态时侯的背景
        [barBtnItem setBackgroundImage:[UIImage imageNamed:@"navigationbar_button_background.png"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
        // button高亮状态时侯的背景
        [barBtnItem setBackgroundImage:[UIImage imageNamed:@"navigationbar_button_background_pushed.png"] forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
        // button未选中状态时侯的背景
        [barBtnItem setBackgroundImage:[UIImage imageNamed:@"navigationbar_button_background_disable.png"] forState:UIControlStateDisabled barMetrics:UIBarMetricsDefault];
        
        
        // 2.2.设置barBtnItem的文字属性
        NSMutableDictionary *barItemTitleDict = [NSMutableDictionary dictionary];
        // barItemDict的文字颜色
        [barItemTitleDict setValue:[UIColor darkGrayColor] forKey:NSForegroundColorAttributeName];
        // barItemDict的字体
        [barItemTitleDict setValue:[UIFont systemFontOfSize:13] forKey:NSFontAttributeName];
        
        // 2.3.用字典 设置barBtnItem的标题文字属性
        [barBtnItem setTitleTextAttributes:barItemTitleDict forState:UIControlStateNormal];
        [barBtnItem setTitleTextAttributes:barItemTitleDict forState:UIControlStateHighlighted];
    }
    @end
    

    Dock.h

    //
    //  Dock.h
    //  20_帅哥no微博
    //
    //  Created by beyond on 14-8-3.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    //  Dock就是主控制器以下的一条bar,它里面是由一个个buttonDockBtn组成
    
    #import <UIKit/UIKit.h>
    @class DockBtn;
    @interface Dock : UIView
    //  加入一个item到Dock(View),參数是图标名,和要显示 的标题 ,以及相应的子控制器的类名
    
    - (void)addDockBtnWithIconName:(NSString *)iconName title:(NSString *)title viewControllerClassName:(NSString *)viewControllerClassName;
    
    // 当Dock里面的某一个button被点击了的时候,调用代码块,处理相应的点击事件
    @property (copy,nonatomic) void(^btnClickBlock)(DockBtn *);
    
    
    
    // 自己定义方法,通过代码决定哪一个dockBtn被点击了,參数是 Dock栏里面的那个将要被点击的button的索引
    - (void)setDockBtnClickedAtIndex:(int)index;
    @end
    


    Dock.m

    //
    //  Dock.m
    //  20_帅哥no微博
    //
    //  Created by beyond on 14-8-3.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    //  这个就是主控制器以下那一栏,Tabbar,也叫Dock,里面有五个button,各自是首页,我,消息,广场,很多其它
    
    #import "Dock.h"
    #import "DockBtn.h"
    
    @interface Dock()
    {
    
        // 当前选中了那个dockBtn
        DockBtn *_currentDockBtn;
    }
    @end
    
    @implementation Dock
    // init方法内部会调用initWithFramne
    - (id)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
            // 固有固定属性,设置Dock背景颜色(分类方法,使用imageName就可进行平铺)
            self.backgroundColor = [UIColor colorWithPatternImageNamed:@"tabbar_background.png"];
        }
        return self;
    }
    
    //  给外部提供一个接口,加入一个DockBtn(button)到Dock(View),參数是图标名,和要显示 的标题,以及相应的子控制器的类名
    - (void)addDockBtnWithIconName:(NSString *)iconName title:(NSString *)title viewControllerClassName:(NSString *)viewControllerClassName
    {
        // 1.创建dock里面的button,并加入到dock里面
        DockBtn *dockBtn = [DockBtn buttonWithType:UIButtonTypeCustom];
        [self addSubview:dockBtn];
        
        // 2.设置dockBtn正常状态下显示 的文字
        [dockBtn setTitle:title forState:UIControlStateNormal];
        
        // 3.分类方法,设置button正常和选中状态下的图片,返回图片尺寸
        [dockBtn setBtnImgForNormalAndSelectedWithName:iconName];
        
        // 4.设置dockBtn相应点击后,要实例化的子控制器的类名
        [dockBtn setViewControllerClassName:viewControllerClassName];
        
        // 5.监听点击,仅仅要按下就响应,(事件先传递给Dock的方法,Dock的方法中再通过调用属性block代码块,从而调用到主控制器里面的代码,原因是:在主控制器里面实例化的dock,在Dock里面才实例化的dockBtn,因此,主控制器并不知道dockItem的存在)
        [dockBtn addTarget:self action:@selector(dockBtnClick:) forControlEvents:UIControlEventTouchDown];
        
        // 6.遍历设置Dock里面全部button的frame (使之平均分布)
        [self setDockBtnFrames];
    }
    
    // 遍历设置Dock里面全部button的frame (使之平均分布)
    - (void)setDockBtnFrames
    {
        // 1,获取dock里面全部的button个数
        int dockBtnNum = self.subviews.count;
        
        // 2,依据dock中,当前当前有多少个DockBtn,计算出每一个dockBtn的宽度(self是dock,320*44)
        CGFloat dockBtnWidth = self.frame.size.width / dockBtnNum;
        CGFloat dockBtnHeight = self.frame.size.height;
        
        for (int i = 0; i <  dockBtnNum; i++) {
            // 1.逐个取出子控件
            DockBtn *btn = self.subviews[i];
            
            // 2.依据索引 计算它的x
            btn.frame = CGRectMake(i * dockBtnWidth, 0, dockBtnWidth, dockBtnHeight);
            
            // 3.初始化的时候,将第0个btn(即首页)选中
            if (i == 0) {
                btn.selected = YES;
                // 最重要的是,将选中的,置为当前的button,用成员变量记住,当点击dock上button的时候,先将current置为未选中,然后就被点击的button选中,最后最重要的是,将被点击的button又一次置为当前 的button,用成员变量记住
                _currentDockBtn = btn;
            }
            
            // 4.由于点击dock里面的button的时候,要知道点击了哪一个button,所以给每一个button绑定一个tag,作为它的索引
            btn.tag = i;
        }
    }
    
    // 最重要的是,当点击dock上button的时候,先将current置为未选中,然后就被点击的button选中,最后最重要的是,将被点击的button又一次置为当前 的button,用成员变量记住
    - (void)dockBtnClick:(DockBtn  *)btn
    {
        // 1.让当前的btn取消选中
        _currentDockBtn.selected = NO;
        
        // 2.让新的btn选中
        btn.selected = YES;
        
        // 3.最后,让新的btn变为当前选中btn
        _currentDockBtn = btn;
        
        // 4.调用block,即主控制中传递过来的代码块,目的是处理点击之后的实例化相应的子控制器
        if (_btnClickBlock) {
            // 将參数 DockBtn传递过去,给主控制器,它里面成员变量记住了它相应的控制器的类名
            _btnClickBlock(btn);
        }
    }
    
    
    
    // 自己定义方法,通过代码决定哪一个dockBtn被点击了,參数是 Dock栏里面的那个将要被点击的button的索引
    - (void)setDockBtnClickedAtIndex:(int)index
    {
        // 1.robust推断
        if (index < 0 || index >= self.subviews.count) return;
        
        // 2.通过索引 拿到相应的DockBtn  viewWithTag也行
        DockBtn *btn = self.subviews[index];
        
        // 3.手动调用以下方法,相当于用户用手点击了dock里面相应的button
        [self dockBtnClick:btn];
    }
    @end
    


    DockBtn.h

    //
    //  DockBtn.h
    //  20_帅哥no微博
    //
    //  Created by beyond on 14-8-4.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    //  一个DockBtn代表Dock上面的一个button,它有个成员是相应子控制器的类名,比方Homebutton,成员属性的值就是叫:HomeViewController
    
    #import <UIKit/UIKit.h>
    
    @interface DockBtn : UIButton
    // 每一个dockBtn中,用一个成员记住 它相应的控制器的类名
    @property (nonatomic,copy) NSString *viewControllerClassName;
    @end
    


    DockBtn.m

    //
    //  DockBtn.m
    //  20_帅哥no微博
    //
    //  Created by beyond on 14-8-4.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    //  一个DockBtn代表Dock上面的一个button,它有个成员是相应子控制器的类名,比方Homebutton,成员属性的值就是叫:HomeViewController
    
    #import "DockBtn.h"
    
    // button的内容的总宽度
    #define kBtnContentWidth contentRect.size.width
    // button的内容的总高度
    #define kBtnContentHeight contentRect.size.height
    
    // button里的图片的所占的高度比例
    #define kImageHeightRatio 0.6
    // button里的文本标签的所占的高度比例
    #define kLabelHeightRatio (1- kImageHeightRatio)
    
    @implementation DockBtn
    
    // 一些默认的通用的属性一定要写在构造方法里面
    - (id)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
            // 1.设置button文字属性 (局中,字体大小)
            self.titleLabel.textAlignment = NSTextAlignmentCenter;
            self.titleLabel.font = [UIFont systemFontOfSize:12];
            
            // 2.设置button图片属性 (放大模式,取消button默认的点击高亮时的变色)
            self.imageView.contentMode = UIViewContentModeScaleAspectFit;
            // 取消button默认的点击高亮时的变色(image is drawn darker when highlighted or pressed)
            self.adjustsImageWhenHighlighted = NO;
            
            // 3.分类方法,设置button选中时的背景
            [self setBgImgForSelected:@"tabbar_slider.png"];
        }
        return self;
    }
    
    #pragma mark 重写父类的方法(覆盖父类在高亮时所作的行为)
    - (void)setHighlighted:(BOOL)highlighted
    {
        // 由于 这里仅仅需用button的选中和默认状态时的图片,所以要取消高亮状态的一些默认变色行为
        // 这里什么也不写,即取消,button本身 在高亮的时候运行的那些行为
        
    }
    
    
    #pragma mark 返回是button内部UIImageView的边框(button中的图片在上方,居中)
    - (CGRect)imageRectForContentRect:(CGRect)contentRect
    {
        // 要居中,最快办法就是让button中的图片宽度和button一样宽
        return CGRectMake(0, 0, kBtnContentWidth, kBtnContentHeight * kImageHeightRatio);
    }
    
    #pragma mark 返回是button内部UILabel的边框(button中的文字在下方,居中)
    - (CGRect)titleRectForContentRect:(CGRect)contentRect
    {
        // 要居中,最快办法就是让button中的Label宽度和button一样宽
        
        // 文字的y位于图片的下边线的上方5个单位距离,即距离图片上方5
        CGFloat labelY = kBtnContentHeight * kImageHeightRatio - 5;
        // 文字的高度是占button余下的全部高度
        CGFloat labelHeight = kBtnContentHeight - labelY;
        return CGRectMake(0, labelY, kBtnContentWidth, labelHeight);
    }
    
    @end
    


    模型Column.h

    //
    //  Column.h
    //  20_帅哥no微博
    //
    //  Created by beyond on 14-8-4.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    //  1个Column模型相应Dock上面的一个button,类别
    
    #import <Foundation/Foundation.h>
    
    // 数据模型 代表一个栏目
    @interface Column : NSObject
    
    // 栏目名称
    @property (nonatomic,copy)NSString *columnName;
    // 栏目图片名称
    @property (nonatomic,copy)NSString *columnImgName;
    // 栏目相应的控制器的类名
    @property (nonatomic,copy)NSString *columnClassName;
    // UI控件用weak,字符串用copy,其它对象用strong
    
    // 提供一个类方法,即构造函数,返回封装好数据的对象(返回id亦可)
    + (Column *)columnNamed:(NSString *)columnName imgName:(NSString*)columnImgName className:(NSString *)columnClassName;
    
    // 类方法,字典 转 对象 相似javaBean一次性填充
    + (Column *)columnWithDict:(NSDictionary *)dict;
    
    // 对象方法,设置对象的属性后,返回对象
    - (Column *)initWithDict:(NSDictionary *)dict;
    
    @end
    


    模型Column.m

    //
    //  Column.m
    //  20_帅哥no微博
    //
    //  Created by beyond on 14-8-4.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    //
    
    #import "Column.h"
    
    @implementation Column
    // 返回一个包括了 栏目相应控制器名字的 对象实例
    + (Column *)columnNamed:(NSString *)columnName imgName:(NSString *)columnImgName className:(NSString *)columnClassName
    {
        // 为了兼容子类 使用self
        Column *column = [[self alloc]init];
        column.columnName = columnName;
        column.columnImgName = columnImgName;
        column.columnClassName = columnClassName;
        return column;
    }
    
    
    // 类方法,字典 转 对象 相似javaBean一次性填充
    + (Column *)columnWithDict:(NSDictionary *)dict
    {
        // 仅仅是调用对象的initWithDict方法,之所以用self是为了对子类进行兼容
        return [[self alloc]initWithDict:dict];
    }
    
    // 对象方法,设置对象的属性后,返回对象
    - (Column *)initWithDict:(NSDictionary *)dict
    {
        // 必须先调用父类NSObject的init方法
        if (self = [super init]) {
            // 设置对象自己的属性
            [self setValuesForKeysWithDictionary:dict];
        }
        // 返回填充好的对象
        return self;
    }
    
    @end
    

    Dock里面的五个栏目button的数据来源ColumnList.plist







    版权声明:本文博主原创文章。博客,未经同意不得转载。

  • 相关阅读:
    相由心生
    超级唯美的爱情语句(中英)
    有多少人败给“对不起,家里不同意”
    请善待老公,其实男人不容易
    摩托车西藏之旅实战攻略
    女人眼里36种不靠谱男人
    什么是爱?什么是幸福?
    踏板车的节油措施汇总
    史上最无语最蛋疼新闻标题
    太他妈幽默了,丫不去写书真浪费了
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4837927.html
Copyright © 2020-2023  润新知