上篇博客写了关于性能优化以及手工绘制自定义单元格内容,这篇我们利用TableView的Section的Header来实现类似QQ好友列表的效果。
TableView有一个代理方法
这个方法返回一个UIView对象,我们可以将一个Button对象设为这个Section的Header,在点击它的时候展开列表内容。
简单地看下假的好友列表数据
一个array中包含多个dictionary,字典中又包括组别的名字以及好友列表,好友也用一个字典来表示,分别有名称,是否在线以及头像图片名字。
加载数据
- (void)loadData { NSString *path = [[NSBundle mainBundle] pathForResource:@"friends" ofType:@"plist"]; _dataList = [NSArray arrayWithContentsOfFile:path]; _headers = [NSMutableDictionary dictionaryWithCapacity:_dataList.count]; _groupNames = [NSMutableArray arrayWithCapacity:_dataList.count]; for (NSInteger i = 0; i < _dataList.count; i++) { NSDictionary *dict = _dataList[i]; [_groupNames addObject:dict[@"groupname"]]; } }
之后我们开始写一个自定义的头部按钮来方便我们想要的布局
- (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { UIImage *image = [UIImage imageNamed:@"arrow-right"]; [self setImage:image forState:UIControlStateNormal]; self.imageView.contentMode = UIViewContentModeScaleAspectFit; //[self setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; _open = NO; } return self; } - (void)setOpen:(BOOL)open { _open = open; //设定点击旋转动画效果 [UIView beginAnimations:nil context:nil]; self.imageView.transform = CGAffineTransformMakeRotation(self.isOpen?M_PI_2:0); [UIView commitAnimations]; }
有一个BOOL型的成员_open来记录button是否被点击的状态,在点击时,左侧的图片有一个短暂的旋转动画效果。
然后设置图片以及标题的位置
//图像显示位置 - (CGRect)imageRectForContentRect:(CGRect)contentRect { return CGRectMake(RMarginX, RMarginY, RIconSide, RIconSide); } //标题显示位置 - (CGRect)titleRectForContentRect:(CGRect)contentRect { return CGRectMake(RIconSide + 4*RMarginX, 0, contentRect.size.width, contentRect.size.height); }
在drawRect中还绘制了底部的分割线以及光泽,不过由于背景没有绘制渐变,光泽效果有些突兀,若想具体了解绘制方法的使用,可以参见UIKit和Core Graphics绘图——绘制光泽,仿射变换与矩阵变换
绘制底边
CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSaveGState(context); CGContextSetStrokeColorWithColor(context, [UIColor darkGrayColor].CGColor); CGContextMoveToPoint(context, 0, rect.size.height); CGContextAddLineToPoint(context, 320, rect.size.height); CGContextSetLineWidth(context, 2.0f); CGContextStrokePath(context); CGContextRestoreGState(context);
然后是光泽效果
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); UIColor *light = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.1]; UIColor *dark = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.35]; NSArray *colors = @[(__bridge id)light.CGColor, (__bridge id)dark.CGColor]; CGFloat locations[] = {0.0, 1.0}; CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations); CGContextSaveGState(context); CGPoint start = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect)); CGPoint end = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); CGContextDrawLinearGradient(context, gradient, start, end, 0); CGColorSpaceRelease(colorSpace); CGGradientRelease(gradient); CGContextRestoreGState(context);
接着我们还需要自定义一个单元格类来接受和绘制数据,
具体实现类似于上篇博客中介绍的手工绘制单元格的内容
这里只看数据绑定的部分
- (void)bindFriend:(NSDictionary *)myFriend { _name = myFriend[@"name"]; _online = [myFriend[@"isonline"] boolValue]; _headerImage = [UIImage imageNamed:myFriend[@"imagename"]]; [self setNeedsDisplay]; }
在完成头部绘制以及单元格绘制的准备后,我们就可以在控制器中实现代理方法,讲数据排版到界面上了。
显示头部视图
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { HeaderButton *header = _headers[@(section)]; if (!header) { header = [HeaderButton buttonWithType:UIButtonTypeCustom]; header.bounds = CGRectMake(0, 0, 320, RHeaderHeight); header.backgroundColor = [UIColor colorWithRed:0.4 green:0.4 blue:0.8 alpha:1.0]; header.titleLabel.font = [UIFont systemFontOfSize:16.0f]; NSString *title = _groupNames[section]; [header setTitle:title forState:UIControlStateNormal]; [header addTarget:self action:@selector(expandFriends:) forControlEvents:UIControlEventTouchUpInside]; [_headers setObject:header forKey:@(section)]; } return header; }
头部点击的监听方法
- (void)expandFriends:(HeaderButton *)header { header.open = !header.isOpen; [self.tableView reloadData]; }
点击的时候改变header的_open值,然后刷新视图内容。
根据header的_open来确定是否显示section中的列表
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { HeaderButton *header = _headers[@(section)]; NSArray *array = [self arrayWithSection:section]; NSInteger count = header.isOpen?array.count:0; return count; }
绑定数据并且显示单元格
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { HRFriendsCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; NSArray *array = [self arrayWithSection:indexPath.section]; [cell bindFriend:array[indexPath.row]]; return cell; }
这样我们的工作几乎就完成了,下面来看一下效果图
具体的demo源码:点击打开链接
以上就是本篇博客全部内容,欢迎指正和交流。转载请注明出处~
文章目的
如何用纯代码的方式创建UITabBarController
方法
1. 首先开启XCode并建立一个Empty Application
2. 加入一个Objective-C Class并继承自UIViewController,取名为FirstViewController
3. 重复一次上面动作,并取名为SecondViewController
4. 之后可在工程中看到此图
5. 开启AppDelegate.h,并加入如下代码
#import <UIKit/UIKit.h> @interface AppDelegate : UIResponder <UIApplicationDelegate> { UITabBarController* tabBarViewController; } @property (strong, nonatomic) UIWindow *window; @end
6. 开启AppDelegate.m,并加入如下代码
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; // Override point for customization after application launch. self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; tabBarViewController = [[UITabBarController alloc]init]; [self.window setRootViewController:tabBarViewController]; FirstViewController* first = [[FirstViewController alloc]init]; SecondViewController* second = [[SecondViewController alloc]init]; tabBarViewController.viewControllers = [NSArray arrayWithObjects:first, second, nil]; [first release]; [second release]; }
tabBarViewController = [[UITabBarController alloc]init]; [self.window setRootViewController:tabBarViewController];第一行代码为初始一个UITabBarController
第二行为将tabBarViewController设定为window的root view controller(根视图控制器)
tabBarViewController.viewControllers = [NSArray arrayWithObjects:first, second, nil];接下来利用UITabBarController的viewControllers属性,设定First及Second两个控制器。
7.用代码修改TabBar与TabBarItem
UITabBar *tabBar = tabBarViewController.tabBar; UITabBarItem *tabBarItem1 = [tabBar.items objectAtIndex:0]; UITabBarItem *tabBarItem2 = [tabBar.items objectAtIndex:1];UITabBarController提供一个tabBar属性,我们可以透过这个属性取得UITabBar
并在UITabBar的items属性取得所有的UITabBarItem
tabBarItem1.title = @"Home"; tabBarItem2.title = @"Maps";透过UITabBarItem的title属性,可以设定tab bar item上显示的文字
[tabBarItem1 setFinishedSelectedImage:[UIImage imageNamed:@"home_selected.png"] withFinishedUnselectedImage:[UIImage imageNamed:@"home.png"]]; [tabBarItem2 setFinishedSelectedImage:[UIImage imageNamed:@"maps_selected.png"] withFinishedUnselectedImage:[UIImage imageNamed:@"maps.png"]];
- (void)setFinishedSelectedImage:(UIImage *)selectedImage withFinishedUnselectedImage:(UIImage *)unselectedImage这是UITabBarItem提供的方法,可以设定上面所显示的图片,selectedImage是只目前选择并显示的TabBatItem显示的图片
unselectedImage则是平常未选中时显示的图片
UIImage* tabBarBackground = [UIImage imageNamed:@"tabbar.png"]; [[UITabBar appearance] setBackgroundImage:tabBarBackground];这段代码可以修改UITabBar显示的背景图
[[UITabBar appearance] setSelectionIndicatorImage:[UIImage imageNamed:@"tabbar_selected.png"]];这可以设定选中的UITabBarItem后面的图
UIColor *titleHighlightedColor = [UIColor colorWithRed:153/255.0 green:192/255.0 blue:48/255.0 alpha:1.0]; [[UITabBarItem appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys: titleHighlightedColor, UITextAttributeTextColor, nil] forState:UIControlStateHighlighted];
- (void)setTitleTextAttributes:(NSDictionary *)attributes forState:(UIControlState)state这个方法可以设定显示文字的属性,在这段代码中,是设定为显示文字的颜色
范例下载
由于用到图片浏览的地方很多,写了个图片浏览的程序(点击下载源码),程序的源码是在 Android-Universal-Image-Loader(点击下载源码) 基础上修改的,支持网络图片的访问,添加了图片显示缓存的处理。
1. 修改了ViewPage显示,添加了类似Gallery的图片集滚动显示。不过由于Gallery类显示图片每次都要分配,速度很慢,已经被官方抛弃。这里使用了HorizontalScrollView代替。
同时,重载了ImageView添加缩放功能。
遇到一个问题:每次想让当前显示的图片在图片集中放大显示,如下图所示。但是放大的部分会被右边的图像覆盖,也就是说右边的图像比左边的图像后绘制。
添加调用bringToFront()函数后,当前图片是在图片集的最后显示,显然这个方法不是很好。这个问题现在还没有想到解决的方案。
显示效果:
缩放效果:
左右滑动效果:
2. 修改了GridView,使它支持水平滑动显示。这里借助于android-viewflow源码(点击下载源码)。
在支持水平滑动的时候,必须要指明每页显示的图片个数。
显示效果:
水平滑动的效果: