IOS 开发使用UITableView实现抽屉打开关闭效果

来源:互联网 发布:网络销售app排行榜 编辑:程序博客网 时间:2024/06/03 18:46

今天给大家带来一个使用tableView实现抽屉打开与关闭的效果,其实tableView这个控件在我们日常开发中使用的频率很高,比如“游戏需要显示他的服务器列表,新闻需要显示它的新闻概略列表等”,但是它可不是单单简单的给你提供一个显示列表的功能,这样太大才小用了,感兴趣的朋友可以去查查UITableView的API函数你就会发现,哇~原来有这么多功能强大的函数,真是相见恨晚啊。

利用UITableView这个控件,我们可以实现类似手机APP上QQ,微信等客户端中聊天好友列表的效果,或者可以实现之前 IOS6 上界面几个APP可以收放在一起,然后点一下像抽屉一样打开的效果(我猜是这样的,若有人知道真相,请务必跟我分享一下,哈哈!)是不是很炫,当然还可以实现其他好看的效果比如Cell的拖拽等。在以后的博客中会跟大家分享一下这个效果。
好,言归正传。现在我们来实现今天这篇博客题目所讲的控件效果吧。首先先来看一下效果(没有太多好看的资源,所以就简单的用了纯色背景)。

大致效果就是这样的(手机没有越狱,所以装不了屏幕录像的软件,所以大家脑补这个点击的动态图吧!:)),接下来 来看一下源码是如何实现的.......


////  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.

第三,在按钮的背景切换上也要注意,看点击的是否是统一个按钮,不然光设置一个变量来记录按钮是按下的还是没有按下的会出现按钮颜色切换了,但是子类问题没有弹出来。还有,再同一行中点击问题大类,如果该问题子类已经显示则需要将他收起来,原则就是一个互斥的效果。

好了,注意事项也说了,源码也发了,今天的就到这把,有什么问题欢迎大家指出来,谢谢!。。。。 :












1 0