標籤:ios ipad 團購 collectionview 繼承
最終:
控制器繼承關係圖:
說明:
點擊主控制器左側的Dock上的按鈕,
比如【團購】、【收藏】、【地圖】時,
實現的功能有許多相同之處。
具體說明如下:
點擊【團購】,以九宮格的形式顯示一個個團購,
並且,點擊一個Cell時,展示該Cell對應的團購詳情
點擊【收藏】,以九宮格的形式顯示一個個已經歸檔的團購模型,
並且,點擊一個Cell時,展示該Cell對應的團購詳情
點擊【地圖】,以MapView上一個個圖釘的形式顯示團購模型(通過城市名+經度+緯度+半徑作參數發送請求)
並且,點擊一個圖釘時,展示該Cell對應的團購詳情
上述各控制器的主要功能和方法聲明如下:
抽取的父類如下:
父類ShowDealDetailController
負責建立並展示和隱藏封裝的【團購詳情控制器】
私人成員:Cover
//// ShowDealDetailController.h// 帥哥_團購//// Created by beyond on 14-8-20.// Copyright (c) 2014年 com.beyond. All rights reserved.// 基類,父類,展示 訂單詳情控制器的控制器,當點擊XXX時,需要展示團購詳情的時候,就繼承這個控制器即可,內部實現了建立並顯示訂單訂單詳情控制器,以及隱藏訂單詳情控制器#import <UIKit/UIKit.h>@class Deal;@interface ShowDealDetailController : UIViewController// 顯示訂單詳情的控制器所依賴的資料模型(資料來源)- (void)showDetail:(Deal *)deal;@end
//// ShowDealDetailController.m// 帥哥_團購//// Created by beyond on 14-8-20.// Copyright (c) 2014年 com.beyond. All rights reserved.// 基類,父類,展示 訂單詳情控制器的控制器,當點擊XXX時,需要展示團購詳情的時候,就繼承這個控制器即可,內部實現了建立並顯示訂單訂單詳情控制器,以及隱藏訂單詳情控制器#import "ShowDealDetailController.h"#import "Cover.h"// 真正的用xib封裝的詳情控制器,建立時,要傳入deal資料來源#import "DealDetailController.h"// 自己封裝的全域統一樣式的導航控制器#import "BeyondNavigationController.h"// 真正的用xib封裝的詳情控制器 其高度可變,但是寬度一般是要固定的#define kDealDetailVCWidth 600@interface ShowDealDetailController (){ // 遮蓋 Cover *_cover;}@end@implementation ShowDealDetailController#pragma mark 顯示詳情控制器- (void)showDetail:(Deal *)deal{ // 1.顯示遮蓋 if (_cover == nil) { _cover = [Cover coverWithTarget:self action:@selector(hideDetail)]; } // self只是子(根)控制器,self的上方是導航控制器的導覽列,因此self.navigationController是拿到整個導航控制器的view(包括導覽列和它的根(子)控制器) // ????導航控制器的view的寬高(包括導覽列)????因為self本控制器,每次出場,都是先經過導航控制器封裝過的,因為self的頂部還有導覽列 _cover.frame = self.navigationController.view.bounds; // 建立時透明,之後動畫變黑 _cover.alpha = 0; [UIView animateWithDuration:kDefaultAnimDuration animations:^{ [_cover alphaReset]; }]; // ???? 添加到導航控制器的view最上面,(蓋住導覽列和其根控制器) [self.navigationController.view addSubview:_cover]; // 2.建立並展示團購詳情控制器 DealDetailController *detailVC = [[DealDetailController alloc] init]; // 重要~~~其導航條左邊是關閉,點擊後調用本控制器的方法執行動畫關閉,建立出來的DealDetailController,之所以不到DealDetailController內部去設定這個按鈕,是因為在內部無法方便地調用外部的這個隱藏DealDetailController的方法 detailVC.navigationItem.leftBarButtonItem = [UIBarButtonItem itemWithIcon:@"btn_nav_close.png" highlightedIcon:@"btn_nav_close_hl.png" target:self action:@selector(hideDetail)]; // 需提供資料來源,供其內部的從xib產生的子控制項顯示資料 detailVC.deal = deal; // 用導航控制器封裝 真正的團購詳情控制器 BeyondNavigationController *nav = [[BeyondNavigationController alloc] initWithRootViewController:detailVC]; // 為實現詳情控制器的抽屜效果,監聽pan手勢拖拽 [nav.view addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(drag:)]]; // 詳情控制器始終靠右邊,所以左邊距伸縮,高度也伸縮 nav.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleLeftMargin; // ????在遮罩的右邊,意思是先完全看不見,然後動畫慢慢向左移動出場??? // 真正的用xib封裝的詳情控制器 其高度可變,但是寬度一般是要固定的 nav.view.frame = CGRectMake(_cover.frame.size.width, 0, kDealDetailVCWidth, _cover.frame.size.height); // 當2個控制器互為父子關係時,它們的view也是互為父子關係,因為如果只加view,不加控制器,那麼控制器在本方法調用完畢之後就會被銷毀,因為其為局部變數 [self.navigationController.view addSubview:nav.view]; [self.navigationController addChildViewController:nav]; // 動畫向左慢慢移動出場,顯示出詳情控制器 [UIView animateWithDuration:kDefaultAnimDuration animations:^{ CGRect f = nav.view.frame; f.origin.x -= kDealDetailVCWidth; nav.view.frame = f; }];}#pragma mark 隱藏詳情控制器- (void)hideDetail{ // 取得封裝了詳情控制器的導航控制器,動畫隱藏其view UIViewController *nav = [self.navigationController.childViewControllers lastObject]; [UIView animateWithDuration:0.3 animations:^{ // 1.隱藏遮蓋 _cover.alpha = 0; // 2.隱藏控制器 CGRect f = nav.view.frame; f.origin.x += kDealDetailVCWidth; nav.view.frame = f; } completion:^(BOOL finished) { [_cover removeFromSuperview]; [nav.view removeFromSuperview]; [nav removeFromParentViewController]; }];}#pragma mark - 讓詳情控制器有抽屜效果,外加彈簧效果- (void)drag:(UIPanGestureRecognizer *)pan{ // 向左走為負 CGFloat tx = [pan translationInView:pan.view].x; // 手勢結束時,即鬆手 if (pan.state == UIGestureRecognizerStateEnded) { CGFloat halfW = pan.view.frame.size.width * 0.5; if (tx >= halfW) { // 已經往右邊挪動超過一半了 [self hideDetail]; } else { [UIView animateWithDuration:kDefaultAnimDuration animations:^{ pan.view.transform = CGAffineTransformIdentity; }]; } } else { // 移動控制器的view if (tx < 0) { // 向左邊拽 tx *= 0.4; } pan.view.transform = CGAffineTransformMakeTranslation(tx, 0); }}@end
父類BaseDealListController
負責建立並維護一個CollectionView,並且向子類要資料來源totalDealsArr
//// BaseDealListController.h// 帥哥_團購//// Created by beyond on 14-8-21.// Copyright (c) 2014年 com.beyond. All rights reserved.// 位於中介層的父類,繼承自ShowtDealDetailController,自動擁有了點擊了XXX,只要供給資料來源,就動畫展示團購詳情的功能,並自動擁有了點擊遮蓋,動畫隱藏掉團購詳情控制器的功能....自己只負責建立維護一個CollectionView(九宮格),並且九宮格的資料來源(團購對象數組)由子類 自己提供#import "ShowDealDetailController.h"@interface BaseDealListController : ShowDealDetailController{ UICollectionView *_collectionView;}// 九宮格的資料來源(團購對象數組)由子類 自己提供- (NSArray *)totalDeals; // 所有的團購資料@end
//// BaseDealListController.m// 帥哥_團購//// Created by beyond on 14-8-21.// Copyright (c) 2014年 com.beyond. All rights reserved.// 位於中介層的父類,繼承自ShowtDealDetailController,自動擁有了點擊了XXX,只要供給資料來源,就動畫展示團購詳情的功能,並自動擁有了點擊遮蓋,動畫隱藏掉團購詳情控制器的功能....自己只負責建立維護一個CollectionView(九宮格),並且九宮格的資料來源(團購對象數組)由子類 自己提供#import "BaseDealListController.h"#import "Deal.h"#import "DealCell.h"// 每一個格子的寬和高#define kItemW 250#define kItemH 250@interface BaseDealListController ()<UICollectionViewDataSource, UICollectionViewDelegate>// 自己建立並維護一個九宮格,資料來源(團購對象數組)由子類提供@property (nonatomic, strong) UICollectionView *collectionView;@end@implementation BaseDealListController#pragma mark - 生命週期方法- (void)viewDidLoad{ [super viewDidLoad]; // 1.建立自己的collectionView [self addCollectionView]; // 2.註冊cell格子要用到的xib檔案 [self.collectionView registerNib:[UINib nibWithNibName:@"DealCell" bundle:nil] forCellWithReuseIdentifier:@"DealCell"]; // 3.設定collectionView永遠支援垂直滾動,為下拉重新整理準備(彈簧) self.collectionView.alwaysBounceVertical = YES; // 4.設定collectionView的背景色 self.collectionView.backgroundColor = kGlobalBg;}// 1.建立自己的collectionView- (void)addCollectionView{ // 建立一個流布局,必須指定 UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; // 設定流布局裡面的每一個格子寬和高,即每一個網格的尺寸 layout.itemSize = CGSizeMake(kItemW, kItemH); // 每一行之間的間距 layout.minimumLineSpacing = 20; // 指定的流布局建立一個collectionView,並且用成員變數記住 self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout]; // 高度和寬度自動調整 self.collectionView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; self.collectionView.delegate = self; self.collectionView.dataSource = self; [self.view addSubview:self.collectionView];}#pragma mark 在viewWillAppear和viewDidAppear中可以取得view最準確的寬高(width和height)// 重要~~~因為在控制器建立時,寬預設是768,高預設是1024,不管橫豎屏// 只有在viewWillAppear和viewDidAppear方法中,可以取得view最準確的(即實際的)寬和高(width和height)- (void)viewWillAppear:(BOOL)animated{ // 預設計算layout [self didRotateFromInterfaceOrientation:0];}#pragma mark - 父類方法// 攔截,螢幕即將旋轉的時候調用(控制器監控旋轉螢幕)- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{ //log(@"螢幕即將旋轉");}#pragma mark 旋轉螢幕完畢的時候調用// 攔截,旋轉螢幕完畢的時候調用- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{ // 1.取出建立CollectionViewController時傳入的的UICollectionViewFlowLayout UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout; // 2.計算間距 CGFloat v = 0; CGFloat h = 0; CGFloat height = self.view.frame.size.height -44; CGFloat width = self.view.frame.size.width; if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation) ) { // 橫屏的間距 v = (height - 2 * kItemH) / 3; h = (width - 3 * kItemW) / 4; } else { // 豎屏的間距 v = (height - 3 * kItemH) / 4; h = (width - 2 * kItemW) / 3; } // 3.動畫調整格子之間的距離 [UIView animateWithDuration:4.0 animations:^{ // 上 左 下 右 四個方向的margin layout.sectionInset = UIEdgeInsetsMake(h, h, v, h); // 每一行之間的間距 layout.minimumLineSpacing = h; }];}#pragma mark - collectionView代理方法// 共有多少個Item(就是格子Cube),詢問子類- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{ return self.totalDeals.count;}#pragma mark 重新整理資料的時候會調用(reloadData)#pragma mark 每當有一個cell重新進入螢幕視野範圍內就會調用// 產生每一個獨一無二的格子,詢問子類- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ static NSString *ID = @"DealCell"; DealCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath]; cell.deal = self.totalDeals[indexPath.row]; return cell;}// 點擊了一個格子時,showDealDetailVC- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{ // 再次調用父類的方法,展示dealDetail控制器 [self showDetail:self.totalDeals[indexPath.row]];}#pragma mark - 生命週期方法- (void)dealloc{ [[NSNotificationCenter defaultCenter] removeObserver:self];}@end
子類 DealListController
只需繼承即可,並向父類提供資料來源totalDealsArr
//// DealListController.h// 帥哥_團購//// Created by beyond on 14-8-14.// Copyright (c) 2014年 com.beyond. All rights reserved.// 點擊dock上面的【團購】按鈕對應的控制器,上面是導覽列,導覽列右邊是searchBar,導覽列左邊是一個大按鈕(TopMenu)(內部由三個小按鈕組成<TopMenuItem>)// 本控制器繼承自BaseDealListController,便自動擁有了九宮格,只需要為其提供資料即可,而BaseDealListController又繼承自ShowDealDetailVc,擁有展示閑情的功能#import "BaseDealListController.h"@interface DealListController : BaseDealListController@end
//// DealListController.m// 帥哥_團購//// Created by beyond on 14-8-14.// Copyright (c) 2014年 com.beyond. All rights reserved.// 點擊dock上面的【團購】按鈕對應的控制器,上面是導覽列,導覽列右邊是searchBar,導覽列左邊是一個大按鈕(TopMenu)(內部由三個小按鈕組成<TopMenuItem>)#import "DealListController.h"// 導覽列左邊是一個大按鈕(頂部菜單)#import "TopMenu.h"// 封裝的自訂cell#import "DealCell.h"// 點評提供的封裝發送請求的類#import "DPAPI.h"// 工具類#import "MetaDataTool.h"// 封裝請求的工具類#import "DealRequestTool.h"// 模型類#import "City.h"#import "Deal.h"// 二次封裝的圖片下載工具類#import "ImgDownloadTool.h"#define kItemW 250#define kItemH 250@interface DealListController()<DPRequestDelegate,MJRefreshBaseViewDelegate>{ // 用於接收伺服器返回的字典數組----轉化成的對象數組,供格子們顯示 NSMutableArray *_deals; // 下拉重新整理 MJRefreshHeaderView *_header; // 上拉載入下一頁 MJRefreshFooterView *_footer; // 每次載入的頁碼數 int _pageNo; }@end@implementation DealListController// 繼承BaseDealListController控制器,必須實現的方法,目的是為collectionView提供資料來源- (NSArray *)totalDeals{ return _deals;}- (void)viewDidLoad{ [super viewDidLoad]; _deals = [NSMutableArray array]; // 1.頂部導覽列的基本設定 [self setNavigationBar]; // 2.添加重新整理控制項 [self addRefresher]; // 0.監聽到城市改變的通知了,就下拉重新整理 kAddAllNotes(dataChange)}// 0.監聽到城市改變的通知了,就下拉重新整理- (void)dataChange{ [_header beginRefreshing];}// 1.頂部導覽列的基本設定- (void)setNavigationBar{ // 1.右邊的搜尋方塊 UISearchBar *s = [[UISearchBar alloc] init]; s.frame = CGRectMake(0, 0, 210, 35); s.placeholder = @"請輸入商品名、地址等"; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:s]; // 2.左邊的功能表列 TopMenu *top = [[TopMenu alloc] init]; // 重要,TopMenu裡面的item點擊後,建立的PopMenu將要添加到哪兒去???就是本控制器的view top.controllerView = self.view; self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:top]; }// 3.添加重新整理控制項- (void)addRefresher{ _header = [MJRefreshHeaderView header]; _header.scrollView = _collectionView; _header.delegate = self; _footer = [MJRefreshFooterView footer]; _footer.scrollView = _collectionView; _footer.delegate = self;}#pragma mark 重新整理控制項的代理方法- (void)refreshViewBeginRefreshing:(MJRefreshBaseView *)refreshView{ // 標記一下,是下拉 還是上拉 BOOL isHeader = [refreshView isKindOfClass:[MJRefreshHeaderView class]]; if (isHeader) { // 下拉重新整理 // 建議先,清除前面下載的圖片記憶體緩衝 [ImgDownloadTool clear]; // 每次下拉,都是載入第一頁 _pageNo = 1; } else { // 上拉載入更多,就是載入下一頁 _pageNo++; } // 載入第第_pageNo頁的資料 [[DealRequestTool sharedDealRequestTool] dealRequestWithPageNo:_pageNo success:^(NSArray *deals,int total_count) { if (isHeader) { // 如果是下拉載入第一頁資料,則先移除舊的 [_deals removeAllObjects]; } // 1.添加新的返回的對象數組 [_deals addObjectsFromArray:deals]; // 2.重新整理表格 [_collectionView reloadData]; // 3.恢複重新整理狀態 [refreshView endRefreshing]; // 4.根據總數量簡單判斷是否需要隱藏上拉控制項 _footer.hidden = _deals.count >= total_count; } fail:^(NSError *error) { log(@"請求失敗--%@",error); // 也要隱藏 [refreshView endRefreshing]; }];}@end
iOS_21團購_控制器繼承關係圖