废话不多说,直接入主题。
期货表,如图下:
这里主要讲期货表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]; }