最終效果圖:
MainStoryBoard示意圖:
BeyondViewController.h
//// BeyondViewController.h// 19_抽屜效果_仿網(wǎng)易//// Created by beyond on 14-8-1.// Copyright (c) 2014年 com.beyond. All rights reserved.//#import#import "LeftTableViewControllerDelegate.h"@interface BeyondViewController : UIViewController// 左半邊 (顯示 的是欄目列表 )@property (weak, nonatomic) IBOutlet UIView *leftView;// 右半邊 (顯示 的是個人信息設(shè)置視圖)@property (weak, nonatomic) IBOutlet UIView *rightView;// 最上面,最大的全屏的是主視圖@property (weak, nonatomic) IBOutlet UIView *mainView;// 上面標題狀態(tài)欄視圖中的標題按鈕 (網(wǎng)易的Logo圖片和欄目的名稱 水平排列)@property (weak, nonatomic) IBOutlet UIButton *titleBtn;// mainView的下半部分 是 正文的view,顯示子欄目的view@property (weak, nonatomic) IBOutlet UIView *contentView;// pan 拽 手勢處理- (IBAction)panGesture:(UIPanGestureRecognizer *)sender;// mainView的上半部分 標題狀態(tài)欄視圖中的左,右按鈕- (IBAction)btnClick:(UIButton *)sender;@end
BeyondViewController.m
//// BeyondViewController.m// 19_抽屜效果_仿網(wǎng)易//// Created by beyond on 14-8-1.// Copyright (c) 2014年 com.beyond. All rights reserved.//#import "BeyondViewController.h"#import "LeftTableViewController.h"#import "RightViewController.h"#import "Column.h"#import// 手勢結(jié)束時的x#define kEndX frame.origin.x// 左view的寬度#define kLeftWidth _leftView.frame.size.width// 右view的寬度#define kRightWidth _rightView.frame.size.width// 對協(xié)議進行提前聲明@protocol LeftTableViewControllerDelegate ;@interface BeyondViewController () { // 手指按下的時候,記住,mainView的起始x CGFloat _startX; // 成員變量,記住左邊控制器的實例 LeftTableViewController *_leftVC; // 成員變量,記住右邊控制器的實例 RightViewController *_rightVC; // 字典 ,記住所有實例化了 欄目的子控制器,避免每次都重新創(chuàng)建 NSMutableDictionary *_columnViewControllers; }@end@implementation BeyondViewController// 隱藏狀態(tài)欄- (BOOL)prefersStatusBarHidden{ return YES;}- (void)viewDidLoad{ [super viewDidLoad]; _titleBtn.backgroundColor = [UIColor clearColor]; // 0 字典 ,記住所有實例化了 欄目的子控制器,避免每次都重新創(chuàng)建 _columnViewControllers = [NSMutableDictionary dictionary]; // 0,設(shè)置導航條bar的背景 為網(wǎng) 易 紅 //[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:@"bg.png"] forBarMetrics:UIBarMetricsDefault]; // 狀態(tài)條顏色 改成默認的樣式 //[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault; // 1,添加左邊控制器的view到左邊的view里面 _leftVC = [[LeftTableViewController alloc]init]; // 關(guān)鍵代碼 為了拿到左邊控制器的某一行被點擊時候,對應的欄目數(shù)據(jù)模型對象,主控制器成為了左邊控制器的代理,遵守了它定好的協(xié)議,實現(xiàn)了協(xié)議中的方法,從而拿到左邊控制器被點擊行號對應的數(shù)據(jù)模型對象 _leftVC.delegate = self; _leftVC.view.frame = self.leftView.bounds; [self.leftView addSubview:_leftVC.view]; // 2,同理,添加右邊控制器的view到右邊的view里面 _rightVC = [[RightViewController alloc]init]; _rightVC.view.frame = self.rightView.bounds; [self.rightView addSubview:_rightVC.view]; // 3,第一次加載時候,就就應該顯示新聞 子欄目的控制器到導航控制器,再將導航控制器的view添加到 mainView里面 [self firstLoading]; }// 自定義方法,第一次加載時候,就就應該顯示新聞 子欄目的控制器到導航控制器,再將導航控制器的view添加到 mainView里面- (void)firstLoading{ Column * column = [Column columnNamed:@"新聞" imgName:@"news.png" className:@"NewsViewController"]; // 僅需手動調(diào)用一個 LeftViewController的代理 方法,leftTableViewRowClicked,傳遞一個新聞 子欄目即可 [self leftTableViewRowClicked:column];}// pan 拽 手勢處理- (IBAction)panGesture:(UIPanGestureRecognizer *)sender{ // 如果是剛按下的狀態(tài),則記住,mainView的起始x if (UIGestureRecognizerStateBegan == sender.state) { _startX = self.mainView.frame.origin.x; } // 平移拖動的距離 CGPoint delta = [sender translationInView:_mainView]; CGRect frame = self.mainView.frame; // 計算新的x值,并做健壯性判斷 kEndX = _startX + delta.x; // 1,限制最大拖動范圍 if (kEndX >= kLeftWidth) { kEndX = kLeftWidth; } if (kEndX 0) { // NSLog(@"--調(diào)用頻率相當高--"); _rightView.hidden = YES; _leftView.hidden = NO; } else { _rightView.hidden = NO; _leftView.hidden = YES; } if (UIGestureRecognizerStateEnded == sender.state) { // 手勢結(jié)束的時候,需進行robust判斷 // 2,分析end松手時候,的位置x,決定展開到什么程度/* // 2.1 如果只向右拖了一點點,小于 1/2 的左view的寬度,則歸0 if (kEndX = 0) { kEndX = 0; }else if (kEndX >= 0.5*kLeftWidth && kEndX - 0.5*kRightWidth && kEndX 0) { kEndX = kLeftWidth; }else if (_startX == 0 && delta.x 0){ // 起始為- kRightWidth,delta.x大于0 代表向右滑動 kEndX = 0; } } // 最后,才設(shè)置mainView的新的frame [UIView animateWithDuration:0.2 animations:^{ self.mainView.frame=frame; }]; // 最后,為mainView所在的圖層 添加陰影效果 [self addShadowFormainViewWithEndX:kEndX]; }// 自定義方法,為mainView所在的圖層 添加陰影效果 (調(diào)用頻率相當高)- (void)addShadowFormainViewWithEndX:(CGFloat)endX{ // 1,點擊工程,加號,導入第3方框架 #import // 2,拿到mainView所在的圖層,設(shè)置陰影 參數(shù) // NSLog(@"調(diào)用頻率很高---"); _mainView.layer.shadowColor = [UIColor blackColor].CGColor; _mainView.layer.shadowOpacity = 0.5; if (endX >= 0) { _mainView.layer.shadowOffset = CGSizeMake(-5, 0); } else { _mainView.layer.shadowOffset = CGSizeMake(5, 0); } }// 單擊按鈕,也一樣可以展開 左右側(cè)邊欄- (IBAction)btnClick:(UIButton *)sender{ // 定義一個臨時變量 CGFloat startX = _mainView.frame.origin.x; // 先為mainView所在的圖層 添加陰影效果 [self addShadowFormainViewWithEndX:sender.tag == 1?1:-1]; // 定義一個臨時變量 CGFloat tempEndX = 0; // 左邊的按鈕被單擊 if (1 == sender.tag) { // 隱藏右半邊 _leftView.hidden = NO; _rightView.hidden = YES; if (startX == 0) { tempEndX = kLeftWidth; }else if (startX == kLeftWidth){ tempEndX = 0; } } else { // 單擊右邊按鈕, 隱藏左半邊 _leftView.hidden = YES; _rightView.hidden = NO; if (startX == 0) { tempEndX = - kRightWidth; }else if (startX == - kRightWidth){ tempEndX = 0; } } // 最后才設(shè)置mainView的x,調(diào)用抽取出來的公共代碼,設(shè)置mainView的x,參數(shù)是endX [self setmainViewX:tempEndX]; }// 抽取出來的公共代碼,設(shè)置mainView的x,參數(shù)是endX- (void)setmainViewX:(CGFloat)endX{ CGRect frame = self.mainView.frame; frame.origin.x = endX; [UIView animateWithDuration:0.2 animations:^{ self.mainView.frame=frame; }]; }// 最關(guān)鍵的方法,左邊控制器的代理 方法,當前左邊控制器中的某一行被點擊的時候 會調(diào)用- (void)leftTableViewRowClicked:(id)columnSelected{ Column *column = (Column *)columnSelected; // 1,關(guān)閉左邊的控制======================= // 調(diào)用抽取出來的公共代碼,設(shè)置mainView的x,參數(shù)是endX [self setmainViewX:0]; // 2,更改標題按鈕上面的文字 _titleBtn.titleLabel.text = column.columnName; // 根據(jù)欄目數(shù)據(jù)模型中的類名,實例化對應欄目的控制器,并且將其設(shè)置為導航控制器的根控制器,最后將導航控制器的view添加到mainView中,目的是方便設(shè)置導航條,以及,各控制器的跳轉(zhuǎn) // 2,從緩存字典中取,如果子控制器字典有曾經(jīng)創(chuàng)建過的子控制器,直接取出來用 UIViewController *columnVC = _columnViewControllers[column.columnClassName]; // 如果子控制器字典中沒有保存過該欄目的控制器,才要創(chuàng)建子控制器 if (columnVC == nil) { Class c = NSClassFromString(column.columnClassName); columnVC = [[c alloc]init]; // 并且一定要將其放到 子控制器字典里面,存起來 [_columnViewControllers setObject:columnVC forKey:column.columnClassName]; } // 4,移除contentView中的正在顯示的舊的子view if (_contentView.subviews.count > 0) { UIView *oldView = [_contentView subviews][0]; [oldView removeFromSuperview]; } // 5,最后將子控制器的view添加到contentView中,顯示 columnVC.view.frame = _contentView.bounds; [self.contentView addSubview:columnVC.view]; NSLog(@"%@",self.contentView); // 在添加到mainView之前 ,先得到mainView導航控制器的子控制器,并將其移除(如果有的話),然后才將新的欄目對應的子控制器添加到導航控制器容器中,注意,這兒可以用字典 記住 所有的已經(jīng)實例化出來 的欄目子控制器,這樣就避免每次都alloc創(chuàng)建新的欄目子控制器,而是只需要根據(jù)類名,從字典取出上一次實例化了的同一欄目的子控制器即可 }@end
欄目數(shù)據(jù)模型Column.h
//// Column.h// 19_抽屜效果_仿網(wǎng)易//// Created by beyond on 14-8-1.// Copyright (c) 2014年 com.beyond. All rights reserved.//#import// 數(shù)據(jù)模型 代表一個欄目@interface Column : NSObject// 欄目名稱@property (nonatomic,copy)NSString *columnName;// 欄目圖片名稱@property (nonatomic,copy)NSString *columnImgName;// 欄目對應的控制器的類名@property (nonatomic,copy)NSString *columnClassName;// UI控件用weak,字符串用copy,其他對象用strong// 提供一個類方法,即構(gòu)造函數(shù),返回封裝好數(shù)據(jù)的對象(返回id亦可)+ (Column *)columnNamed:(NSString *)columnName imgName:(NSString*)columnImgName className:(NSString *)columnClassName;@end
欄目數(shù)據(jù)模型Column.m
//// Column.m// 19_抽屜效果_仿網(wǎng)易//// Created by beyond on 14-8-1.// Copyright (c) 2014年 com.beyond. All rights reserved.// 數(shù)據(jù)模型 代表一條欄目#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;}@end
左邊控制器定義好的協(xié)議LeftTableViewControllerDelegate.h
//// LeftTableViewControllerDelegate.h// 19_抽屜效果_仿網(wǎng)易//// Created by beyond on 14-8-1.// Copyright (c) 2014年 com.beyond. All rights reserved.//#import#import "Column.h"http:// 左邊控制器 定義的代理/協(xié)議 它通過調(diào)用自己的成員屬性(即代理)的該方法,將數(shù)據(jù)傳遞出去(給它的代理去使用) (其實 是主控制器想要數(shù)據(jù),所以主控制器在實例化左邊控制器的時候,要設(shè)置左邊控制器對應的代理 為 主控制器 自身)@protocol LeftTableViewControllerDelegate - (void)leftTableViewRowClicked:(Column *)columnSelected;@end
LeftTableViewController.h
//// LeftTableViewController.h// 19_抽屜效果_仿網(wǎng)易//// Created by beyond on 14-8-1.// Copyright (c) 2014年 com.beyond. All rights reserved.//#import// 對協(xié)議進行提前聲明@protocol LeftTableViewControllerDelegate;@interface LeftTableViewController : UITableViewController// 代理 用weak,防止循環(huán)問題,可以是任意類型,但必須遵守協(xié)議@property (nonatomic,weak) id delegate;@end
LeftTableViewController.m
//
//? LeftTableViewController.m
//? 19_抽屜效果_仿網(wǎng)易
//
//? Created by beyond on 14-8-1.
//? Copyright (c) 2014年 com.beyond. All rights reserved.
//
#import "LeftTableViewController.h"
#import "Column.h"
#import "LeftTableViewControllerDelegate.h"
@interface LeftTableViewController ()
{
??? // 欄目數(shù)組,保存的是左邊欄目列表中的所有欄目對象
??? NSArray *_arr;
}
@end
@implementation LeftTableViewController
- (void)viewDidLoad
{
??? [super viewDidLoad];
??? // 新聞 欄目
??? Column *newsColumn = [Column columnNamed:@"新聞" imgName:@"news.png" className:@"NewsViewController"];
??? // 圖片 欄目
??? Column *picColumn = [Column columnNamed:@"圖片" imgName:@"pic.png" className:@"PicViewController"];
??? // 圖片 欄目
??? Column *commentColumn = [Column columnNamed:@"跟帖" imgName:@"comment.png" className:@"CommentViewController"];
??? // 以后要添加欄目,只要改這里就可以了
???
???
??? // 將欄目對象,一次性全添加到不可變數(shù)組中
??? _arr = @[newsColumn,picColumn,commentColumn];
???
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
??? return _arr.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
??? static NSString *cellID = @"leftVC";
??? // 下面這個dequeue只能用于storyboard或xib中
??? // UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID forIndexPath:indexPath];
??? //
??? UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
???
??? if (cell == nil) {
??????? cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
??? }
???
??? // 設(shè)置獨一無二的數(shù)據(jù)
??? Column *column = _arr[indexPath.row];
??? cell.textLabel.text = column.columnName;
??? cell.imageView.image = [UIImage imageNamed:column.columnImgName];
??? return cell;
}
// 點擊一行時,主控制中的主視圖必須展示相應欄目的內(nèi)容,因此,必須實例化對應點擊的行的欄目控制器,并用添加到導航控制器,調(diào)用代理 的方法傳遞數(shù)據(jù)給代理 使用,此處的代理 其實就是 主控制器
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
??? // 先取消默認的點擊 高亮的顏色
??? [tableView deselectRowAtIndexPath:indexPath animated:YES];
??? // 取出對應行的數(shù)據(jù)模型(欄目)
??? Column *column = _arr[indexPath.row];
???
??? if ([self.delegate respondsToSelector:@selector(leftTableViewRowClicked:)]) {
???????
??????? // 傳遞數(shù)據(jù)給主控制器 BeyondViewController,通過代理
??????? // 關(guān)鍵代碼~
??????? [self.delegate leftTableViewRowClicked:column];
??? }
???
???
}
@end
RightViewController.xib
NewsViewController.xib
PicViewController.xib
CommentViewController.xib
新聞熱點
疑難解答
圖片精選