当前位置: 移动技术网 > IT编程>移动开发>IOS > iOS开发之路--微博骨架搭建

iOS开发之路--微博骨架搭建

2019年07月24日  | 移动技术网IT编程  | 我要评论

狂傲第一夫人,邪道王阿拉丁,fifa2005下载

最终效果图:


beyondviewcontroller.m

//
// beyondviewcontroller.m
// 20_帅哥no微博
//
// created by beyond on 14-8-3.
// copyright (c) 2014年 com.beyond. all rights reserved.
// 这个就是主控制器,分为两块,下面是dock栏,上面是显示不同的子控制器的view,子控制器最好用导航控制器包装一下,这样子控制器就自带了导航条,左按钮,标题,右按钮

/*
 无法点击,或点击 无响应的原因:
 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.设置背景
  // 按钮正常状态时侯的背景
  [barbtnitem setbackgroundimage:[uiimage imagenamed:@"navigationbar_button_background.png"] forstate:uicontrolstatenormal barmetrics:uibarmetricsdefault];
  // 按钮高亮状态时侯的背景
  [barbtnitem setbackgroundimage:[uiimage imagenamed:@"navigationbar_button_background_pushed.png"] forstate:uicontrolstatehighlighted barmetrics:uibarmetricsdefault];
  // 按钮未选中状态时侯的背景
  [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,它里面是由一个个按钮dockbtn组成

#import <uikit/uikit.h>
@class dockbtn;
@interface dock : uiview
// 添加一个item到dock(view),参数是图标名,和要显示 的标题 ,以及对应的子控制器的类名

- (void)adddockbtnwithiconname:(nsstring *)iconname title:(nsstring *)title viewcontrollerclassname:(nsstring *)viewcontrollerclassname;

// 当dock里面的某一个按钮被点击了的时候,调用代码块,处理相应的点击事件
@property (copy,nonatomic) void(^btnclickblock)(dockbtn *);



// 自定义方法,通过代码决定哪一个dockbtn被点击了,参数是 dock栏里面的那个将要被点击的按钮的索引
- (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,里面有五个按钮,分别是首页,我,消息,广场,更多

#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(按钮)到dock(view),参数是图标名,和要显示 的标题,以及对应的子控制器的类名
- (void)adddockbtnwithiconname:(nsstring *)iconname title:(nsstring *)title viewcontrollerclassname:(nsstring *)viewcontrollerclassname
{
  // 1.创建dock里面的按钮,并添加到dock里面
  dockbtn *dockbtn = [dockbtn buttonwithtype:uibuttontypecustom];
  [self addsubview:dockbtn];
  
  // 2.设置dockbtn正常状态下显示 的文字
  [dockbtn settitle:title forstate:uicontrolstatenormal];
  
  // 3.分类方法,设置按钮正常和选中状态下的图片,返回图片尺寸
  [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里面所有按钮的frame (使之平均分布)
  [self setdockbtnframes];
}

// 遍历设置dock里面所有按钮的frame (使之平均分布)
- (void)setdockbtnframes
{
  // 1,获取dock里面所有的按钮个数
  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;
      // 最重要的是,将选中的,置为当前的按钮,用成员变量记住,当点击dock上button的时候,先将current置为未选中,然后就被点击的按钮选中,最后最重要的是,将被点击的按钮重新置为当前 的按钮,用成员变量记住
      _currentdockbtn = btn;
    }
    
    // 4.因为点击dock里面的按钮的时候,要知道点击了哪一个按钮,所以给每个按钮绑定一个tag,作为它的索引
    btn.tag = i;
  }
}

// 最重要的是,当点击dock上button的时候,先将current置为未选中,然后就被点击的按钮选中,最后最重要的是,将被点击的按钮重新置为当前 的按钮,用成员变量记住
- (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栏里面的那个将要被点击的按钮的索引
- (void)setdockbtnclickedatindex:(int)index
{
  // 1.robust判断
  if (index < 0 || index >= self.subviews.count) return;
  
  // 2.通过索引 拿到对应的dockbtn viewwithtag也行
  dockbtn *btn = self.subviews[index];
  
  // 3.手动调用下面方法,相当于用户用手点击了dock里面对应的按钮
  [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上面的一个按钮,它有个成员是对应子控制器的类名,比如home按钮,成员属性的值就是叫: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上面的一个按钮,它有个成员是对应子控制器的类名,比如home按钮,成员属性的值就是叫:homeviewcontroller

#import "dockbtn.h"

// 按钮的内容的总宽度
#define kbtncontentwidth contentrect.size.width
// 按钮的内容的总高度
#define kbtncontentheight contentrect.size.height

// 按钮里的图片的所占的高度比例
#define kimageheightratio 0.6
// 按钮里的文本标签的所占的高度比例
#define klabelheightratio (1- kimageheightratio)

@implementation dockbtn

// 一些默认的通用的属性一定要写在构造方法里面
- (id)initwithframe:(cgrect)frame
{
  self = [super initwithframe:frame];
  if (self) {
    // 1.设置按钮文字属性 (局中,字体大小)
    self.titlelabel.textalignment = nstextalignmentcenter;
    self.titlelabel.font = [uifont systemfontofsize:12];
    
    // 2.设置按钮图片属性 (放大模式,取消按钮默认的点击高亮时的变色)
    self.imageview.contentmode = uiviewcontentmodescaleaspectfit;
    // 取消按钮默认的点击高亮时的变色(image is drawn darker when highlighted or pressed)
    self.adjustsimagewhenhighlighted = no;
    
    // 3.分类方法,设置按钮选中时的背景
    [self setbgimgforselected:@"tabbar_slider.png"];
  }
  return self;
}

#pragma mark 重写父类的方法(覆盖父类在高亮时所作的行为)
- (void)sethighlighted:(bool)highlighted
{
  // 因为 这里只需用按钮的选中和默认状态时的图片,所以要取消高亮状态的一些默认变色行为
  // 这里什么也不写,即取消,按钮本身 在高亮的时候执行的那些行为
  
}


#pragma mark 返回是按钮内部uiimageview的边框(按钮中的图片在上方,居中)
- (cgrect)imagerectforcontentrect:(cgrect)contentrect
{
  // 要居中,最快办法就是让按钮中的图片宽度和按钮一样宽
  return cgrectmake(0, 0, kbtncontentwidth, kbtncontentheight * kimageheightratio);
}

#pragma mark 返回是按钮内部uilabel的边框(按钮中的文字在下方,居中)
- (cgrect)titlerectforcontentrect:(cgrect)contentrect
{
  // 要居中,最快办法就是让按钮中的label宽度和按钮一样宽
  
  // 文字的y位于图片的下边线的上方5个单位距离,即距离图片上方5
  cgfloat labely = kbtncontentheight * kimageheightratio - 5;
  // 文字的高度是占按钮余下的所有高度
  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上面的一个按钮,类别

#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里面的五个栏目按钮的数据来源columnlist.plist


如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网