最終效果圖:
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
【網站聲明】本站部分內容來源于互聯網,旨在幫助大家更快的解決問題,如果有圖片或者內容侵犯了您的權益,請聯系我們刪除處理,感謝您的支持!