天天看點

iOS 開發使用UITableView實作抽屜打開關閉效果

今天給大家帶來一個使用tableView實作抽屜打開與關閉的效果,其實tableView這個控件在我們日常開發中使用的頻率很高,比如“遊戲需要顯示他的伺服器清單,新聞需要顯示它的新聞概略清單等”,但是它可不是單單簡單的給你提供一個顯示清單的功能,這樣太大才小用了,感興趣的朋友可以去查查UITableView的API函數你就會發現,哇~原來有這麼多功能強大的函數,真是相見恨晚啊。

利用UITableView這個控件,我們可以實作類似手機APP上QQ,微信等用戶端中聊天好友清單的效果,或者可以實作之前 IOS6 上界面幾個APP可以收放在一起,然後點一下像抽屜一樣打開的效果(我猜是這樣的,若有人知道真相,請務必跟我分享一下,哈哈!)是不是很炫,當然還可以實作其他好看的效果比如Cell的拖拽等。在以後的部落格中會跟大家分享一下這個效果。

好,言歸正傳。現在我們來實作今天這篇部落格題目所講的控件效果吧。首先先來看一下效果(沒有太多好看的資源,是以就簡單的用了純色背景)。

iOS 開發使用UITableView實作抽屜打開關閉效果
iOS 開發使用UITableView實作抽屜打開關閉效果
iOS 開發使用UITableView實作抽屜打開關閉效果
iOS 開發使用UITableView實作抽屜打開關閉效果

大緻的效果就是這樣的(手機沒有越獄,是以裝不了螢幕錄像的軟體,是以大家腦補這個點選的動态圖吧!:)),接下來 來看一下源碼是如何實作的.......

//
//  MainViewController.h
//  SlideUPAndDown
//
//  Created by silicon on 14-9-5.
//  Copyright (c) 2014年 silicon. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface MainViewController : UIViewController<UITableViewDataSource, UITableViewDelegate,UIAlertViewDelegate>

@property (strong, nonatomic) UITableView *myTableView;

@property (strong, nonatomic) NSMutableArray *setionArray;

@property (strong, nonatomic) NSMutableDictionary *sectionDic;

//是否有問題子視圖處于打開狀态
@property (assign) BOOL isOpen;

@property (strong, nonatomic) NSMutableArray *questionArray;

//目前選中的是哪一行
@property (strong, nonatomic) NSIndexPath *selectIndex;

//按鈕與indexPath之間的對應關系
@property (strong, nonatomic) NSMutableDictionary *dic;

//記錄按下按鈕的名稱
@property (strong, nonatomic) NSString *clickObj;

//按鈕是否處于按下狀态
@property (assign) BOOL isPressed;

//記錄目前點的按鈕
@property (strong, nonatomic) UIButton *preview_btn;

//關閉時要被删除的行
@property (strong, nonatomic) NSMutableArray *rowToDelete;


@end           
//
//  MainViewController.m
//  SlideUPAndDown
//
//  Created by silicon on 14-9-5.
//  Copyright (c) 2014年 silicon. All rights reserved.
//

#import "MainViewController.h"
#define _width [UIScreen mainScreen].bounds.size.width/2 - 20
#define isiPad (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)

@interface MainViewController ()

@end

@implementation MainViewController
@synthesize myTableView = _myTableView;
@synthesize setionArray = _setionArray;
@synthesize isOpen = _isOpen;
@synthesize questionArray = _questionArray;
@synthesize selectIndex = _selectIndex;
@synthesize sectionDic = _sectionDic;
@synthesize clickObj = _clickObj;
@synthesize isPressed = _isPressed;
@synthesize preview_btn = _preview_btn;
@synthesize rowToDelete = _rowToDelete;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    [self.view setBackgroundColor:[UIColor lightGrayColor]];
    
    //添加tableview
    self.myTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 20, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)];
    [self.view addSubview:_myTableView];
    
    _myTableView.delegate = self;
    _myTableView.dataSource = self;
    
    [_myTableView setBackgroundColor:[UIColor clearColor]];
    [_myTableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
    
    //初始化顯示的測試資料 問題的大類與問題的小類
    self.sectionDic = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@[@"運作問題", @"賬号問題", @"充值問題", @"論壇問題", @"公會問題"], @"問題一", @[@"運作問題1", @"賬号問題1", @"充值問題1", @"論壇問題1", @"公會問題1"], @"問題二", @[@"運作問題2", @"賬号問題2", @"充值問題2", @"論壇問題2", @"公會問題2"], @"問題三", @[@"運作問題4", @"賬号問題4", @"充值問題4", @"論壇問題4", @"公會問題4"], @"問題四", @[@"運作問題5", @"賬号問題5", @"充值問題5", @"論壇問題5", @"公會問題5"], @"問題5", @[@"運作問題6", @"賬号問題6", @"充值問題6", @"論壇問題6", @"公會問題6"],@"問題6", @[@"運作問題7", @"賬号問題7", @"充值問題7", @"論壇問題7", @"公會問題7"] ,@"問題7", nil];
    
    //問題大類
    self.setionArray = [[NSMutableArray alloc] initWithObjects:@"問題一", @"問題二", @"問題三", @"問題四", @"問題5", @"問題6", @"問題7", nil];

    self.dic = [[NSMutableDictionary alloc] init];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/*
 *@brief 顯示section的個數,由于每行顯示兩個,需要對奇數個的情況做判斷
 */
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    if([self.sectionDic count]%2 == 0){
        return [self.sectionDic count]/2;
    }else{
        return [self.sectionDic count]/2 + 1;
    }
}

/*
 *@brief 按鈕對應打開子類視圖時,每行也顯示兩個,對奇數情況也要做處理,由于行數的個數需要把section也算上,
         是以需要再 +1
 */
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    if(self.isOpen){
        if(self.selectIndex.section == section){
            NSInteger count = [[self.sectionDic objectForKey:self.clickObj] count];
            if(count%2 == 0){
                return count/2 +1;
            }else{
                return count/2 + 2;
            }
        }
    }
    
    return 1;
}

- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return 50;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{

}

/*
 *@brief 按鈕添加以及擺放位置處理
 */
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    static NSString *TableSampleIdentifier = @"MyIdentifier";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
                             TableSampleIdentifier];
    if (cell == nil) {
        //不重複使用Cell
        cell = [[UITableViewCell alloc] init];
        [cell setBackgroundColor:[UIColor clearColor]];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
    }
    //若果點選的是某一個section,則開始加載對應的子問題
    if(self.isOpen && self.selectIndex.section == indexPath.section && indexPath.row != 0){
        float marginTop = 10;
        //左邊按鈕
        int count = [_questionArray count];
        UIButton *l_btn = [UIButton buttonWithType:UIButtonTypeCustom];
        [l_btn setFrame:CGRectMake(10, marginTop, _width, 30)];
        UIFont *font = [UIFont systemFontOfSize:13.0f];
        [l_btn setTitle:[_questionArray objectAtIndex:indexPath.row*2-2] forState:UIControlStateNormal];
        [l_btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
        [l_btn setBackgroundColor:[UIColor lightGrayColor]];
        [l_btn setFont:font];
        [l_btn addTarget:self action:@selector(clickDetail:) forControlEvents:UIControlEventTouchUpInside];
        [cell addSubview:l_btn];
        //右邊按鈕
        if((indexPath.row*2-1) != count){
            UIButton *r_btn = [UIButton buttonWithType:UIButtonTypeCustom];
            [r_btn setFrame:CGRectMake(_width + 30, marginTop, _width, 30)];
            UIFont *font = [UIFont systemFontOfSize:13.0f];
            [r_btn setTitle:[_questionArray objectAtIndex:indexPath.row*2-1] forState:UIControlStateNormal];
            [r_btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
            [r_btn setBackgroundColor:[UIColor lightGrayColor]];
            [r_btn setFont:font];
            [r_btn addTarget:self action:@selector(clickDetail:) forControlEvents:UIControlEventTouchUpInside];
            [cell addSubview:r_btn];
        }
        
        [cell setBackgroundColor:[UIColor whiteColor]];
    }else{
        //加載問題大類的顯示
        if(isiPad){
            
        }else{
            //左邊按鈕
            UIButton *leftBtn = [UIButton buttonWithType:UIButtonTypeCustom];
            [leftBtn setFrame:CGRectMake(10, 5, 145, 40)];
            [leftBtn setTitle:[self.setionArray objectAtIndex:indexPath.section*2] forState:UIControlStateNormal];
            [leftBtn setBackgroundImage:[UIImage imageNamed:@"blue.png"] forState:UIControlStateNormal];
            [leftBtn setBackgroundImage:[UIImage imageNamed:@"darkblue.png"] forState:UIControlStateSelected];
            [leftBtn addTarget:self action:@selector(clickQuestion:) forControlEvents:UIControlEventTouchUpInside];
            [cell addSubview:leftBtn];
            [_dic setObject:indexPath forKey:leftBtn.titleLabel.text];
            
            //右邊按鈕
            if((indexPath.section *2 + 1) < [_setionArray count]){
                UIButton *rightBtn = [UIButton buttonWithType:UIButtonTypeCustom];
                [rightBtn setFrame:CGRectMake(145 + 20, 5, 145, 40)];
                [rightBtn setTitle:[self.setionArray objectAtIndex:indexPath.section*2 + 1] forState:UIControlStateNormal];
                [rightBtn setBackgroundImage:[UIImage imageNamed:@"blue.png"] forState:UIControlStateNormal];
                [rightBtn setBackgroundImage:[UIImage imageNamed:@"darkblue.png"] forState:UIControlStateSelected];
                [rightBtn addTarget:self action:@selector(clickQuestion:) forControlEvents:UIControlEventTouchUpInside];
                [cell addSubview:rightBtn];
                [_dic setObject:indexPath forKey:rightBtn.titleLabel.text];
            }
        }
    }
    
    return cell;
}

/*
 *@brief 問題大類按鈕響應函數
 */
- (void)clickQuestion:(id)sender{
    UIButton *btn = (UIButton*)sender;
    NSLog(@"%@", btn.titleLabel.text);
    BOOL differ = NO;
    
    //改變按鈕背景色
    [self changeBtnColor:btn];
    //判斷點選的是否是同一個按鈕
    if([btn.titleLabel.text isEqualToString:self.clickObj]){
        differ = NO;
    }else{
        differ = YES;
    }
    //擷取問題子類
    self.questionArray = [self.sectionDic objectForKey:btn.titleLabel.text];
    self.clickObj = btn.titleLabel.text;
    //問題子視圖顯示與關閉邏輯操作
    [self showViewState:differ];
}

- (void)changeBtnColor:(UIButton *)btn{
    if(self.isPressed){
        //若有按鈕處于按下狀态,則改變記錄按鈕的背景
        [self.preview_btn setBackgroundImage:[UIImage imageNamed:@"blue.png"] forState:UIControlStateNormal];
        //若點選的不是同一個按鈕則改變背景,否則不變
        if(![btn.titleLabel.text isEqualToString:self.preview_btn.titleLabel.text]){
            [btn setBackgroundImage:[UIImage imageNamed:@"darkblue.png"] forState:UIControlStateNormal];
            self.preview_btn = btn;
        }else{
            self.preview_btn = btn;
            self.isPressed = NO;
        }
    }else{
        [btn setBackgroundImage:[UIImage imageNamed:@"darkblue.png"] forState:UIControlStateNormal];
        self.preview_btn = btn;
        self.isPressed = YES;
    }
}

- (void)clickDetail:(id)sender{
    UIButton *btn = (UIButton *)sender;
    NSLog(@"%@", btn.titleLabel.text);

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:btn.titleLabel.text delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
    [alert show];
    
}


- (void)showViewState:(BOOL)isDiff{
    NSIndexPath *indexPath = [_dic objectForKey:self.clickObj];
    
    if(indexPath.row == 0){
        if(!self.selectIndex){
            self.selectIndex = indexPath;
            [self slideTheView:NO slideDown:YES];
        }else{
            //是否點選的是同一行
            if([self.selectIndex isEqual:indexPath]){
                self.selectIndex = indexPath;
                //處于打開狀态
                if(self.isOpen){
                    if(isDiff){
                        //若不是同一個按鈕,則先關閉之前的子問題視圖,然後再打開現有的子問題視圖
                        [self slideTheView:YES slideDown:NO];
                        [self slideTheView:NO slideDown:YES];
                    }else{
                        //若是同一個按鈕,則關閉它
                        [self slideTheView:YES slideDown:NO];
                    }
                }else{
                    [self slideTheView:NO slideDown:YES];
                }
            }else{
                if(self.isOpen){
                    [self slideTheView:YES slideDown:NO];
                    self.selectIndex = indexPath;
                    [self slideTheView:NO slideDown:YES];
                }else{
                    self.selectIndex = indexPath;
                    [self slideTheView:NO slideDown:YES];
                }
            }
        }
    }
}

- (void)slideTheView:(BOOL) _slideUp slideDown:(BOOL)_slideDown{
    //記錄目前是打開還是關閉狀态
    self.isOpen = _slideDown;
    
    [self.myTableView beginUpdates];
    //擷取目前的section
    int section = self.selectIndex.section;
    int count = [_questionArray count];
    //記錄需要從哪一個section下面去插入
    NSMutableArray *rowToInsert = [[NSMutableArray alloc] init];
    if(count == 1){
        NSIndexPath *indexPathToInsert = [NSIndexPath indexPathForRow:1 inSection:section];
        [rowToInsert addObject:indexPathToInsert];
    }else{
        if(count%2 != 0){
            count = count + 1;
        }
        
        for(int i = 1; i < count/2+1; i++){
            NSIndexPath *indexPathToInsert = [NSIndexPath indexPathForRow:i inSection:section];
            [rowToInsert addObject:indexPathToInsert];
        }
    }

    if(_slideUp){
        [self.myTableView deleteRowsAtIndexPaths:self.rowToDelete withRowAnimation:UITableViewRowAnimationTop];
    }else if(_slideDown){
        //将先插入行的位置記錄,在下一次點選時可将上次打開的視圖關閉
        self.rowToDelete = rowToInsert;
        [self.myTableView insertRowsAtIndexPaths:rowToInsert withRowAnimation:UITableViewRowAnimationTop];
    }
    
    [self.myTableView endUpdates];
}



@end
           

在代碼中寫這個效果的時候有幾個雷區跟大家分享一下,不然運作程式的時候手機會當機。

首先,該效果的需求是點選每一個問題大類的按鈕都會下拉顯示出來問題子類的視圖,是以在這裡我們定義的section個數就不是我們像平常顯示資料清單那樣直接就傳回一個數字 “1”就可以結束的,是以在傳回secton個數的函數中,我們要根據資料的個數動态的來傳回(我這裡是預設每行顯示兩個,當你要顯示3個或者4個的時候,就需要你自己去調整邏輯了)。

其次,由于每個問題大類的子類個數也是不确定的,是一個動态的狀态,是以在函數- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section中的邏輯處理也要格外的小心,傳回的時候别忘了要将section也看做一行,傳回+1。在上面我也是每行顯示兩個按鈕,當子類的問題個數為奇數個的時候還需要單獨加一行顯示它,是以我在我的代碼裡面是如果是奇數個的話 我就再加上1.

第三,在按鈕的背景切換上也要注意,看點選的是否是統一個按鈕,不然光設定一個變量來記錄按鈕是按下的還是沒有按下的會出現按鈕顔色切換了,但是子類問題沒有彈出來。還有,再同一行中點選問題大類,如果該問題子類已經顯示則需要将他收起來,原則就是一個互斥的效果。

好了,注意事項也說了,源碼也發了,今天的就到這把,有什麼問題歡迎大家指出來,謝謝!。。。。 :)