废话不多说,直接入主题。
期货表,如图下:
这里主要讲期货表UI地实现。
期货表是一个能横向+竖向滚动地表格,实时更新最新期货数据并且高亮显示。期货表地实现方法有很多,主要看组件地选择。我这里讲一个稍微简单点地方法。
这是期货表地IB图,很简单,就是一个UITableView:
我地方法是在每个UITableViewCell中创建一个UILable和UIScrollView,具体样子如图:
第一列为固定不动地UILabel,右边为滚动显示地UIScrollView,竖向滚动由UITableView自带实现,横向滚动做Cell同步处理,同步处理的Cell为当前显示地Cell(这点是必须地,表可能比较大,同步范围太大会有延迟)。
由于标题部分和Cell部分是相同地,所以代码不能放在Cell中,抽离公共部分单独为一个View,代码如下:
#import <UIKit/UIKit.h>
#import "TouchScrollView.h"
#define FuturesStyleTitle 0
#define FuturesStyleContent 1
@interface FuturesView : UIView <UIScrollViewDelegate>{
UILabel *_titleLab; // 第一列标题
TouchScrollView *_titleScroll; // 滚动数据
UILabel *_arrowLeft; // 标识方向地左箭头
UILabel *_arrowRight; // 右箭头
int _style; // 类别:标题还是数据
}
@property(nonatomic, readonly) UILabel *titleLab;
@property(nonatomic, readonly) TouchScrollView *titleScroll;
@property(nonatomic, readonly) UILabel *arrowLeft;
@property(nonatomic, readonly) UILabel *arrowRight;
@property(nonatomic, readonly) int style;
- (id)initWithFrame:(CGRect)frame byStyle:(int)style;
// 赋值
- (void) setData:(id)data allow:(BOOL)isAllow;
// 数据更新
- (void) update:(id)value;
// 高亮单元恢复显示
- (void) normal;
@end
.M文件
#import "FuturesView.h"
#import "FuturesEntity.h"
@implementation FuturesView
@synthesize titleLab = _titleLab;
@synthesize titleScroll = _titleScroll;
@synthesize arrowLeft = _arrowLeft;
@synthesize arrowRight = _arrowRight;
static NSMutableArray *pool = nil; // 共享池,内部存储当前显示地CellView,因为UITableView地Cell本来就支持重用
- (id) init {
return [self initWithFrame:CGRectMake(0, 0, 320, 28)];
}
- (id)initWithFrame:(CGRect)frame
{
self = [self initWithFrame:frame byStyle:FuturesStyleContent];
return self;
}
- (id)initWithFrame:(CGRect)frame byStyle:(int)style {
self = [super initWithFrame:frame];
_style = style;
float width = 75;
float height = style == FuturesStyleTitle ? 30 : 28;
_titleLab = [self createLabel:0 width:(width+5) height:height];
_titleLab.frame = CGRectMake(3, 0, width+5, height);
_titleLab.textAlignment = UITextAlignmentLeft;
if(self.style == FuturesStyleTitle) {
[_titleLab setTextColor:[UIColor colorWithRed:0.35 green:0.76 blue:0.71 alpha:1]];
} else {
[_titleLab setTextColor:[UIColor yellowColor]];
}
[self addSubview:_titleLab];
[_titleLab release];
// 自定义ScrollView,主要处理事件穿透
_titleScroll = [[TouchScrollView alloc] initWithFrame:CGRectMake((width+5), 0, frame.size.width - (width+5), height)];
[_titleScroll setShowsVerticalScrollIndicator:NO];
[_titleScroll setShowsHorizontalScrollIndicator:NO];
[_titleScroll setBounces:NO];
[_titleScroll setBackgroundColor:[UIColor clearColor]];
[self addSubview:_titleScroll];
[_titleScroll release];
[_titleScroll setContentSize:CGSizeMake(10 * width + 5, height)];
UILabel *temp = nil;
for(int i=0; i<10; i++) {
temp = [self createLabel:i width:width height:height];
if(style == FuturesStyleTitle) {
if(i <= 2) {
temp.textColor = [UIColor whiteColor];
} else {
temp.textColor = [UIColor colorWithRed:0.35 green:0.76 blue:0.71 alpha:1];
}
} else if(style == FuturesStyleContent && (i == 2 || i == 6)) {
temp.textColor = [UIColor yellowColor];
}
[_titleScroll addSubview:temp];
[temp release];
}
if(pool == nil) {
pool = [[NSMutableArray alloc] initWithCapacity:15];
}
// 加入共享池
_titleScroll.delegate = self;
[pool addObject:_titleScroll];
if(style == FuturesStyleTitle) {
_arrowLeft = [[UILabel alloc] initWithFrame:CGRectMake(70, 0, 10, 10)];
_arrowLeft.font = [UIFont boldSystemFontOfSize:10];
_arrowLeft.textColor = [UIColor colorWithRed:0.35 green:0.76 blue:0.71 alpha:1];
_arrowLeft.backgroundColor = [UIColor clearColor];
_arrowLeft.text = @"《";
_arrowLeft.hidden = YES;
[self addSubview:_arrowLeft];
[_arrowLeft release];
_arrowRight = [[UILabel alloc] initWithFrame:CGRectMake(self.frame.size.width - 10, 0, 10, 10)];
_arrowRight.font = [UIFont boldSystemFontOfSize:10];
_arrowRight.textColor = [UIColor colorWithRed:0.35 green:0.76 blue:0.71 alpha:1];
_arrowRight.backgroundColor = [UIColor clearColor];
_arrowRight.text = @"》";
[self addSubview:_arrowRight];
[_arrowRight release];
}
return self;
}
- (void) dealloc {
[pool release];
pool = nil;
[super dealloc];
}
-(void)scrollViewDidScroll:(UIScrollView*)scrollView {
for(UIScrollView *scroll in pool) {
[scroll setContentOffset:scrollView.contentOffset];
}
// [info setContentOffset:CGPointMake(titl.contentOffset.x, info.contentOffset.y)];
[self.arrowLeft setHidden:(scrollView.contentOffset.x <= 0.0f)];
[self.arrowRight setHidden:(scrollView.contentOffset.x >= scrollView.contentSize.width - self.frame.size.width + scrollView.frame.origin.x)];
}
- (UILabel *) createLabel:(int)cellIndex width:(float)width height:(float)height {
UILabel *lab = [[UILabel alloc] initWithFrame:CGRectMake(cellIndex * width, 0, width, height)];
[lab setTextAlignment:UITextAlignmentRight];
[lab setBackgroundColor:[UIColor clearColor]];
[lab setTextColor:[UIColor whiteColor]];
[lab setFont:[UIFont boldSystemFontOfSize:15]];
return lab;
}
- (void) setData:(id)data allow:(BOOL)isAllow {
if(self.style == FuturesStyleTitle) {
NSArray *arr = (NSArray *)data;
self.titleLab.text = [arr objectAtIndex:0];
UILabel *temp = nil;
for(int i=0; i<arr.count - 1; i++) {
temp = [self.titleScroll.subviews objectAtIndex:i];
temp.text = [arr objectAtIndex:(i+1)];
}
} else {
FuturesEntity *entity = (FuturesEntity *)data;
self.titleLab.text = entity.name;
if(!isAllow) {
NSArray *subviews = self.titleScroll.subviews;
for(UILabel *lab in subviews) {
lab.text = @"*****";
}
return;
}
NSArray *subviews = self.titleScroll.subviews;
[self updateValue:[subviews objectAtIndex:0] value:entity.news isColor:FALSE];
[self updateValue:[subviews objectAtIndex:1] value:entity.change isColor:FALSE];
[self updateValue:[subviews objectAtIndex:2] value:entity.volume isColor:FALSE];
[self updateValue:[subviews objectAtIndex:3] value:[NSString stringWithFormat:@"%@%%",entity.margin] isColor:FALSE];
[self updateValue:[subviews objectAtIndex:4] value:entity.last_av isColor:FALSE];
[self updateValue:[subviews objectAtIndex:5] value:entity.close isColor:FALSE];
[self updateValue:[subviews objectAtIndex:6] value:entity.hold isColor:FALSE];
[self updateValue:[subviews objectAtIndex:7] value:entity.open isColor:FALSE];
[self updateValue:[subviews objectAtIndex:8] value:entity.high isColor:FALSE];
[self updateValue:[subviews objectAtIndex:9] value:entity.low isColor:FALSE];
UIColor *red = [UIColor redColor];
UIColor *green = [UIColor greenColor];
UIColor *color = entity.change.floatValue > 0 ? red : green;
[[subviews objectAtIndex:0] setTextColor:color];
[[subviews objectAtIndex:1] setTextColor:color];
[[subviews objectAtIndex:3] setTextColor:color];
float last = [entity.last_av floatValue];
[[subviews objectAtIndex:7] setTextColor:(entity.open.floatValue > last ? red : green)];
[[subviews objectAtIndex:8] setTextColor:(entity.high.floatValue > last ? red : green)];
[[subviews objectAtIndex:9] setTextColor:(entity.low.floatValue > last ? red : green)];
}
}
- (void) update:(id)value {
FuturesEntity *entity = value;
NSArray *subviews = self.titleScroll.subviews;
[self updateValue:[subviews objectAtIndex:0] value:entity.news isColor:TRUE];
[self updateValue:[subviews objectAtIndex:1] value:entity.change isColor:TRUE];
[self updateValue:[subviews objectAtIndex:2] value:entity.volume isColor:TRUE];
[self updateValue:[subviews objectAtIndex:3] value:[NSString stringWithFormat:@"%@%%",entity.margin] isColor:TRUE];
[self updateValue:[subviews objectAtIndex:4] value:entity.last_av isColor:TRUE];
[self updateValue:[subviews objectAtIndex:5] value:entity.close isColor:TRUE];
[self updateValue:[subviews objectAtIndex:6] value:entity.hold isColor:TRUE];
[self updateValue:[subviews objectAtIndex:7] value:entity.open isColor:TRUE];
[self updateValue:[subviews objectAtIndex:8] value:entity.high isColor:TRUE];
[self updateValue:[subviews objectAtIndex:9] value:entity.low isColor:TRUE];
}
- (void) updateValue:(UILabel *)lab value:(NSString *)value isColor:(BOOL)isColor {
if(isColor && ![lab.text isEqualToString:value]) {
[lab setBackgroundColor:[UIColor blueColor]];
} else {
[lab setBackgroundColor:[UIColor clearColor]];
}
lab.text = value;
}
- (void) normal {
UIColor *color =[UIColor clearColor];
[self.titleLab setBackgroundColor:color];
NSArray *subviews = self.titleScroll.subviews;
for(int i=0; i<10; i++) {
UILabel *temp = [subviews objectAtIndex:i];
[temp setBackgroundColor:color];
}
}
// 事件穿透处理,向上层传递
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.nextResponder touchesBegan:touches withEvent:event];
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self.nextResponder touchesEnded:touches withEvent:event];
}
注意部分我加了注释,因为在UITableView中添加UIScrollView会出现事件拦截问题,即UIScrollView和UITableView只有一个能响应事件,要么
只能横向滚动要么只能竖向滚动,为了解决这个问题,自定义了UIScrollView,重载touch系列方法,实现事件穿透。
代码里有个FuturesEntity类,这是一个实例类,存储期货数据字段,代码比较长且没意义,就没贴上来了,可以根据上面调用看得出内容。
自定义地UISCrollView很简单:
#import "TouchScrollView.h"
@implementation TouchScrollView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
return self;
}
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.nextResponder touchesBegan:touches withEvent:event];
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self.nextResponder touchesEnded:touches withEvent:event];
}
UISCrollView的touch传递给上级,也就是包含UIScrollView地FuturesView,FuturesView再传递给上层,即UITableView,最终实现UIScrollView和UITableView都响应事件地目的。
最终UITableView协议方法代码如下:
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"futuresCell"];
FuturesView *cellView = nil;
if(cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"futuresCell"] autorelease];
cellView = [[FuturesView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 28) byStyle:FuturesStyleContent];
[cell addSubview:cellView];
[cellView release];
} else {
cellView = (FuturesView *)[cell.subviews objectAtIndex:(cell.subviews.count-1)];
}
[cellView setData:[self.mainData objectAtIndex:indexPath.row] allow:self.timer.isAllow];
return cell;
}
整体运行起来就是前面第一幅图地样子,代码是我项目中复制过来地,如果有人需要,请改改。另外如果有问题需要交流请给我留言,不谢。
补充回复要求Timer:
FuturesTimer.h
#import <Foundation/Foundation.h>
#import "HttpUtil.h"
#import "FuturesView.h"
@interface FuturesTimer : NSObject <HttpProtocol> {
BOOL _isWorking;
id _delegate;
int _filter;
NSString *_searchValue;
NSTimer *_timer;
}
@property (nonatomic, readonly) BOOL isWorking;
@property (nonatomic, assign) id delegate;
@property (nonatomic) int filter;
@property (nonatomic, retain) NSString *searchValue;
@property (nonatomic, retain) NSTimer *timer;
- (void) destory;
- (void) work;
- (void) stop;
- (void) empty;
- (FuturesView *) futuresViewInCell:(UITableViewCell *)cell;
@end
#import "FuturesTimer.h"
#import "Header.h"
#import "Config.h"
#import "UserEntity.h"
#import "FuturesViewController.h"
#import "FuturesEntity.h"
#import "UIUtil.h"
#import "AppDelegate.h"
@implementation FuturesTimer
@synthesize isWorking = _isWorking;
@synthesize delegate = _delegate;
@synthesize filter = _filter;
@synthesize searchValue = _searchValue;
@synthesize timer = _timer;
- (id) init {
self = [super init];
[self empty];
return self;
}
- (void) empty {
self.searchValue = nil;
self.filter = -1;
}
- (void) work {
_isWorking = YES;
// retainCount=1为self.timer持有retain,本身对象已经释放
if(self.timer == nil) {
[self loadData];
} else if([self.timer retainCount] > 1) {
[self.timer fire];
}
}
- (void) stop {
_isWorking = FALSE;
}
- (void) loadData {
NSString *url = nil;
if(self.searchValue != nil) {
// 期货表搜索
url = [NSString stringWithFormat:@"xxx?key=%@", self.searchValue];
} else if (self.filter == -1) {
// 个人订阅
UserEntity *user = (UserEntity *)[[Config Instance] getCache:@"user"];
url = [NSString stringWithFormat:@"xxx?corpID=%d", user.uid];
} else {
// 期货表筛选(品种)
url = [NSString stringWithFormat:@"xxx?id=%d", self.filter];
}
[HttpUtil HttpGet:self aciton:url keyBack:0];
}
- (float) getTime {
UserEntity *user = (UserEntity *)[[Config Instance] getCache:@"user"];
int time = [[[Config Instance] getCache:[NSString stringWithFormat:@"%@%d", futures_timer, user.uid]] intValue];
if(time == 0) {
time = 5;
}
return time;
}
- (void) start {
self.timer = [NSTimer scheduledTimerWithTimeInterval:[self getTime] target:self selector:@selector(loop) userInfo:nil repeats:NO];
}
- (void) loop {
FuturesViewController *viewController = (FuturesViewController *)self.delegate;
if(self.isWorking && viewController.isViewLoaded && viewController.view.window) {
// 是否处于显示状态,显示状态则刷新数据
[self loadData];
} else {
[self start];
}
}
- (void) httpCallBack:(id)data keyBack:(int)keyBack {
// 成功处理
[self data:data];
if(self.searchValue == nil && self.filter == -1) {
[NSThread detachNewThreadSelector:@selector(native:) toTarget:self withObject:data];
}
[self start];
}
- (void) httpError:(NSError *)error keyBack:(int)keyBack {
NSString *msg = [error.userInfo objectForKey:@"message"];
if([msg isEqualToString:@"cancel"]) {
// NSLog(@"%d 被取消", keyBack);
} else {
// 失败处理,提示消息
[UIUtil msgBox:msg];
}
[self start];
}
- (void) native:(NSArray *)data {
// 处理数据
}
- (void) normal {
FuturesViewController *futures = (FuturesViewController *)self.delegate;
for(UITableViewCell *cell in futures.contentTableView.visibleCells) {
[[self futuresViewInCell:cell] normal];
}
}
- (FuturesView *) futuresViewInCell:(UITableViewCell *)cell {
NSArray *subviews = cell.contentView.subviews;
for(UIView *view in subviews) {
if([view isKindOfClass:[FuturesView class]]) {
return (FuturesView *)view;
}
}
return nil;
}
- (void) destory {
_isWorking = FALSE;
self.searchValue = nil;
self.delegate = nil;
if([self.timer retainCount] > 1) {
[self.timer invalidate];
}
self.timer = nil;
}
- (void) dealloc {
[self destory];
[super dealloc];
}
@end
- (void)viewDidLoad
{
[super viewDidLoad];
/* 期货表TITLE */
FuturesView *titleView = [[FuturesView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 38) byStyle:FuturesStyleTitle];
[titleView setData:[NSArray arrayWithObjects:@"品种名", @"最新", @"涨跌", @"成交量", @"涨跌幅", @"昨结算", @"昨收", @"持仓量", @"开盘", @"最高", @"最低", nil]];
[self.view addSubview:titleView];
[titleView release];
/* 计时器加载数据 */
_timer = [[FuturesTimer alloc] init];
self.timer.delegate = self;
}
- (void) viewDidAppear:(BOOL)animated {
[self.timer work];
}
- (void) destory {
if(self.isViewLoaded && self.view.window) {
return;
}
[self destory2];
}
- (void) clearTabCell {
if(self.contentTableView.visibleCells.count > 0) {
[[self.timer futuresViewInCell:[self.contentTableView.visibleCells objectAtIndex:0]] destory];
}
}
- (void) destory2 {
[self.timer destory];
[self clearTabCell];
self.timer = nil;
self.contentTableView = nil;
self.mainData = nil;
self.load = nil;
self.view = nil;
}
- (void) viewDidUnload {
[self destory];
[super viewDidUnload];
}
- (void) didReceiveMemoryWarning {
// NSLog(@"futures didReceiveMemoryWarning called");
[self destory];
[super didReceiveMemoryWarning];
}
- (void) dealloc {
[self destory2];
[super dealloc];
}