iOS_21團購_控制器繼承關系圖 – iPhone手機開發技術文章 iPhone軟體開發教學課程

最終效果圖:

vcD4KPHA+PGJyPgo8L3A+CjxwPr/Y1sbG97zMs9C52M+1zbw6PGJyPgo8L3A+CjxwPsu1w/c6PC9wPgo8cD6147v31ve/2NbGxvfX87LgtcREb2Nryc+1xLC0xaUsPC9wPgo8cD6xyMjnob7Nxbm6ob+hoqG+ytWy2KG/oaKhvrXYzbyhv8qxo6w8L3A+CjxwPsq1z9a1xLmmxNzT0NDttuDP4M2s1q60pqGjPC9wPgo8cD48YnI+CjwvcD4KPHA+vt/M5cu1w/fI58/Co7o8L3A+CjxwPrXju/ehvs3Fubqhv6Os0tS+xbmsJiMyNjY4NDu1xNDOyr3P1Mq+0ru49rj2zcW5uqOsPC9wPgo8cD6yosfSo6y147v30ru49kNlbGzKsSzVucq+uMNDZWxsttTTprXEzcW5us/qx+k8L3A+CjxwPjxicj4KPC9wPgo8cD48c3Ryb25nPrXju/ehvsrVstihv6Os0tS+xbmsJiMyNjY4NDu1xNDOyr3P1Mq+0ru49rj20tG+rbnptbW1xM3FubrEo9DNo6w8L3N0cm9uZz48L3A+CjxwPjxzdHJvbmc+sqLH0qOsteO799K7uPZDZWxsyrEs1bnKvrjDQ2VsbLbU06a1xM3FubrP6sfpPC9zdHJvbmc+PC9wPgo8cD48c3Ryb25nPjxicj4KPC9zdHJvbmc+PC9wPgo8cD48c3Ryb25nPrXju/ehvrXYzbyhv6Os0tRNYXBWaWV3yc/Su7j2uPa088231eu1xNDOyr3P1Mq+zcW5usSj0M0ozai5/bPHytDD+yYjNDM7vq22yCYjNDM7zrO2yCYjNDM7sOu+ttf3ss7K/beiy83H68fzKTwvc3Ryb25nPjwvcD4KPHA+PHN0cm9uZz6yosfSLLXju/fSu7j2tPPNt9XryrEs1bnKvrjDQ2VsbLbU06a1xM3FubrP6sfpPC9zdHJvbmc+PC9wPgo8cD48YnI+CjwvcD4KPHA+PGltZyBzcmM9″https://www.aiwalls.com/uploadfile/Collfiles/20140826/2014082608532724.png” alt=”\”>

上述各控制器的主要功能和方法聲明如下:

抽取的父類如下:

父類ShowDealDetailController

負責創建並展示和隱藏封裝的【團購詳情控制器】

私有成員:Cover

//
//  ShowDealDetailController.h
//  帥哥_團購
//
//  Created by beyond on 14-8-20.
//  Copyright (c) 2014年 com.beyond. All rights reserved.
//  基類,父類,展示 訂單詳情控制器的控制器,當點擊XXX時,需要展示團購詳情的時候,就繼承這個控制器即可,內部實現瞭創建並顯示訂單訂單詳情控制器,以及隱藏訂單詳情控制器

#import 
@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 ()
// 自己創建並維護一個九宮格,數據源(團購對象數組)由子類提供
@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)(內部由三個小按鈕組成)
// 本控制器繼承自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)(內部由三個小按鈕組成)

#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()
{
    // 用於接收服務器返回的字典數組----轉化成的對象數組,供格子們顯示
    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

發佈留言