-
0App/HC/AppDelegate.h
-
0App/HC/AppDelegate.m
-
0App/HC/Assets.xcassets/AccentColor.colorset/Contents.json
-
BINApp/HC/Assets.xcassets/AppIcon.appiconset/10241024.jpg
-
1App/HC/Assets.xcassets/AppIcon.appiconset/Contents.json
-
0App/HC/Assets.xcassets/Contents.json
-
0App/HC/Assets.xcassets/banner1.imageset/Contents.json
-
0App/HC/Assets.xcassets/banner1.imageset/banner1.jpg
-
0App/HC/Assets.xcassets/banner2.imageset/Contents.json
-
0App/HC/Assets.xcassets/banner2.imageset/banner2.jpg
-
0App/HC/Assets.xcassets/email.imageset/Contents.json
-
0App/HC/Assets.xcassets/email.imageset/email.jpg
-
0App/HC/Assets.xcassets/hqz.imageset/Contents.json
-
0App/HC/Assets.xcassets/hqz.imageset/hqz.jpg
-
0App/HC/Assets.xcassets/image.imageset/Contents.json
-
0App/HC/Assets.xcassets/logo.imageset/20251117-225344.jpg
-
0App/HC/Assets.xcassets/logo.imageset/Contents.json
-
0App/HC/Assets.xcassets/logo.jpg
-
0App/HC/Assets.xcassets/singapore_flag.imageset/Contents.json
-
0App/HC/Assets.xcassets/singapore_flag.imageset/singapore_flag.jpg
-
0App/HC/Base.lproj/LaunchScreen.storyboard
-
0App/HC/Base.lproj/Main.storyboard
-
0App/HC/CNViewController.h
-
0App/HC/CNViewController.m
-
0App/HC/ChartViewController.h
-
977App/HC/ChartViewController.m
-
0App/HC/CommonTabBar.h
-
0App/HC/CommonTabBar.m
-
0App/HC/CountryModel.h
-
0App/HC/CountryModel.m
-
0App/HC/HC.xcdatamodeld/.xccurrentversion
-
0App/HC/HC.xcdatamodeld/HC.xcdatamodel/contents
-
0App/HC/HomeViewController.h
-
0App/HC/HomeViewController.m
-
0App/HC/Info.plist
-
0App/HC/MLXYViewController.h
-
585App/HC/MLXYViewController.m
-
0App/HC/MyViewController.h
-
0App/HC/MyViewController.m
-
0App/HC/QuotationViewController.h
-
11App/HC/QuotationViewController.m
-
0App/HC/SceneDelegate.h
-
14App/HC/SceneDelegate.m
-
29App/HC/StockInfoCardView.h
-
156App/HC/StockInfoCardView.m
-
18App/HC/StockKLineModel.h
-
0App/HC/StockKLineModel.m
-
0App/HC/ViewController.h
-
0App/HC/ViewController.m
-
0App/HC/main.m
-
28HC.xcodeproj/project.pbxproj
-
5HC.xcodeproj/project.xcworkspace/xcuserdata/huilinli.xcuserdatad/IDEFindNavigatorScopes.plist
-
592HC/ChartViewController.m
-
442HC/MLXYViewController.m
|
After Width: 1024 | Height: 1024 | Size: 218 KiB |
@ -1,6 +1,7 @@ |
|||
{ |
|||
"images" : [ |
|||
{ |
|||
"filename" : "10241024.jpg", |
|||
"idiom" : "universal", |
|||
"platform" : "ios", |
|||
"size" : "1024x1024" |
|||
|
Before Width: 1161 | Height: 378 | Size: 21 KiB After Width: 1161 | Height: 378 | Size: 21 KiB |
|
Before Width: 1161 | Height: 378 | Size: 21 KiB After Width: 1161 | Height: 378 | Size: 21 KiB |
|
Before Width: 75 | Height: 55 | Size: 1.4 KiB After Width: 75 | Height: 55 | Size: 1.4 KiB |
|
Before Width: 602 | Height: 582 | Size: 258 KiB After Width: 602 | Height: 582 | Size: 258 KiB |
|
Before Width: 143 | Height: 142 | Size: 3.1 KiB After Width: 143 | Height: 142 | Size: 3.1 KiB |
|
Before Width: 143 | Height: 142 | Size: 3.1 KiB After Width: 143 | Height: 142 | Size: 3.1 KiB |
|
Before Width: 146 | Height: 87 | Size: 2.1 KiB After Width: 146 | Height: 87 | Size: 2.1 KiB |
@ -0,0 +1,977 @@ |
|||
// |
|||
// ChartViewController.m |
|||
// HC |
|||
// |
|||
// Created by huilinLi on 2025/11/27. |
|||
// |
|||
|
|||
#import "ChartViewController.h" |
|||
#import "StockKLineModel.h" |
|||
#import "StockInfoCardView.h" |
|||
|
|||
static CGFloat kLineUnitWidth = 5.0; // 单位宽度 |
|||
static CGFloat kPriceLabelAreaWidth = 35.0; |
|||
static CGFloat kPriceLabelPadding = 10.0; |
|||
static CGFloat kKLineHeight = 300.0; |
|||
static CGFloat kContainerHeight = 120.0; |
|||
|
|||
@interface ChartViewController () <UIGestureRecognizerDelegate, UIScrollViewDelegate> |
|||
|
|||
@property (nonatomic, strong) StockInfoCardView *cardContainer; |
|||
@property (nonatomic, strong) UIView *kSelectContainer; |
|||
@property (nonatomic, strong) UIView *kLineContainer; |
|||
@property (nonatomic, strong) UIView *macdContainer; |
|||
@property (nonatomic, strong) UIView *kdjContainer; |
|||
@property (nonatomic, strong) UIView *otherContainer; |
|||
@property (nonatomic, strong) UIScrollView *kLineScrollView; |
|||
@property (nonatomic, strong) NSArray *kLineData; |
|||
@property (nonatomic, assign) NSInteger visibleKLineCount; |
|||
@property (nonatomic, strong) NSArray<UILabel *> *priceLabels; |
|||
@property (nonatomic, strong) UILabel *startDateLabel; |
|||
@property (nonatomic, strong) UILabel *endDateLabel; |
|||
@property (nonatomic, strong) UILabel *maLegendLabel; // MA5 |
|||
@property (nonatomic, strong) UILabel *macdLegendLabel; // DIF DEA |
|||
@property (nonatomic, strong) UILabel *kdjLegendLabel; // K D |
|||
@property (nonatomic, strong) UILabel *highPriceMarkLabel; |
|||
@property (nonatomic, strong) UILabel *lowPriceMarkLabel; |
|||
@property (nonatomic, assign) CGFloat currentMaxPrice; |
|||
@property (nonatomic, assign) CGFloat currentMinPrice; |
|||
@property (nonatomic, assign) CGFloat macdMaxValue; |
|||
@property (nonatomic, assign) CGFloat macdMinValue; |
|||
@property (nonatomic, assign) CGFloat kdjMaxValue; |
|||
@property (nonatomic, assign) CGFloat kdjMinValue; |
|||
|
|||
// K线图层 |
|||
@property (nonatomic, strong) CAShapeLayer *redCandleLayer; |
|||
@property (nonatomic, strong) CAShapeLayer *greenCandleLayer; |
|||
|
|||
// 均线图层 |
|||
@property (nonatomic, strong) CAShapeLayer *ma5Layer; |
|||
@property (nonatomic, strong) CAShapeLayer *ma10Layer; |
|||
@property (nonatomic, strong) CAShapeLayer *ma30Layer; |
|||
|
|||
// MACD图层 |
|||
@property (nonatomic, strong) CAShapeLayer *macdRedBarLayer; |
|||
@property (nonatomic, strong) CAShapeLayer *macdGreenBarLayer; |
|||
@property (nonatomic, strong) CAShapeLayer *difLayer; |
|||
@property (nonatomic, strong) CAShapeLayer *deaLayer; |
|||
@property (nonatomic, strong) CAShapeLayer *zeroLineLayer; |
|||
|
|||
// KDJ图层 |
|||
@property (nonatomic, strong) CAShapeLayer *kLayer; |
|||
@property (nonatomic, strong) CAShapeLayer *dLayer; |
|||
@property (nonatomic, strong) CAShapeLayer *jLayer; |
|||
|
|||
@property (nonatomic, strong) UIView *crossVerticalLine; |
|||
@property (nonatomic, strong) UIView *crossHorizontalLine; |
|||
@property (nonatomic, strong) UILabel *crossPriceLabel; |
|||
@property (nonatomic, strong) UILabel *crossDateLabel; |
|||
@property (nonatomic, assign) BOOL isLongPressing; |
|||
|
|||
@end |
|||
|
|||
@implementation ChartViewController |
|||
|
|||
#pragma mark - viewDidLoad |
|||
- (void)viewDidLoad { |
|||
[super viewDidLoad]; |
|||
self.view.backgroundColor = [UIColor blackColor]; |
|||
|
|||
self.visibleKLineCount = 40; |
|||
|
|||
[self generateMockData]; |
|||
|
|||
[self calculateMA]; |
|||
[self calculateMACD]; |
|||
[self calculateKDJ]; |
|||
|
|||
[self setupSubviews]; |
|||
[self setupConstraints]; |
|||
[self setupDojiViews]; |
|||
|
|||
CGFloat chartVisibleWidth = self.view.bounds.size.width - kPriceLabelAreaWidth; |
|||
if (chartVisibleWidth > 0) { |
|||
kLineUnitWidth = chartVisibleWidth / self.visibleKLineCount; |
|||
} |
|||
|
|||
dispatch_async(dispatch_get_main_queue(), ^{ |
|||
CGFloat totalChartWidth = kLineUnitWidth * self.kLineData.count; |
|||
self.kLineScrollView.contentSize = CGSizeMake(totalChartWidth, self.kLineContainer.bounds.size.height); |
|||
|
|||
// 滚到最右侧 |
|||
if (self.kLineScrollView.contentSize.width > self.kLineScrollView.bounds.size.width) { |
|||
CGPoint offset = CGPointMake(self.kLineScrollView.contentSize.width - self.kLineScrollView.bounds.size.width, 0);//(横向偏移,纵向) |
|||
[self.kLineScrollView setContentOffset:offset animated:NO];// 禁用动画效果 |
|||
} |
|||
|
|||
[self drawAllCharts]; |
|||
}); |
|||
} |
|||
|
|||
#pragma mark - 绘制 |
|||
|
|||
- (void)drawAllCharts { |
|||
if (self.kLineData.count == 0) return; |
|||
|
|||
// 获取当前ScrollView滚到了哪里 |
|||
CGFloat contentOffsetX = self.kLineScrollView.contentOffset.x; |
|||
// 可视区域宽度 |
|||
CGFloat visibleWidth = self.kLineScrollView.bounds.size.width; |
|||
|
|||
NSInteger startIndex = floor(contentOffsetX / kLineUnitWidth);// 向下取整 |
|||
NSInteger endIndex = ceil((contentOffsetX + visibleWidth) / kLineUnitWidth);// 向上取整 |
|||
|
|||
// 防止越界 |
|||
if (startIndex < 0) startIndex = 0; |
|||
if (endIndex >= self.kLineData.count) endIndex = self.kLineData.count - 1; |
|||
if (startIndex > endIndex) endIndex = startIndex; |
|||
|
|||
// 把start,end和偏移量传给所有子视图 |
|||
[self drawKLineChartFromIndex:startIndex toIndex:endIndex contentOffset:contentOffsetX]; |
|||
[self drawMACDChartFromIndex:startIndex toIndex:endIndex contentOffset:contentOffsetX]; |
|||
[self drawKDJChartFromIndex:startIndex toIndex:endIndex contentOffset:contentOffsetX]; |
|||
|
|||
// 更新指标参数 |
|||
if (!self.isLongPressing) { |
|||
NSInteger visibleEndIndex = endIndex; |
|||
|
|||
CGFloat visibleRightX = contentOffsetX + self.kLineScrollView.bounds.size.width; |
|||
|
|||
NSInteger calculatedIndex = floor(visibleRightX / kLineUnitWidth) - 1; |
|||
|
|||
if (calculatedIndex < 0) calculatedIndex = 0; |
|||
if (calculatedIndex >= self.kLineData.count) calculatedIndex = self.kLineData.count - 1; |
|||
|
|||
visibleEndIndex = calculatedIndex; |
|||
|
|||
[self updateLegendsWithIndex:visibleEndIndex]; |
|||
} |
|||
} |
|||
|
|||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView { |
|||
if (self.isLongPressing) { |
|||
self.isLongPressing = NO; |
|||
self.crossVerticalLine.hidden = YES; |
|||
self.crossHorizontalLine.hidden = YES; |
|||
self.crossPriceLabel.hidden = YES; |
|||
self.crossDateLabel.hidden = YES; |
|||
} |
|||
[self drawAllCharts]; |
|||
} |
|||
|
|||
#pragma mark - 绘制K线+均线 |
|||
|
|||
- (void)drawKLineChartFromIndex:(NSInteger)startIndex toIndex:(NSInteger)endIndex contentOffset:(CGFloat)contentOffsetX { |
|||
// 图层大小=容器大小 |
|||
CGRect bounds = self.kLineContainer.bounds; |
|||
self.redCandleLayer.frame = bounds; |
|||
self.greenCandleLayer.frame = bounds; |
|||
self.ma5Layer.frame = bounds; |
|||
self.ma10Layer.frame = bounds; |
|||
self.ma30Layer.frame = bounds; |
|||
|
|||
CGFloat chartHeight = bounds.size.height; |
|||
|
|||
// 最高最低价 |
|||
CGFloat maxPrice = 0, minPrice = 0; |
|||
NSInteger maxIndex = -1, minIndex = -1; |
|||
[self calculateMinMaxPriceForStartIndex:startIndex endIndex:endIndex maxPrice:&maxPrice minPrice:&minPrice maxIndex:&maxIndex minIndex:&minIndex];//将计算出的最高价赋值给max/minPrice |
|||
|
|||
self.currentMaxPrice = maxPrice; |
|||
self.currentMinPrice = minPrice; |
|||
|
|||
CGFloat priceRange = maxPrice - minPrice; |
|||
if (priceRange <= 0) { |
|||
self.redCandleLayer.path = nil; |
|||
return; |
|||
} |
|||
|
|||
// 更新价格和日期 |
|||
[self updatePriceLabelsWithMaxPrice:maxPrice minPrice:minPrice]; |
|||
[self updateDateLabelsStartIndex:startIndex endIndex:endIndex]; |
|||
|
|||
// 准备路径 |
|||
UIBezierPath *redPath = [UIBezierPath bezierPath];// 涨的路径 |
|||
UIBezierPath *greenPath = [UIBezierPath bezierPath];// 跌的路径 |
|||
UIBezierPath *ma5Path = [UIBezierPath bezierPath];// 均线路径 |
|||
UIBezierPath *ma10Path = [UIBezierPath bezierPath]; |
|||
UIBezierPath *ma30Path = [UIBezierPath bezierPath]; |
|||
|
|||
CGFloat kLineWidth = kLineUnitWidth * 0.8;// k线实体宽度,0.2是间隔 |
|||
BOOL f5 = YES, f10 = YES, f30 = YES; |
|||
// 路径是否是第一个点(第一个点用moveToPoint,后面用addLineToPoint) |
|||
|
|||
for (NSInteger i = startIndex; i <= endIndex; i++) { |
|||
StockKLineModel *model = self.kLineData[i]; |
|||
|
|||
// 坐标转换 |
|||
CGFloat xCenter = [self getScreenCenterXAtIndex:i contentOffset:contentOffsetX]; |
|||
CGFloat xLeft = xCenter - kLineWidth / 2.0; |
|||
|
|||
// 高度 * (最高价 - 当前价)/ 价格区间 |
|||
CGFloat yOpen = chartHeight * (maxPrice - model.open) / priceRange; |
|||
CGFloat yClose = chartHeight * (maxPrice - model.close) / priceRange; |
|||
CGFloat yHigh = chartHeight * (maxPrice - model.high) / priceRange; |
|||
CGFloat yLow = chartHeight * (maxPrice - model.low) / priceRange; |
|||
|
|||
UIBezierPath *targetPath = (model.close >= model.open) ? redPath : greenPath; |
|||
|
|||
[targetPath moveToPoint:CGPointMake(xCenter, yHigh)]; |
|||
[targetPath addLineToPoint:CGPointMake(xCenter, yLow)];// 影线 |
|||
// 实体 |
|||
[targetPath appendPath:[UIBezierPath bezierPathWithRect: |
|||
CGRectMake(xLeft,// 左边界 |
|||
MIN(yOpen, yClose),// 上边界 |
|||
kLineWidth,// 宽 |
|||
MAX(1.0, fabs(yClose - yOpen)))]];// 高 |
|||
|
|||
// 绘制均线 |
|||
void (^drawMA)(UIBezierPath*, CGFloat, BOOL*) = ^(UIBezierPath *p, CGFloat v, BOOL *f) { |
|||
if (v > 0) { |
|||
CGFloat y = chartHeight * (maxPrice - v) / priceRange;// y轴 |
|||
if (*f) {// 是第一个点 |
|||
[p moveToPoint:CGPointMake(xCenter, y)];// 移动到起点 |
|||
*f = NO; }// 起点已设置 |
|||
else { [p addLineToPoint:CGPointMake(xCenter, y)]; }// 连线,从上一个点链接这个点 |
|||
} |
|||
}; |
|||
drawMA(ma5Path, model.MA5, &f5); |
|||
drawMA(ma10Path, model.MA10, &f10); |
|||
drawMA(ma30Path, model.MA30, &f30); |
|||
} |
|||
|
|||
[CATransaction begin];// 开启Core Animation事务 |
|||
[CATransaction setDisableActions:YES];// 禁止动画 |
|||
self.redCandleLayer.path = redPath.CGPath;// 图层绑定 |
|||
self.greenCandleLayer.path = greenPath.CGPath; |
|||
self.ma5Layer.path = ma5Path.CGPath; |
|||
self.ma10Layer.path = ma10Path.CGPath; |
|||
self.ma30Layer.path = ma30Path.CGPath; |
|||
[CATransaction commit];// 事务提交 |
|||
|
|||
// 更新最高最低价的箭头位置 |
|||
[self updateMaxMinArrowsWithMaxIndex:maxIndex minIndex:minIndex maxPrice:maxPrice minPrice:minPrice chartHeight:chartHeight range:priceRange contentOffsetX:contentOffsetX]; |
|||
} |
|||
|
|||
#pragma mark - 绘制MACD |
|||
- (void)drawMACDChartFromIndex:(NSInteger)startIndex toIndex:(NSInteger)endIndex contentOffset:(CGFloat)contentOffsetX { |
|||
CGRect bounds = self.macdContainer.bounds; |
|||
self.macdRedBarLayer.frame = bounds; |
|||
self.macdGreenBarLayer.frame = bounds; |
|||
self.difLayer.frame = bounds; |
|||
self.deaLayer.frame = bounds; |
|||
self.zeroLineLayer.frame = bounds; |
|||
|
|||
CGFloat h = bounds.size.height; |
|||
|
|||
// 计算极值 |
|||
CGFloat maxV = -MAXFLOAT, minV = MAXFLOAT;// 先给正负无穷 |
|||
for (NSInteger i = startIndex; i <= endIndex; i++) { |
|||
StockKLineModel *m = self.kLineData[i]; |
|||
maxV = MAX(maxV, MAX(m.macdBar, MAX(m.dif, m.dea))); |
|||
minV = MIN(minV, MIN(m.macdBar, MIN(m.dif, m.dea))); |
|||
} |
|||
if (maxV == minV) { maxV += 1; minV -= 1; } |
|||
self.macdMaxValue = maxV; |
|||
self.macdMinValue = minV; |
|||
CGFloat range = maxV - minV; |
|||
CGFloat zeroY = h * (maxV - 0) / range; |
|||
|
|||
UIBezierPath *rPath = [UIBezierPath bezierPath]; |
|||
UIBezierPath *gPath = [UIBezierPath bezierPath]; |
|||
UIBezierPath *dPath = [UIBezierPath bezierPath]; |
|||
UIBezierPath *ePath = [UIBezierPath bezierPath]; |
|||
UIBezierPath *zPath = [UIBezierPath bezierPath]; |
|||
[zPath moveToPoint:CGPointMake(0, zeroY)];// 0轴起点 |
|||
[zPath addLineToPoint:CGPointMake(bounds.size.width, zeroY)];// 0轴终点 |
|||
|
|||
CGFloat barWidth = kLineUnitWidth * 0.2; |
|||
BOOL first = YES;// 快线慢线起点标记 |
|||
|
|||
for (NSInteger i = startIndex; i <= endIndex; i++) { |
|||
StockKLineModel *m = self.kLineData[i]; |
|||
|
|||
CGFloat xCenter = [self getScreenCenterXAtIndex:i contentOffset:contentOffsetX]; |
|||
CGFloat xLeft = xCenter - barWidth/2.0; |
|||
|
|||
CGFloat yBar = h * (maxV - m.macdBar) / range; |
|||
CGFloat barY = (m.macdBar > 0) ? yBar : zeroY;// 上边界 |
|||
|
|||
CGFloat barH = MAX(0.5, fabs(zeroY - yBar));// 高度,大于0.5,省得看不见 |
|||
|
|||
UIBezierPath *tp = (m.macdBar > 0) ? rPath : gPath; |
|||
[tp appendPath:[UIBezierPath bezierPathWithRect: |
|||
CGRectMake(xLeft, |
|||
barY, |
|||
barWidth, |
|||
barH)]]; |
|||
|
|||
CGFloat yD = h * (maxV - m.dif) / range; |
|||
CGFloat yE = h * (maxV - m.dea) / range; |
|||
|
|||
if (first) { |
|||
[dPath moveToPoint:CGPointMake(xCenter, yD)]; |
|||
[ePath moveToPoint:CGPointMake(xCenter, yE)]; |
|||
first = NO; |
|||
} else { |
|||
[dPath addLineToPoint:CGPointMake(xCenter, yD)]; |
|||
[ePath addLineToPoint:CGPointMake(xCenter, yE)]; |
|||
} |
|||
} |
|||
|
|||
[CATransaction begin]; |
|||
[CATransaction setDisableActions:YES]; |
|||
self.macdRedBarLayer.path = rPath.CGPath; |
|||
self.macdGreenBarLayer.path = gPath.CGPath; |
|||
self.difLayer.path = dPath.CGPath; |
|||
self.deaLayer.path = ePath.CGPath; |
|||
self.zeroLineLayer.path = zPath.CGPath; |
|||
[CATransaction commit]; |
|||
} |
|||
|
|||
#pragma mark - 绘制KDJ |
|||
- (void)drawKDJChartFromIndex:(NSInteger)startIndex toIndex:(NSInteger)endIndex contentOffset:(CGFloat)contentOffsetX { |
|||
self.kLayer.frame = self.kdjContainer.bounds; |
|||
self.dLayer.frame = self.kdjContainer.bounds; |
|||
self.jLayer.frame = self.kdjContainer.bounds; |
|||
|
|||
CGFloat h = self.kdjContainer.bounds.size.height; |
|||
|
|||
CGFloat maxV = 0, minV = 100; |
|||
for (NSInteger i = startIndex; i <= endIndex; i++) { |
|||
StockKLineModel *m = self.kLineData[i]; |
|||
maxV = MAX(maxV, MAX(m.K, MAX(m.D, m.J))); |
|||
minV = MIN(minV, MIN(m.K, MIN(m.D, m.J))); |
|||
} |
|||
self.kdjMaxValue = maxV; |
|||
self.kdjMinValue = minV; |
|||
CGFloat range = maxV - minV; |
|||
if (range <= 0) range = 1; |
|||
|
|||
UIBezierPath *kp = [UIBezierPath bezierPath]; |
|||
UIBezierPath *dp = [UIBezierPath bezierPath]; |
|||
UIBezierPath *jp = [UIBezierPath bezierPath]; |
|||
BOOL first = YES; |
|||
|
|||
for (NSInteger i = startIndex; i <= endIndex; i++) { |
|||
StockKLineModel *m = self.kLineData[i]; |
|||
CGFloat xCenter = [self getScreenCenterXAtIndex:i contentOffset:contentOffsetX]; |
|||
|
|||
CGFloat yK = h * (maxV - m.K) / range; |
|||
CGFloat yD = h * (maxV - m.D) / range; |
|||
CGFloat yJ = h * (maxV - m.J) / range; |
|||
|
|||
if (first) { |
|||
[kp moveToPoint:CGPointMake(xCenter, yK)]; |
|||
[dp moveToPoint:CGPointMake(xCenter, yD)]; |
|||
[jp moveToPoint:CGPointMake(xCenter, yJ)]; |
|||
first = NO; |
|||
} else { |
|||
[kp addLineToPoint:CGPointMake(xCenter, yK)]; |
|||
[dp addLineToPoint:CGPointMake(xCenter, yD)]; |
|||
[jp addLineToPoint:CGPointMake(xCenter, yJ)]; |
|||
} |
|||
} |
|||
|
|||
[CATransaction begin]; |
|||
[CATransaction setDisableActions:YES]; |
|||
self.kLayer.path = kp.CGPath; |
|||
self.dLayer.path = dp.CGPath; |
|||
self.jLayer.path = jp.CGPath; |
|||
[CATransaction commit]; |
|||
} |
|||
|
|||
#pragma mark - 坐标计算 |
|||
// 获取某根K线在屏幕上的x轴中心坐标 |
|||
- (CGFloat)getScreenCenterXAtIndex:(NSInteger)index contentOffset:(CGFloat)offsetX { |
|||
// 半个单位宽+索引*宽 |
|||
CGFloat x = kLineUnitWidth * (index + 0.5); |
|||
// 屏幕坐标 = 绝对坐标 - 滚动偏移量 + 左侧空白区 |
|||
return x - offsetX + kPriceLabelAreaWidth; |
|||
} |
|||
|
|||
#pragma mark - 极值箭头更新 |
|||
- (void)updateMaxMinArrowsWithMaxIndex:(NSInteger)maxIdx minIndex:(NSInteger)minIdx maxPrice:(CGFloat)maxPrice minPrice:(CGFloat)minPrice chartHeight:(CGFloat)height range:(CGFloat)range contentOffsetX:(CGFloat)offsetX { |
|||
|
|||
void (^updateLabel)(UILabel *, NSInteger, CGFloat) = ^(UILabel *label, NSInteger index, CGFloat price) { |
|||
if (index >= 0 && index < self.kLineData.count) { |
|||
label.hidden = NO; |
|||
CGFloat xCenter = [self getScreenCenterXAtIndex:index contentOffset:offsetX]; |
|||
CGFloat y = height * (maxPrice - price) / range;// y轴位置 |
|||
|
|||
// 默认放在k线右边 |
|||
label.text = [NSString stringWithFormat:@"← %.2f", price]; |
|||
[label sizeToFit];// 根据文字尺寸自适应标签长度 |
|||
CGFloat targetX = xCenter + kLineUnitWidth/2.0 + 2 + label.bounds.size.width/2.0; |
|||
label.center = CGPointMake(targetX, y); |
|||
|
|||
// 如果label不在屏幕内,就放到k线左边 |
|||
if (CGRectGetMaxX(label.frame) > self.kLineContainer.bounds.size.width) { |
|||
label.text = [NSString stringWithFormat:@"%.2f →", price]; |
|||
[label sizeToFit]; |
|||
targetX = xCenter - kLineUnitWidth/2.0 - 2 - label.bounds.size.width/2.0; |
|||
label.center = CGPointMake(targetX, y); |
|||
} |
|||
} else { |
|||
label.hidden = YES; |
|||
} |
|||
}; |
|||
|
|||
StockKLineModel *maxModel = self.kLineData[maxIdx]; |
|||
updateLabel(self.highPriceMarkLabel, maxIdx, maxModel.high); |
|||
|
|||
StockKLineModel *minModel = self.kLineData[minIdx]; |
|||
updateLabel(self.lowPriceMarkLabel, minIdx, minModel.low); |
|||
} |
|||
|
|||
#pragma mark - 更新指标数值 |
|||
- (void)updateLegendsWithIndex:(NSInteger)index { |
|||
if (index < 0 || index >= self.kLineData.count) return; |
|||
|
|||
StockKLineModel *m = self.kLineData[index]; |
|||
|
|||
NSMutableAttributedString * (^createStr)(NSString *, UIColor *) = ^(NSString *txt, UIColor *col) { |
|||
return [[NSMutableAttributedString alloc] initWithString:txt attributes:@{NSForegroundColorAttributeName: col}]; |
|||
}; |
|||
|
|||
// MA |
|||
NSMutableAttributedString *maStr = [[NSMutableAttributedString alloc] init]; |
|||
[maStr appendAttributedString:createStr([NSString stringWithFormat:@"MA5:%.2f ", m.MA5], [UIColor yellowColor])]; |
|||
[maStr appendAttributedString:createStr([NSString stringWithFormat:@"MA10:%.2f ", m.MA10], [UIColor magentaColor])]; |
|||
[maStr appendAttributedString:createStr([NSString stringWithFormat:@"MA30:%.2f", m.MA30], [UIColor cyanColor])]; |
|||
self.maLegendLabel.attributedText = maStr; |
|||
|
|||
// MACD |
|||
NSMutableAttributedString *macdStr = [[NSMutableAttributedString alloc] init]; |
|||
[macdStr appendAttributedString:createStr([NSString stringWithFormat:@"DIF:%.2f ", m.dif], [UIColor whiteColor])]; |
|||
[macdStr appendAttributedString:createStr([NSString stringWithFormat:@"DEA:%.2f ", m.dea], [UIColor yellowColor])]; |
|||
UIColor *barColor = (m.macdBar > 0) ? [UIColor redColor] : [UIColor greenColor]; |
|||
[macdStr appendAttributedString:createStr([NSString stringWithFormat:@"MACD:%.2f", m.macdBar], barColor)]; |
|||
self.macdLegendLabel.attributedText = macdStr; |
|||
|
|||
// KDJ |
|||
NSMutableAttributedString *kdjStr = [[NSMutableAttributedString alloc] init]; |
|||
[kdjStr appendAttributedString:createStr([NSString stringWithFormat:@"K:%.2f ", m.K], [UIColor whiteColor])]; |
|||
[kdjStr appendAttributedString:createStr([NSString stringWithFormat:@"D:%.2f ", m.D], [UIColor yellowColor])]; |
|||
[kdjStr appendAttributedString:createStr([NSString stringWithFormat:@"J:%.2f", m.J], [UIColor magentaColor])]; |
|||
self.kdjLegendLabel.attributedText = kdjStr; |
|||
} |
|||
|
|||
#pragma mark - 手势与缩放 |
|||
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)gesture { |
|||
if (self.isLongPressing) {// 缩放不显示十字星 |
|||
self.isLongPressing = NO; |
|||
self.crossVerticalLine.hidden = YES; |
|||
self.crossHorizontalLine.hidden = YES; |
|||
self.crossPriceLabel.hidden = YES; |
|||
self.crossDateLabel.hidden = YES; |
|||
} |
|||
|
|||
if (gesture.state == UIGestureRecognizerStateChanged) { |
|||
CGFloat scale = gesture.scale; |
|||
CGFloat minUnitWidth = (self.view.bounds.size.width - kPriceLabelAreaWidth) / 500.0; |
|||
CGFloat maxUnitWidth = 40.0; |
|||
|
|||
CGFloat newUnitWidth = MAX(minUnitWidth, MIN(kLineUnitWidth * scale, maxUnitWidth)); |
|||
|
|||
// 屏幕中心点缩放 |
|||
CGFloat ratio = (self.kLineScrollView.contentOffset.x + self.kLineScrollView.bounds.size.width/2.0) / self.kLineScrollView.contentSize.width; |
|||
kLineUnitWidth = newUnitWidth;// 全局更新 |
|||
|
|||
// 新的滚动视图的内容宽度 |
|||
CGFloat newContentWidth = kLineUnitWidth * self.kLineData.count; |
|||
self.kLineScrollView.contentSize = CGSizeMake(newContentWidth, self.kLineContainer.bounds.size.height); |
|||
|
|||
// 新的偏移量 |
|||
CGFloat newOffset = ratio * newContentWidth - self.kLineScrollView.bounds.size.width/2.0; |
|||
self.kLineScrollView.contentOffset = CGPointMake(MAX(0, newOffset), 0); |
|||
|
|||
[self drawAllCharts]; |
|||
gesture.scale = 1.0; |
|||
} |
|||
} |
|||
|
|||
#pragma mark - 数据生成 |
|||
- (void)generateMockData { |
|||
NSMutableArray *arr = [NSMutableArray array]; |
|||
CGFloat lastClose = 100.0; |
|||
for (int i = 0; i < 2000; i++) { |
|||
StockKLineModel *model = [[StockKLineModel alloc] init]; |
|||
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:-(2000 - i) * 24 * 3600]; |
|||
NSDateFormatter *fmt = [[NSDateFormatter alloc] init]; |
|||
[fmt setDateFormat:@"yyyy-MM-dd"]; |
|||
model.date = [fmt stringFromDate:date]; |
|||
CGFloat volatility = lastClose * 0.02; |
|||
CGFloat randomChange = ((arc4random() % 100) / 100.0 - 0.5) * 2 * volatility; |
|||
model.open = lastClose + ((arc4random() % 100) / 100.0 - 0.5) * volatility * 0.5; |
|||
model.close = model.open + randomChange; |
|||
CGFloat maxOC = MAX(model.open, model.close); |
|||
CGFloat minOC = MIN(model.open, model.close); |
|||
model.high = maxOC + (arc4random() % 100) / 100.0 * 1.0; |
|||
model.low = minOC - (arc4random() % 100) / 100.0 * 1.0; |
|||
if (model.low < 0) model.low = 0.01; |
|||
[arr addObject:model]; |
|||
lastClose = model.close; |
|||
} |
|||
self.kLineData = arr; |
|||
} |
|||
|
|||
- (void)calculateMA { |
|||
for (int i = 0; i < self.kLineData.count; i++) { |
|||
StockKLineModel *model = self.kLineData[i]; |
|||
model.MA5 = [self getMAWithIndex:i count:5]; |
|||
model.MA10 = [self getMAWithIndex:i count:10]; |
|||
model.MA30 = [self getMAWithIndex:i count:30]; |
|||
} |
|||
} |
|||
- (CGFloat)getMAWithIndex:(NSInteger)index count:(NSInteger)count { |
|||
if (index < count - 1) return 0; |
|||
CGFloat sum = 0; |
|||
for (NSInteger i = index; i > index - count; i--) { |
|||
StockKLineModel *m = self.kLineData[i]; |
|||
sum += m.close; |
|||
} |
|||
return sum / count; |
|||
} |
|||
- (void)calculateMACD { |
|||
if (self.kLineData.count == 0) return; |
|||
const CGFloat kShortEMA = 2.0 / (12 + 1); |
|||
const CGFloat kLongEMA = 2.0 / (26 + 1); |
|||
const CGFloat kSignalEMA = 2.0 / (9 + 1); |
|||
CGFloat lastShortEMA = 0, lastLongEMA = 0, lastDEA = 0; |
|||
for (int i = 0; i < self.kLineData.count; i++) { |
|||
StockKLineModel *model = self.kLineData[i]; |
|||
CGFloat close = model.close; |
|||
if (i == 0) { |
|||
lastShortEMA = close; lastLongEMA = close; |
|||
model.dif = 0; model.dea = 0; model.macdBar = 0; |
|||
} else { |
|||
lastShortEMA = kShortEMA * close + (1 - kShortEMA) * lastShortEMA; |
|||
lastLongEMA = kLongEMA * close + (1 - kLongEMA) * lastLongEMA; |
|||
model.dif = lastShortEMA - lastLongEMA; |
|||
model.dea = kSignalEMA * model.dif + (1 - kSignalEMA) * lastDEA; |
|||
model.macdBar = 2.0 * (model.dif - model.dea); |
|||
} |
|||
lastDEA = model.dea; |
|||
} |
|||
} |
|||
- (void)calculateKDJ { |
|||
CGFloat k = 50.0, d = 50.0; |
|||
for (int i = 0; i < self.kLineData.count; i++) { |
|||
StockKLineModel *model = self.kLineData[i]; |
|||
NSInteger startIndex = MAX(0, i - 8); |
|||
CGFloat maxHigh = -MAXFLOAT; |
|||
CGFloat minLow = MAXFLOAT; |
|||
for (NSInteger j = startIndex; j <= i; j++) { |
|||
StockKLineModel *m = self.kLineData[j]; |
|||
maxHigh = MAX(maxHigh, m.high); |
|||
minLow = MIN(minLow, m.low); |
|||
} |
|||
CGFloat rsv = 0; |
|||
if (maxHigh != minLow) rsv = (model.close - minLow) / (maxHigh - minLow) * 100.0; |
|||
k = (2.0 * k + rsv) / 3.0; |
|||
d = (2.0 * d + k) / 3.0; |
|||
model.K = k; model.D = d; model.J = 3.0 * k - 2.0 * d; |
|||
} |
|||
} |
|||
|
|||
#pragma mark 计算极值 |
|||
- (void)calculateMinMaxPriceForStartIndex:(NSInteger)startIndex endIndex:(NSInteger)endIndex maxPrice:(CGFloat *)maxPrice minPrice:(CGFloat *)minPrice maxIndex:(NSInteger *)maxIdx minIndex:(NSInteger *)minIdx { |
|||
*maxPrice = -MAXFLOAT; *minPrice = MAXFLOAT; |
|||
*maxIdx = -1; *minIdx = -1; |
|||
if (self.kLineData.count == 0) return; |
|||
for (NSInteger i = startIndex; i <= endIndex; i++) { |
|||
StockKLineModel *m = self.kLineData[i]; |
|||
if (m.high > *maxPrice) { *maxPrice = m.high; *maxIdx = i; } |
|||
if (m.low < *minPrice) { *minPrice = m.low; *minIdx = i; } |
|||
if (m.MA5 > 0) { *maxPrice = MAX(*maxPrice, m.MA5); *minPrice = MIN(*minPrice, m.MA5); } |
|||
if (m.MA10 > 0) { *maxPrice = MAX(*maxPrice, m.MA10); *minPrice = MIN(*minPrice, m.MA10); } |
|||
if (m.MA30 > 0) { *maxPrice = MAX(*maxPrice, m.MA30); *minPrice = MIN(*minPrice, m.MA30); } |
|||
} |
|||
if (*maxPrice > *minPrice) { |
|||
CGFloat d = *maxPrice - *minPrice; |
|||
*maxPrice += d * 0.05; *minPrice -= d * 0.05; |
|||
} |
|||
} |
|||
|
|||
#pragma mark - setupSubviews |
|||
-(void) setupSubviews { |
|||
UIColor *bgColor = [UIColor colorWithRed:26.0/255.0 green:26.0/255.0 blue:26.0/255.0 alpha:1.0]; |
|||
|
|||
_cardContainer = [[StockInfoCardView alloc] init]; |
|||
_cardContainer.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_cardContainer setupView]; |
|||
[self.view addSubview:_cardContainer]; |
|||
|
|||
_kSelectContainer = [[UIView alloc] init]; |
|||
_kSelectContainer.backgroundColor = bgColor; |
|||
_kSelectContainer.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[self.view addSubview:_kSelectContainer]; |
|||
[self addKSelectOptions]; |
|||
|
|||
self.kLineContainer = [self createContainerViewWithColor:bgColor]; |
|||
self.macdContainer = [self createContainerViewWithColor:bgColor]; |
|||
self.kdjContainer = [self createContainerViewWithColor:bgColor]; |
|||
self.otherContainer = [self createContainerViewWithColor:bgColor]; |
|||
|
|||
self.kLineScrollView = [[UIScrollView alloc] init]; |
|||
self.kLineScrollView.backgroundColor = [UIColor clearColor]; |
|||
self.kLineScrollView.showsHorizontalScrollIndicator = NO; |
|||
self.kLineScrollView.translatesAutoresizingMaskIntoConstraints = NO; |
|||
self.kLineScrollView.delegate = self; |
|||
[self.kLineContainer addSubview:self.kLineScrollView]; |
|||
|
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[self.kLineScrollView.topAnchor constraintEqualToAnchor:self.kLineContainer.topAnchor], |
|||
[self.kLineScrollView.bottomAnchor constraintEqualToAnchor:self.kLineContainer.bottomAnchor], |
|||
[self.kLineScrollView.leadingAnchor constraintEqualToAnchor:self.kLineContainer.leadingAnchor constant:kPriceLabelAreaWidth], |
|||
[self.kLineScrollView.trailingAnchor constraintEqualToAnchor:self.kLineContainer.trailingAnchor], |
|||
]]; |
|||
|
|||
UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinchGesture:)]; |
|||
[self.kLineScrollView addGestureRecognizer:pinchGesture]; |
|||
|
|||
[self setupAllLayers]; |
|||
[self setupLabels]; |
|||
|
|||
self.maLegendLabel = [self createLegendLabel]; |
|||
[self.view addSubview:self.maLegendLabel]; |
|||
self.macdLegendLabel = [self createLegendLabel]; |
|||
[self.view addSubview:self.macdLegendLabel]; |
|||
self.kdjLegendLabel = [self createLegendLabel]; |
|||
[self.view addSubview:self.kdjLegendLabel]; |
|||
|
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[self.maLegendLabel.leadingAnchor constraintEqualToAnchor:self.kLineContainer.leadingAnchor constant:kPriceLabelAreaWidth], |
|||
[self.maLegendLabel.bottomAnchor constraintEqualToAnchor:self.kLineContainer.topAnchor], |
|||
[self.macdLegendLabel.leadingAnchor constraintEqualToAnchor:self.macdContainer.leadingAnchor constant:kPriceLabelAreaWidth], |
|||
[self.macdLegendLabel.bottomAnchor constraintEqualToAnchor:self.macdContainer.topAnchor], |
|||
[self.kdjLegendLabel.leadingAnchor constraintEqualToAnchor:self.kdjContainer.leadingAnchor constant:kPriceLabelAreaWidth], |
|||
[self.kdjLegendLabel.bottomAnchor constraintEqualToAnchor:self.kdjContainer.topAnchor] |
|||
]]; |
|||
|
|||
self.highPriceMarkLabel = [self createMarkLabel]; |
|||
[self.kLineContainer addSubview:self.highPriceMarkLabel]; |
|||
self.lowPriceMarkLabel = [self createMarkLabel]; |
|||
[self.kLineContainer addSubview:self.lowPriceMarkLabel]; |
|||
} |
|||
|
|||
- (UIView *)createContainerViewWithColor:(UIColor *)color { |
|||
UIView *uiView = [[UIView alloc] init]; |
|||
uiView.backgroundColor = color; |
|||
uiView.translatesAutoresizingMaskIntoConstraints = NO; |
|||
uiView.clipsToBounds = YES; |
|||
[self.view addSubview:uiView]; |
|||
return uiView; |
|||
} |
|||
|
|||
#pragma mark - setupConstraints |
|||
- (void)setupConstraints { |
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[_cardContainer.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor], |
|||
[_cardContainer.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], |
|||
[_cardContainer.widthAnchor constraintEqualToAnchor:self.view.widthAnchor], |
|||
[_cardContainer.heightAnchor constraintEqualToConstant:100], |
|||
|
|||
[_kSelectContainer.topAnchor constraintEqualToAnchor:_cardContainer.bottomAnchor constant:5], |
|||
[_kSelectContainer.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], |
|||
[_kSelectContainer.widthAnchor constraintEqualToAnchor:self.view.widthAnchor], |
|||
[_kSelectContainer.heightAnchor constraintEqualToConstant:40], |
|||
]]; |
|||
|
|||
// 循环设置图表容器约束 |
|||
NSArray *containers = @[self.kLineContainer, self.macdContainer, self.kdjContainer, self.otherContainer]; |
|||
NSArray *heights = @[@(kKLineHeight), @(kContainerHeight), @(kContainerHeight), @(kContainerHeight)]; |
|||
|
|||
UIView *previousView = _kSelectContainer; |
|||
|
|||
for (int i = 0; i < containers.count; i++) { |
|||
UIView *container = containers[i]; |
|||
// 间距 第一个是15,MACD是30,其他是15 |
|||
CGFloat spacing = (i == 1) ? 30.0 : 15.0; |
|||
|
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[container.topAnchor constraintEqualToAnchor:previousView.bottomAnchor constant:spacing], |
|||
[container.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], |
|||
[container.widthAnchor constraintEqualToAnchor:self.view.widthAnchor], |
|||
[container.heightAnchor constraintEqualToConstant:[heights[i] floatValue]] |
|||
]]; |
|||
|
|||
previousView = container; |
|||
} |
|||
} |
|||
|
|||
- (void)setupAllLayers { |
|||
// K线 |
|||
_redCandleLayer = [self createLayerColor:[UIColor redColor] width:1.0 filled:YES]; |
|||
_greenCandleLayer = [self createLayerColor:[UIColor greenColor] width:1.0 filled:YES]; |
|||
[self.kLineContainer.layer insertSublayer:_redCandleLayer atIndex:0]; |
|||
[self.kLineContainer.layer insertSublayer:_greenCandleLayer atIndex:0]; |
|||
|
|||
// 均线 |
|||
_ma5Layer = [self createLayerColor:[UIColor yellowColor] width:1.0 filled:NO]; |
|||
_ma10Layer = [self createLayerColor:[UIColor magentaColor] width:1.0 filled:NO]; |
|||
_ma30Layer = [self createLayerColor:[UIColor cyanColor] width:1.0 filled:NO]; |
|||
[self.kLineContainer.layer insertSublayer:_ma5Layer below:_redCandleLayer]; |
|||
[self.kLineContainer.layer insertSublayer:_ma10Layer below:_ma5Layer]; |
|||
[self.kLineContainer.layer insertSublayer:_ma30Layer below:_ma10Layer]; |
|||
|
|||
// MACD |
|||
_macdRedBarLayer = [self createLayerColor:[UIColor redColor] width:0 filled:YES]; |
|||
_macdGreenBarLayer = [self createLayerColor:[UIColor greenColor] width:0 filled:YES]; |
|||
_difLayer = [self createLayerColor:[UIColor whiteColor] width:1.0 filled:NO]; |
|||
_deaLayer = [self createLayerColor:[UIColor yellowColor] width:1.0 filled:NO]; |
|||
_zeroLineLayer = [self createLayerColor:[UIColor grayColor] width:0.5 filled:NO]; |
|||
[self.macdContainer.layer insertSublayer:_zeroLineLayer atIndex:0]; |
|||
[self.macdContainer.layer insertSublayer:_macdRedBarLayer above:_zeroLineLayer]; |
|||
[self.macdContainer.layer insertSublayer:_macdGreenBarLayer above:_macdRedBarLayer]; |
|||
[self.macdContainer.layer insertSublayer:_difLayer above:_macdGreenBarLayer]; |
|||
[self.macdContainer.layer insertSublayer:_deaLayer above:_difLayer]; |
|||
|
|||
// KDJ |
|||
_kLayer = [self createLayerColor:[UIColor whiteColor] width:1.0 filled:NO]; |
|||
_dLayer = [self createLayerColor:[UIColor yellowColor] width:1.0 filled:NO]; |
|||
_jLayer = [self createLayerColor:[UIColor magentaColor] width:1.0 filled:NO]; |
|||
[self.kdjContainer.layer insertSublayer:_kLayer atIndex:0]; |
|||
[self.kdjContainer.layer insertSublayer:_dLayer above:_kLayer]; |
|||
[self.kdjContainer.layer insertSublayer:_jLayer above:_dLayer]; |
|||
} |
|||
|
|||
- (CAShapeLayer *)createLayerColor:(UIColor *)color width:(CGFloat)width filled:(BOOL)fill { |
|||
CAShapeLayer *layer = [CAShapeLayer layer]; |
|||
layer.lineWidth = width; |
|||
layer.strokeColor = color.CGColor; |
|||
layer.fillColor = fill ? color.CGColor : [UIColor clearColor].CGColor; |
|||
layer.lineCap = kCALineCapSquare; |
|||
layer.lineJoin = kCALineJoinRound;// 圆角连接,避免折线拐角出现尖锐锯齿 |
|||
return layer; |
|||
} |
|||
|
|||
- (UILabel *)createLegendLabel { |
|||
UILabel *label = [[UILabel alloc] init]; |
|||
label.font = [UIFont systemFontOfSize:10]; |
|||
label.textColor = [UIColor whiteColor]; |
|||
label.backgroundColor = [UIColor clearColor]; |
|||
label.translatesAutoresizingMaskIntoConstraints = NO; |
|||
return label; |
|||
} |
|||
|
|||
- (UILabel *)createMarkLabel { |
|||
UILabel *label = [[UILabel alloc] init]; |
|||
label.font = [UIFont systemFontOfSize:10]; |
|||
label.textColor = [UIColor whiteColor]; |
|||
label.backgroundColor = [UIColor clearColor]; |
|||
label.hidden = YES; |
|||
return label; |
|||
} |
|||
|
|||
- (void)setupLabels { |
|||
NSMutableArray *priceLabelArr = [NSMutableArray array]; |
|||
CGFloat chartUsableHeight = kKLineHeight - kPriceLabelPadding; |
|||
for (NSInteger i = 0; i < 5; i++) { |
|||
UILabel *label = [[UILabel alloc] init]; |
|||
label.text = @"--"; |
|||
label.textColor = [UIColor lightGrayColor]; |
|||
label.font = [UIFont systemFontOfSize:10]; |
|||
label.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[self.kLineContainer addSubview:label]; |
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[label.leadingAnchor constraintEqualToAnchor:self.kLineContainer.leadingAnchor constant:2], |
|||
[label.topAnchor constraintEqualToAnchor:self.kLineContainer.topAnchor constant: (chartUsableHeight / 4 * i)] |
|||
]]; |
|||
[priceLabelArr addObject:label]; |
|||
} |
|||
self.priceLabels = priceLabelArr; |
|||
|
|||
self.startDateLabel = [[UILabel alloc] init]; |
|||
self.startDateLabel.textColor = [UIColor lightGrayColor]; |
|||
self.startDateLabel.font = [UIFont systemFontOfSize:10]; |
|||
self.startDateLabel.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[self.view addSubview:self.startDateLabel]; |
|||
|
|||
self.endDateLabel = [[UILabel alloc] init]; |
|||
self.endDateLabel.textColor = [UIColor lightGrayColor]; |
|||
self.endDateLabel.font = [UIFont systemFontOfSize:10]; |
|||
self.endDateLabel.textAlignment = NSTextAlignmentRight; |
|||
self.endDateLabel.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[self.view addSubview:self.endDateLabel]; |
|||
|
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[self.startDateLabel.leadingAnchor constraintEqualToAnchor:self.kLineContainer.leadingAnchor constant:kPriceLabelAreaWidth], |
|||
[self.startDateLabel.topAnchor constraintEqualToAnchor:self.kLineContainer.bottomAnchor constant:2], |
|||
[self.endDateLabel.trailingAnchor constraintEqualToAnchor:self.kLineContainer.trailingAnchor], |
|||
[self.endDateLabel.topAnchor constraintEqualToAnchor:self.kLineContainer.bottomAnchor constant:2], |
|||
]]; |
|||
} |
|||
|
|||
#pragma mark - 左侧价格刻度计算 |
|||
- (void)updatePriceLabelsWithMaxPrice:(CGFloat)maxPrice minPrice:(CGFloat)minPrice { |
|||
CGFloat range = maxPrice - minPrice; |
|||
for (NSInteger i = 0; i < 5; i++) { |
|||
self.priceLabels[i].text = [NSString stringWithFormat:@"%.2f", maxPrice - range / 4.0 * i]; |
|||
} |
|||
} |
|||
|
|||
# pragma mark - 日期计算 |
|||
- (void)updateDateLabelsStartIndex:(NSInteger)startIndex endIndex:(NSInteger)endIndex { |
|||
if (self.kLineData.count > 0) { |
|||
if (startIndex < self.kLineData.count) self.startDateLabel.text = ((StockKLineModel *)self.kLineData[startIndex]).date; |
|||
if (endIndex < self.kLineData.count) self.endDateLabel.text = ((StockKLineModel *)self.kLineData[endIndex]).date; |
|||
} |
|||
} |
|||
|
|||
#pragma mark - k线选择 |
|||
- (void)addKSelectOptions { |
|||
NSArray *titles = @[@"分时",@"日k", @"周k", @"月k", @"更多", @"设置"]; |
|||
UIStackView *stackView = [[UIStackView alloc] init]; |
|||
stackView.axis = UILayoutConstraintAxisHorizontal; |
|||
stackView.distribution = UIStackViewDistributionFillEqually; |
|||
stackView.alignment = UIStackViewAlignmentCenter; |
|||
stackView.spacing = 5.0; |
|||
stackView.translatesAutoresizingMaskIntoConstraints = NO; |
|||
for (NSString *title in titles) { |
|||
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem]; |
|||
[button setTitle:title forState:UIControlStateNormal]; |
|||
button.titleLabel.font = [UIFont systemFontOfSize:14]; |
|||
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; |
|||
[stackView addArrangedSubview:button]; |
|||
} |
|||
[_kSelectContainer addSubview:stackView]; |
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[stackView.leadingAnchor constraintEqualToAnchor:_kSelectContainer.leadingAnchor constant:10], |
|||
[stackView.trailingAnchor constraintEqualToAnchor:_kSelectContainer.trailingAnchor constant:-10], |
|||
[stackView.topAnchor constraintEqualToAnchor:_kSelectContainer.topAnchor constant:15], |
|||
[stackView.bottomAnchor constraintEqualToAnchor:_kSelectContainer.bottomAnchor constant:-15] |
|||
]]; |
|||
} |
|||
|
|||
#pragma mark - 十字星 |
|||
- (void)setupDojiViews { |
|||
// 竖线 |
|||
self.crossVerticalLine = [[UIView alloc] init]; |
|||
self.crossVerticalLine.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.8]; |
|||
self.crossVerticalLine.hidden = YES; |
|||
self.crossVerticalLine.userInteractionEnabled = NO; // 不挡手势 |
|||
[self.view addSubview:self.crossVerticalLine]; |
|||
|
|||
// 横线 |
|||
self.crossHorizontalLine = [[UIView alloc] init]; |
|||
self.crossHorizontalLine.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.8]; |
|||
self.crossHorizontalLine.hidden = YES; |
|||
self.crossHorizontalLine.userInteractionEnabled = NO; |
|||
[self.view addSubview:self.crossHorizontalLine]; |
|||
|
|||
// 价格 |
|||
self.crossPriceLabel = [[UILabel alloc] init]; |
|||
self.crossPriceLabel.backgroundColor = [UIColor systemBlueColor]; |
|||
self.crossPriceLabel.textColor = [UIColor whiteColor]; |
|||
self.crossPriceLabel.font = [UIFont systemFontOfSize:9]; |
|||
self.crossPriceLabel.textAlignment = NSTextAlignmentCenter; |
|||
self.crossPriceLabel.clipsToBounds = YES; |
|||
self.crossPriceLabel.layer.cornerRadius = 2.0; |
|||
self.crossPriceLabel.hidden = YES; |
|||
[self.view addSubview:self.crossPriceLabel]; |
|||
|
|||
// 日期 |
|||
self.crossDateLabel = [[UILabel alloc] init]; |
|||
self.crossDateLabel.backgroundColor = [UIColor systemBlueColor]; |
|||
self.crossDateLabel.textColor = [UIColor whiteColor]; |
|||
self.crossDateLabel.font = [UIFont systemFontOfSize:9]; |
|||
self.crossDateLabel.textAlignment = NSTextAlignmentCenter;// 居中对齐 |
|||
//NSTextAlignmentJustified 两端对齐 |
|||
self.crossDateLabel.clipsToBounds = YES; |
|||
self.crossDateLabel.layer.cornerRadius = 2.0; |
|||
self.crossDateLabel.hidden = YES; |
|||
[self.view addSubview:self.crossDateLabel]; |
|||
|
|||
NSArray *targetViews = @[self.kLineScrollView, self.macdContainer, self.kdjContainer]; |
|||
for (UIView *view in targetViews) { |
|||
// 必须在循环里创建新的手势对象,因为一个手势只能绑定一个View |
|||
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)]; |
|||
longPress.minimumPressDuration = 0.3; // 设置触发时间 |
|||
[view addGestureRecognizer:longPress]; |
|||
} |
|||
} |
|||
|
|||
- (void)handleLongPress:(UILongPressGestureRecognizer *)gesture { |
|||
CGPoint touchPoint = [gesture locationInView:self.kLineScrollView]; |
|||
CGPoint touchPointInView = [gesture locationInView:self.view]; |
|||
|
|||
CGFloat offsetX = self.kLineScrollView.contentOffset.x; |
|||
|
|||
NSInteger index = floor(touchPoint.x / kLineUnitWidth);// 向下取整 |
|||
if (index < 0) index = 0; |
|||
if (index >= self.kLineData.count) index = self.kLineData.count - 1; |
|||
|
|||
if (gesture.state == UIGestureRecognizerStateBegan || gesture.state == UIGestureRecognizerStateChanged) {// 手势开始/移动 |
|||
self.isLongPressing = YES; |
|||
self.crossVerticalLine.hidden = NO; |
|||
self.crossHorizontalLine.hidden = NO; |
|||
self.crossPriceLabel.hidden = NO; |
|||
self.crossDateLabel.hidden = NO;// 显示十字星 |
|||
|
|||
// 更新竖线位置 |
|||
CGFloat xCenterInScroll = kLineUnitWidth * 0.5 + kLineUnitWidth * index; |
|||
//滚动视图x坐标 - 滚动偏移量 + 价格标签宽度 |
|||
CGFloat xCenterInView = xCenterInScroll - offsetX + kPriceLabelAreaWidth; |
|||
|
|||
// 不要越界 |
|||
if (xCenterInView < kPriceLabelAreaWidth || xCenterInView > self.view.bounds.size.width) return; |
|||
|
|||
CGFloat topY = self.kLineContainer.frame.origin.y;// k线容器在主视图的顶部y |
|||
CGFloat bottomY = CGRectGetMaxY(self.otherContainer.frame);// 空容器的底部 |
|||
self.crossVerticalLine.frame = CGRectMake(xCenterInView, topY, 0.5, bottomY - topY); |
|||
|
|||
CGFloat displayValue = 0.0; |
|||
BOOL isTouchValid = NO; // 用于标记是否摸到了有效的图表区域 |
|||
|
|||
// 判断手指在哪个容器里 |
|||
if (CGRectContainsPoint(self.kLineContainer.frame, touchPointInView)) {// CGRectContainsPoint(矩形区域, 点) 布尔: |
|||
isTouchValid = YES; |
|||
CGFloat relativeY = touchPointInView.y - self.kLineContainer.frame.origin.y; |
|||
CGFloat height = self.kLineContainer.frame.size.height; |
|||
displayValue = self.currentMaxPrice - (relativeY / height) * (self.currentMaxPrice - self.currentMinPrice); |
|||
|
|||
} else if (CGRectContainsPoint(self.macdContainer.frame, touchPointInView)) { |
|||
isTouchValid = YES; |
|||
CGFloat relativeY = touchPointInView.y - self.macdContainer.frame.origin.y; |
|||
CGFloat height = self.macdContainer.frame.size.height; |
|||
displayValue = self.macdMaxValue - (relativeY / height) * (self.macdMaxValue - self.macdMinValue); |
|||
|
|||
} else if (CGRectContainsPoint(self.kdjContainer.frame, touchPointInView)) { |
|||
isTouchValid = YES; |
|||
CGFloat relativeY = touchPointInView.y - self.kdjContainer.frame.origin.y; |
|||
CGFloat height = self.kdjContainer.frame.size.height; |
|||
displayValue = self.kdjMaxValue - (relativeY / height) * (self.kdjMaxValue - self.kdjMinValue); |
|||
} |
|||
|
|||
// 只有触摸在图表内才更新横线 |
|||
if (isTouchValid) { |
|||
self.crossHorizontalLine.frame = CGRectMake(kPriceLabelAreaWidth, touchPointInView.y, self.view.bounds.size.width - kPriceLabelAreaWidth, 0.5); |
|||
|
|||
self.crossPriceLabel.text = [NSString stringWithFormat:@" %.2f ", displayValue]; |
|||
[self.crossPriceLabel sizeToFit]; |
|||
self.crossPriceLabel.center = CGPointMake(kPriceLabelAreaWidth / 2.0, touchPointInView.y); |
|||
} |
|||
|
|||
StockKLineModel *model = self.kLineData[index]; |
|||
self.crossDateLabel.text = [NSString stringWithFormat:@" %@ ", model.date]; |
|||
[self.crossDateLabel sizeToFit]; |
|||
CGFloat dateLabelY = CGRectGetMaxY(self.kLineContainer.frame); |
|||
self.crossDateLabel.center = CGPointMake(xCenterInView, dateLabelY); |
|||
|
|||
[self updateLegendsWithIndex:index]; |
|||
|
|||
} else if (gesture.state == UIGestureRecognizerStateEnded || gesture.state == UIGestureRecognizerStateCancelled) {// 结束/取消 |
|||
self.isLongPressing = NO; |
|||
self.crossVerticalLine.hidden = YES; |
|||
self.crossHorizontalLine.hidden = YES; |
|||
self.crossPriceLabel.hidden = YES; |
|||
self.crossDateLabel.hidden = YES; |
|||
|
|||
[self drawAllCharts]; |
|||
} |
|||
} |
|||
|
|||
|
|||
@end |
|||
@ -0,0 +1,585 @@ |
|||
// |
|||
// MLXYViewController.m |
|||
// HC |
|||
// |
|||
// Created by huilinLi on 2025/11/26. |
|||
// |
|||
|
|||
#import "MLXYViewController.h" |
|||
#import "ChartViewController.h" |
|||
|
|||
#pragma mark - cell们 |
|||
static NSString *const stockCell = @"StockCell";// 股票cell |
|||
static NSString *const permCell = @"PermCell";// 接口cell |
|||
|
|||
@interface MLXYViewController () <UITableViewDataSource, UITableViewDelegate> |
|||
|
|||
#pragma mark - Config |
|||
@property (nonatomic, strong) UIButtonConfiguration *buttonConfig; |
|||
@property (nonatomic, strong) NSArray *sectorDataConfig; |
|||
@property (nonatomic, strong) NSMutableArray *permissionDataList; |
|||
|
|||
#pragma mark - 滚动相关 |
|||
@property (nonatomic, strong) UIScrollView *globalScrollView; |
|||
@property (nonatomic, strong) UIView *scrollContentView; |
|||
@property (nonatomic, strong) UIButton *mlxyButton; |
|||
|
|||
#pragma mark - 大盘 |
|||
@property (nonatomic, strong) UILabel *marketTitleLabel; |
|||
@property (nonatomic, strong) UIView *marketView; |
|||
@property (nonatomic, strong) UILabel *marketLine1; |
|||
@property (nonatomic, strong) UILabel *marketLine2; |
|||
@property (nonatomic, strong) UILabel *marketLine3; |
|||
|
|||
#pragma mark - 板块 |
|||
@property (nonatomic, strong) UILabel *sectorTitleLabel; |
|||
@property (nonatomic, strong) UIButton *sectorBtn; |
|||
@property (nonatomic, strong) UIView *sectorCard1; |
|||
@property (nonatomic, strong) UIView *sectorCard2; |
|||
@property (nonatomic, strong) UIView *sectorCard3; |
|||
|
|||
#pragma mark - 股票容器 |
|||
@property (nonatomic, strong) UIView *stockContainer; |
|||
@property (nonatomic, strong) UIView *stockHeaderView; |
|||
@property (nonatomic, strong) UITableView *stockTableView; |
|||
|
|||
#pragma mark - 接口容器 |
|||
@property (nonatomic, strong) UIView *stockContainer1; |
|||
@property (nonatomic, strong) UILabel *stockLabel1; |
|||
@property (nonatomic, strong) UIView *permissionHeaderView; |
|||
@property (nonatomic, strong) UITableView *permissionTableView; |
|||
|
|||
@end |
|||
|
|||
@implementation MLXYViewController |
|||
|
|||
#pragma mark - viewDidLoad |
|||
- (void)viewDidLoad { |
|||
[super viewDidLoad]; |
|||
self.view.backgroundColor = [UIColor blackColor]; |
|||
|
|||
[self setupData]; |
|||
|
|||
[self setupGlobalScrollView]; |
|||
[self setupSubviews]; |
|||
|
|||
[self marketViewClick]; |
|||
|
|||
[self setupConstraints]; |
|||
|
|||
[self fetchPermissionData]; |
|||
} |
|||
|
|||
#pragma mark - 数据 |
|||
- (void)setupData { |
|||
_permissionDataList = [NSMutableArray array]; |
|||
|
|||
_sectorDataConfig = @[ |
|||
@{@"name":@"Health", @"value1":@"1099.683", @"value2":@"27.236",@"value3":@"2.270%"}, |
|||
@{@"name":@"Others", @"value1":@"2083.783", @"value2":@"0.000",@"value3":@"0.000%"}, |
|||
@{@"name":@"HLKLI", @"value1":@"987.895", @"value2":@"-1.854",@"value3":@"-0.230%"} |
|||
]; |
|||
|
|||
UIButtonConfiguration *config = [UIButtonConfiguration plainButtonConfiguration]; |
|||
config.title = @"更多"; |
|||
config.attributedTitle = [[NSAttributedString alloc] initWithString:@"更多" attributes:@{ |
|||
NSFontAttributeName: [UIFont systemFontOfSize:14], |
|||
NSForegroundColorAttributeName: [UIColor lightGrayColor]// 文字颜色 |
|||
}]; |
|||
config.image = [UIImage systemImageNamed:@"chevron.right"]; |
|||
config.imagePlacement = NSDirectionalRectEdgeTrailing; |
|||
config.imagePadding = 1; |
|||
config.baseForegroundColor = [UIColor lightGrayColor];// 按钮文字和模板都会继承这个颜色 |
|||
_buttonConfig = config; |
|||
} |
|||
|
|||
#pragma mark - 发请求 |
|||
|
|||
- (void)fetchPermissionData { |
|||
NSString *urlString = @"https://hwjb.homilychart.com/dev/admin/permission/getPermission"; |
|||
NSURL *url = [NSURL URLWithString:urlString]; |
|||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; |
|||
request.HTTPMethod = @"POST"; |
|||
|
|||
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; |
|||
NSString *token = @"eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIwMTQ0ZDE4MWQ3NDI0ZjljYjk1OTI0Y2RiZWQzZTlmYSIsInN1YiI6IntcImlkXCI6MTAwMDAxMyxcImFkbWluTmFtZVwiOlwi5p2O5oWn55Cz5ryU56S65py6XCIsXCJhY2NvdW50XCI6XCI5MDA0NzY4MVwiLFwicGFzc3dvcmRcIjpcIiQyYSQxMCRzSXVXMFNSMllEZFIyMGxxZnVRRXBlbDdHU2NTVmRqb1lDaXNrSmpTU0suNE5hbmN5NkUyLlwiLFwibWFjaGluZUlkXCI6XCIyMDAzMDQwMSxGQzM2RDYzOEZGM0FcIixcIm1hY2hpbmVJZHNcIjpudWxsLFwiYWRtaW5TdGF0dXNcIjoxLFwibWFya2V0XCI6bnVsbCxcIm1hcmtldHNcIjpcIuaAu-mDqFwiLFwicm9sZUtleVwiOm51bGwsXCJwb3N0aXRvblwiOlwi56CU5Y-RXCIsXCJyZW1hcmtcIjpcIuWRmOW3pVwiLFwiY3JlYXRlVGltZVwiOlwiMjAyNS0wOC0xMCAxNDozNDowOVwiLFwidXBkYXRlVGltZVwiOlwiMjAyNS0xMS0yMSAxNzo1NDo0MVwiLFwicm9sZUlkXCI6MixcInVzZXJuYW1lXCI6XCI5MDA0NzY4MVwifSIsImlzcyI6InNnIiwiaWF0IjoxNzY0OTE1NzQ5LCJleHAiOjE3NjUwMDIxNDl9.5O__C0lpO3CBHMVPOjiUsLD1Cp733aBZCcDmpkfcXSc"; |
|||
[request setValue:token forHTTPHeaderField:@"token"]; |
|||
|
|||
NSDictionary *params = @{ |
|||
@"pageNum": @1, |
|||
@"pageSize": @10, |
|||
@"permission": @{ @"account": @"", @"market": @"", @"postiton": @"" } |
|||
}; |
|||
|
|||
// 参数转json |
|||
NSError *error; |
|||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:params options:0 error:&error]; |
|||
if (!error) {request.HTTPBody = jsonData; |
|||
}else{ |
|||
NSLog(@"参数转json失败:%@",error); |
|||
return; |
|||
} |
|||
|
|||
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { |
|||
if (error) { |
|||
NSLog(@"请求失败: %@", error); |
|||
return; |
|||
} |
|||
if (data) { |
|||
NSError *jsonError; |
|||
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError]; |
|||
if (!jsonError && [json[@"code"] integerValue] == 200) { |
|||
NSArray *list = json[@"data"][@"list"]; |
|||
[self.permissionDataList removeAllObjects]; |
|||
[self.permissionDataList addObjectsFromArray:list]; |
|||
dispatch_async(dispatch_get_main_queue(), ^{ |
|||
[self.permissionTableView reloadData]; |
|||
}); |
|||
} |
|||
} |
|||
}]; |
|||
[task resume]; |
|||
} |
|||
|
|||
#pragma mark - 子视图们 |
|||
- (void)setupGlobalScrollView { |
|||
_globalScrollView = [[UIScrollView alloc] init]; |
|||
_globalScrollView.backgroundColor = [UIColor blackColor]; |
|||
_globalScrollView.showsVerticalScrollIndicator = YES; |
|||
_globalScrollView.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[self.view addSubview:_globalScrollView]; |
|||
|
|||
_scrollContentView = [[UIView alloc] init]; |
|||
_scrollContentView.backgroundColor = [UIColor blackColor]; |
|||
_scrollContentView.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_globalScrollView addSubview:_scrollContentView]; |
|||
} |
|||
|
|||
- (void)setupSubviews { |
|||
_mlxyButton = [UIButton buttonWithType:UIButtonTypeSystem]; |
|||
[_mlxyButton setTitle:@"马来西亚" forState:UIControlStateNormal]; |
|||
[_mlxyButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; |
|||
[_mlxyButton setBackgroundColor:[UIColor blueColor]]; |
|||
_mlxyButton.layer.cornerRadius = 15; |
|||
_mlxyButton.titleLabel.font = [UIFont systemFontOfSize:14]; |
|||
_mlxyButton.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_scrollContentView addSubview:_mlxyButton]; |
|||
|
|||
// 大盘 |
|||
[self setupMarketUI]; |
|||
// 板块 |
|||
[self setupSectorUI]; |
|||
// 股票 |
|||
[self setupStockListUI]; |
|||
// 接口 |
|||
[self setupPermissionUI]; |
|||
} |
|||
|
|||
- (void)setupMarketUI { |
|||
_marketTitleLabel = [self createTitleLabel:@"大盘指数"]; |
|||
[_scrollContentView addSubview:_marketTitleLabel]; |
|||
|
|||
_marketView = [[UIView alloc] init]; |
|||
_marketView.backgroundColor = [UIColor greenColor]; |
|||
_marketView.layer.cornerRadius = 8; |
|||
_marketView.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_scrollContentView addSubview:_marketView]; |
|||
|
|||
_marketLine1 = [self createMarketLabel:@"富时马来西亚KLCI" fontSize:12 isBold:YES]; |
|||
_marketLine2 = [self createMarketLabel:@"1624.500" fontSize:17 isBold:YES]; |
|||
_marketLine3 = [self createMarketLabel:@"12.760 0.792%" fontSize:12 isBold:YES]; |
|||
|
|||
[_marketView addSubview:_marketLine1]; |
|||
[_marketView addSubview:_marketLine2]; |
|||
[_marketView addSubview:_marketLine3]; |
|||
} |
|||
|
|||
- (void)setupSectorUI { |
|||
_sectorTitleLabel = [self createTitleLabel:@"板块"]; |
|||
[_scrollContentView addSubview:_sectorTitleLabel]; |
|||
|
|||
_sectorBtn = [UIButton buttonWithConfiguration:self.buttonConfig primaryAction:nil]; |
|||
_sectorBtn.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_scrollContentView addSubview:_sectorBtn]; |
|||
|
|||
_sectorCard1 = [self createSectorCardWithData:self.sectorDataConfig[0]]; |
|||
_sectorCard2 = [self createSectorCardWithData:self.sectorDataConfig[1]]; |
|||
_sectorCard3 = [self createSectorCardWithData:self.sectorDataConfig[2]]; |
|||
|
|||
[_scrollContentView addSubview:_sectorCard1]; |
|||
[_scrollContentView addSubview:_sectorCard2]; |
|||
[_scrollContentView addSubview:_sectorCard3]; |
|||
} |
|||
|
|||
- (void)setupStockListUI { |
|||
_stockContainer = [[UIView alloc] init]; |
|||
_stockContainer.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_scrollContentView addSubview:_stockContainer]; |
|||
|
|||
UILabel *title = [self createTitleLabel:@"股票"]; |
|||
[_stockContainer addSubview:title]; |
|||
|
|||
UIButton *btn = [UIButton buttonWithConfiguration:self.buttonConfig primaryAction:nil]; |
|||
btn.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_stockContainer addSubview:btn]; |
|||
|
|||
_stockHeaderView = [[UIView alloc] init]; |
|||
_stockHeaderView.backgroundColor = [UIColor blackColor]; |
|||
_stockHeaderView.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_stockContainer addSubview:_stockHeaderView]; |
|||
|
|||
[self setupHeaderLabelsForView:_stockHeaderView titles:@[@"名称", @"最新", @"涨幅"] isWhite:NO]; |
|||
|
|||
_stockTableView = [[UITableView alloc] init]; |
|||
_stockTableView.backgroundColor = [UIColor blackColor]; |
|||
_stockTableView.separatorStyle = UITableViewCellSeparatorStyleNone; |
|||
_stockTableView.delegate = self; |
|||
_stockTableView.dataSource = self; |
|||
_stockTableView.scrollEnabled = NO; |
|||
[_stockTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:stockCell]; |
|||
_stockTableView.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_stockContainer addSubview:_stockTableView]; |
|||
} |
|||
|
|||
- (void)setupPermissionUI { |
|||
_stockContainer1 = [[UIView alloc] init]; |
|||
_stockContainer1.backgroundColor = [UIColor whiteColor]; |
|||
_stockContainer1.layer.cornerRadius = 8; |
|||
_stockContainer1.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_scrollContentView addSubview:_stockContainer1]; |
|||
|
|||
_stockLabel1 = [[UILabel alloc] init]; |
|||
_stockLabel1.text = @"真数据!"; |
|||
_stockLabel1.textColor = [UIColor blackColor]; |
|||
_stockLabel1.font = [UIFont systemFontOfSize:16 weight:UIFontWeightBold]; |
|||
_stockLabel1.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_stockContainer1 addSubview:_stockLabel1]; |
|||
|
|||
_permissionHeaderView = [[UIView alloc] init]; |
|||
_permissionHeaderView.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.0]; |
|||
_permissionHeaderView.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_stockContainer1 addSubview:_permissionHeaderView]; |
|||
|
|||
[self setupHeaderLabelsForView:_permissionHeaderView titles:@[@"Name", @"Account", @"Market", @"Position"] isWhite:YES]; |
|||
|
|||
_permissionTableView = [[UITableView alloc] init]; |
|||
_permissionTableView.backgroundColor = [UIColor whiteColor]; |
|||
_permissionTableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine; |
|||
_permissionTableView.delegate = self; |
|||
_permissionTableView.dataSource = self; |
|||
_permissionTableView.scrollEnabled = NO; |
|||
[_permissionTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:permCell]; |
|||
_permissionTableView.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_stockContainer1 addSubview:_permissionTableView]; |
|||
} |
|||
|
|||
#pragma mark - 小工具 |
|||
- (UILabel *)createTitleLabel:(NSString *)text { |
|||
UILabel *label = [[UILabel alloc] init]; |
|||
label.text = text; |
|||
label.textColor = [UIColor whiteColor]; |
|||
label.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium]; |
|||
label.translatesAutoresizingMaskIntoConstraints = NO; |
|||
return label; |
|||
} |
|||
|
|||
- (UILabel *)createMarketLabel:(NSString *)text fontSize:(CGFloat)size isBold:(BOOL)isBold { |
|||
UILabel *label = [[UILabel alloc] init]; |
|||
label.text = text; |
|||
label.textColor = [UIColor whiteColor]; |
|||
label.font = isBold ? [UIFont boldSystemFontOfSize:size] : [UIFont systemFontOfSize:size]; |
|||
label.textAlignment = NSTextAlignmentCenter; |
|||
label.translatesAutoresizingMaskIntoConstraints = NO; |
|||
return label; |
|||
} |
|||
|
|||
- (void)setupHeaderLabelsForView:(UIView *)view titles:(NSArray *)titles isWhite:(BOOL)isWhite { |
|||
CGFloat multiplier = 1.0 / titles.count; |
|||
for (NSInteger i = 0; i < titles.count; i++) { |
|||
UILabel *label = [[UILabel alloc] init]; |
|||
label.text = titles[i]; |
|||
label.textColor = isWhite ? [UIColor darkGrayColor] : [UIColor lightGrayColor]; |
|||
label.font = isWhite ? [UIFont boldSystemFontOfSize:12] : [UIFont systemFontOfSize:12]; |
|||
label.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[view addSubview:label]; |
|||
|
|||
if (isWhite) { |
|||
// 等分 |
|||
label.textAlignment = NSTextAlignmentCenter; |
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[label.topAnchor constraintEqualToAnchor:view.topAnchor], |
|||
[label.bottomAnchor constraintEqualToAnchor:view.bottomAnchor], |
|||
[label.widthAnchor constraintEqualToAnchor:view.widthAnchor multiplier:multiplier], |
|||
(i == 0) ? [label.leadingAnchor constraintEqualToAnchor:view.leadingAnchor] : [label.leadingAnchor constraintEqualToAnchor:view.subviews[i-1].trailingAnchor] |
|||
]]; |
|||
} else { |
|||
// 股票表头 |
|||
label.textAlignment = (i == 0) ? NSTextAlignmentLeft : NSTextAlignmentRight; |
|||
[label.centerYAnchor constraintEqualToAnchor:view.centerYAnchor].active = YES; |
|||
if (i == 0) { |
|||
[label.leadingAnchor constraintEqualToAnchor:view.leadingAnchor constant:15].active = YES; |
|||
} else if (i == 1) { |
|||
[label.widthAnchor constraintEqualToConstant:60].active = YES; |
|||
[label.trailingAnchor constraintEqualToAnchor:view.trailingAnchor constant:-110].active = YES; |
|||
} else { |
|||
[label.widthAnchor constraintEqualToConstant:80].active = YES; |
|||
[label.trailingAnchor constraintEqualToAnchor:view.trailingAnchor constant:-30].active = YES; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
- (UIView *)createSectorCardWithData:(NSDictionary *)data { |
|||
UIView *card = [[UIView alloc] init]; |
|||
card.backgroundColor = [UIColor darkGrayColor]; |
|||
card.layer.cornerRadius = 8; |
|||
card.translatesAutoresizingMaskIntoConstraints = NO; |
|||
|
|||
UILabel *nameLabel = [self createCardLabel:data[@"name"] color:[UIColor whiteColor] size:14 bold:NO]; |
|||
UILabel *val1Label = [self createCardLabel:data[@"value1"] color:[UIColor greenColor] size:16 bold:YES]; |
|||
UILabel *val2Label = [self createCardLabel:data[@"value2"] color:[UIColor greenColor] size:12 bold:NO]; |
|||
UILabel *val3Label = [self createCardLabel:data[@"value3"] color:[UIColor greenColor] size:12 bold:NO]; |
|||
|
|||
[card addSubview:nameLabel]; |
|||
[card addSubview:val1Label]; |
|||
[card addSubview:val2Label]; |
|||
[card addSubview:val3Label]; |
|||
|
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[nameLabel.topAnchor constraintEqualToAnchor:card.topAnchor constant:7], |
|||
[nameLabel.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:10], |
|||
[val1Label.topAnchor constraintEqualToAnchor:nameLabel.bottomAnchor constant:10], |
|||
[val1Label.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:10], |
|||
[val2Label.topAnchor constraintEqualToAnchor:val1Label.bottomAnchor constant:7], |
|||
[val2Label.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:10], |
|||
[val3Label.topAnchor constraintEqualToAnchor:val2Label.bottomAnchor constant:6], |
|||
[val3Label.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:10] |
|||
]]; |
|||
return card; |
|||
} |
|||
|
|||
- (UILabel *)createCardLabel:(NSString *)text color:(UIColor *)color size:(CGFloat)size bold:(BOOL)bold { |
|||
UILabel *label = [[UILabel alloc] init]; |
|||
label.text = text; |
|||
label.textColor = color; |
|||
label.font = bold ? [UIFont boldSystemFontOfSize:size] : [UIFont systemFontOfSize:size]; |
|||
label.translatesAutoresizingMaskIntoConstraints = NO; |
|||
return label; |
|||
} |
|||
|
|||
#pragma mark - 约束们 |
|||
|
|||
- (void)setupConstraints { |
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[_globalScrollView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], |
|||
[_globalScrollView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor], |
|||
[_globalScrollView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor], |
|||
[_globalScrollView.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor], |
|||
|
|||
[_scrollContentView.leadingAnchor constraintEqualToAnchor:_globalScrollView.leadingAnchor], |
|||
[_scrollContentView.trailingAnchor constraintEqualToAnchor:_globalScrollView.trailingAnchor], |
|||
[_scrollContentView.topAnchor constraintEqualToAnchor:_globalScrollView.topAnchor], |
|||
[_scrollContentView.bottomAnchor constraintEqualToAnchor:_globalScrollView.bottomAnchor], |
|||
[_scrollContentView.widthAnchor constraintEqualToAnchor:self.view.widthAnchor] |
|||
]]; |
|||
|
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[_mlxyButton.topAnchor constraintEqualToAnchor:_scrollContentView.topAnchor constant:15], |
|||
[_mlxyButton.leadingAnchor constraintEqualToAnchor:_scrollContentView.leadingAnchor constant:15], |
|||
[_mlxyButton.widthAnchor constraintEqualToConstant:90], |
|||
[_mlxyButton.heightAnchor constraintEqualToConstant:30] |
|||
]]; |
|||
|
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[_marketTitleLabel.topAnchor constraintEqualToAnchor:_mlxyButton.bottomAnchor constant:10], |
|||
[_marketTitleLabel.leadingAnchor constraintEqualToAnchor:_scrollContentView.leadingAnchor constant:15], |
|||
|
|||
[_marketView.topAnchor constraintEqualToAnchor:_marketTitleLabel.bottomAnchor constant:10], |
|||
[_marketView.leadingAnchor constraintEqualToAnchor:_scrollContentView.leadingAnchor constant:15], |
|||
[_marketView.widthAnchor constraintEqualToAnchor:_scrollContentView.widthAnchor multiplier:0.3], |
|||
[_marketView.heightAnchor constraintEqualToConstant:100], |
|||
|
|||
[_marketLine1.topAnchor constraintEqualToAnchor:_marketView.topAnchor constant:10], |
|||
[_marketLine1.centerXAnchor constraintEqualToAnchor:_marketView.centerXAnchor], |
|||
[_marketLine2.topAnchor constraintEqualToAnchor:_marketLine1.bottomAnchor constant:15], |
|||
[_marketLine2.centerXAnchor constraintEqualToAnchor:_marketView.centerXAnchor], |
|||
[_marketLine3.topAnchor constraintEqualToAnchor:_marketLine2.bottomAnchor constant:12], |
|||
[_marketLine3.centerXAnchor constraintEqualToAnchor:_marketView.centerXAnchor] |
|||
]]; |
|||
|
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[_sectorTitleLabel.topAnchor constraintEqualToAnchor:_marketView.bottomAnchor constant:20], |
|||
[_sectorTitleLabel.leadingAnchor constraintEqualToAnchor:_scrollContentView.leadingAnchor constant:15], |
|||
|
|||
[_sectorBtn.centerYAnchor constraintEqualToAnchor:_sectorTitleLabel.centerYAnchor], |
|||
[_sectorBtn.trailingAnchor constraintEqualToAnchor:_scrollContentView.trailingAnchor constant:-15], |
|||
|
|||
[_sectorCard1.topAnchor constraintEqualToAnchor:_sectorTitleLabel.bottomAnchor constant:10], |
|||
[_sectorCard1.leadingAnchor constraintEqualToAnchor:_scrollContentView.leadingAnchor constant:8], |
|||
[_sectorCard1.widthAnchor constraintEqualToAnchor:_scrollContentView.widthAnchor multiplier:0.3], |
|||
[_sectorCard1.heightAnchor constraintEqualToConstant:100], |
|||
|
|||
[_sectorCard2.topAnchor constraintEqualToAnchor:_sectorCard1.topAnchor], |
|||
[_sectorCard2.leadingAnchor constraintEqualToAnchor:_sectorCard1.trailingAnchor constant:8], |
|||
[_sectorCard2.widthAnchor constraintEqualToAnchor:_sectorCard1.widthAnchor], |
|||
[_sectorCard2.heightAnchor constraintEqualToAnchor:_sectorCard1.heightAnchor], |
|||
|
|||
[_sectorCard3.topAnchor constraintEqualToAnchor:_sectorCard1.topAnchor], |
|||
[_sectorCard3.leadingAnchor constraintEqualToAnchor:_sectorCard2.trailingAnchor constant:8], |
|||
[_sectorCard3.trailingAnchor constraintEqualToAnchor:_scrollContentView.trailingAnchor constant:-16], |
|||
[_sectorCard3.heightAnchor constraintEqualToAnchor:_sectorCard1.heightAnchor] |
|||
]]; |
|||
|
|||
UILabel *stockTitle = _stockContainer.subviews[0]; |
|||
UIButton *stockBtn = _stockContainer.subviews[1]; |
|||
|
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[_stockContainer.topAnchor constraintEqualToAnchor:_sectorCard1.bottomAnchor constant:20], |
|||
[_stockContainer.leadingAnchor constraintEqualToAnchor:_scrollContentView.leadingAnchor], |
|||
[_stockContainer.trailingAnchor constraintEqualToAnchor:_scrollContentView.trailingAnchor], |
|||
[_stockContainer.bottomAnchor constraintEqualToAnchor:_stockTableView.bottomAnchor constant:10], |
|||
|
|||
[stockTitle.topAnchor constraintEqualToAnchor:_stockContainer.topAnchor constant:15], |
|||
[stockTitle.leadingAnchor constraintEqualToAnchor:_stockContainer.leadingAnchor constant:15], |
|||
[stockBtn.centerYAnchor constraintEqualToAnchor:stockTitle.centerYAnchor], |
|||
[stockBtn.trailingAnchor constraintEqualToAnchor:_stockContainer.trailingAnchor constant:-15], |
|||
|
|||
[_stockHeaderView.topAnchor constraintEqualToAnchor:stockTitle.bottomAnchor constant:10], |
|||
[_stockHeaderView.leadingAnchor constraintEqualToAnchor:_stockContainer.leadingAnchor], |
|||
[_stockHeaderView.trailingAnchor constraintEqualToAnchor:_stockContainer.trailingAnchor], |
|||
[_stockHeaderView.heightAnchor constraintEqualToConstant:30], |
|||
|
|||
[_stockTableView.topAnchor constraintEqualToAnchor:_stockHeaderView.bottomAnchor], |
|||
[_stockTableView.leadingAnchor constraintEqualToAnchor:_stockContainer.leadingAnchor], |
|||
[_stockTableView.trailingAnchor constraintEqualToAnchor:_stockContainer.trailingAnchor], |
|||
[_stockTableView.heightAnchor constraintEqualToConstant:240] |
|||
]]; |
|||
|
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[_stockContainer1.topAnchor constraintEqualToAnchor:_stockContainer.bottomAnchor constant:20], |
|||
[_stockContainer1.leadingAnchor constraintEqualToAnchor:_scrollContentView.leadingAnchor constant:15], |
|||
[_stockContainer1.trailingAnchor constraintEqualToAnchor:_scrollContentView.trailingAnchor constant:-15], |
|||
[_stockContainer1.heightAnchor constraintEqualToConstant:290], // Height approximation |
|||
[_stockContainer1.bottomAnchor constraintEqualToAnchor:_scrollContentView.bottomAnchor constant:-20], |
|||
|
|||
[_stockLabel1.topAnchor constraintEqualToAnchor:_stockContainer1.topAnchor constant:10], |
|||
[_stockLabel1.leadingAnchor constraintEqualToAnchor:_stockContainer1.leadingAnchor constant:15], |
|||
|
|||
[_permissionHeaderView.topAnchor constraintEqualToAnchor:_stockLabel1.bottomAnchor constant:10], |
|||
[_permissionHeaderView.leadingAnchor constraintEqualToAnchor:_stockContainer1.leadingAnchor], |
|||
[_permissionHeaderView.trailingAnchor constraintEqualToAnchor:_stockContainer1.trailingAnchor], |
|||
[_permissionHeaderView.heightAnchor constraintEqualToConstant:30], |
|||
|
|||
[_permissionTableView.topAnchor constraintEqualToAnchor:_permissionHeaderView.bottomAnchor], |
|||
[_permissionTableView.leadingAnchor constraintEqualToAnchor:_stockContainer1.leadingAnchor], |
|||
[_permissionTableView.trailingAnchor constraintEqualToAnchor:_stockContainer1.trailingAnchor], |
|||
[_permissionTableView.bottomAnchor constraintEqualToAnchor:_stockContainer1.bottomAnchor constant:-5] |
|||
]]; |
|||
} |
|||
|
|||
#pragma mark - 跳转k线 |
|||
-(void)marketViewClick{ |
|||
_marketView.userInteractionEnabled = YES; |
|||
UITapGestureRecognizer *click = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(marketViewPush)]; |
|||
[_marketView addGestureRecognizer:click]; |
|||
} |
|||
|
|||
-(void)marketViewPush{ |
|||
ChartViewController *chartViewController = [[ChartViewController alloc] init]; |
|||
[self.navigationController pushViewController:chartViewController animated:YES]; |
|||
} |
|||
|
|||
#pragma mark - 哦哦哦哦哦 |
|||
|
|||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { |
|||
if (tableView == _stockTableView) return 4;// 股票表4行 |
|||
if (tableView == _permissionTableView) return MIN(self.permissionDataList.count, 5);// 接口最多5行 |
|||
return 0; |
|||
} |
|||
|
|||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { |
|||
return (tableView == _stockTableView) ? 60 : 44;// 行高 |
|||
} |
|||
|
|||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { |
|||
if (tableView == _stockTableView) { |
|||
return [self stockCellForIndexPath:indexPath]; |
|||
} else { |
|||
return [self permissionCellForIndexPath:indexPath]; |
|||
} |
|||
} |
|||
|
|||
// 股票Cell |
|||
- (UITableViewCell *)stockCellForIndexPath:(NSIndexPath *)indexPath { |
|||
UITableViewCell *cell = [_stockTableView dequeueReusableCellWithIdentifier:stockCell forIndexPath:indexPath]; |
|||
cell.backgroundColor = [UIColor blackColor]; |
|||
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
|||
[cell.contentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; |
|||
|
|||
NSArray *stocks = @[ |
|||
@{@"name":@"PEGASUS HEIGHTS BERHAD", @"code":@"4464", @"price":@"0.010", @"value2":@"100.000%"}, |
|||
@{@"name":@"SMTRACK BERHAD", @"code":@"0169", @"price":@"0.010", @"value2":@"100.000%"}, |
|||
@{@"name":@"TXCD BERHAD - ICPS 2020/2030", @"code":@"7145PA", @"price":@"0.020", @"value2":@"33.333%"}, |
|||
@{@"name":@"DNONCE TECHNOLOGY BHD", @"code":@"7114", @"price":@"0.040", @"value2":@"33.333%"} |
|||
]; |
|||
NSDictionary *data = stocks[indexPath.row]; |
|||
|
|||
UILabel *name = [self createCardLabel:data[@"name"] color:[UIColor whiteColor] size:14 bold:NO]; |
|||
name.lineBreakMode = NSLineBreakByTruncatingTail; |
|||
UILabel *code = [self createCardLabel:data[@"code"] color:[UIColor lightGrayColor] size:12 bold:NO]; |
|||
UILabel *price = [self createCardLabel:data[@"price"] color:[UIColor greenColor] size:14 bold:NO]; |
|||
price.textAlignment = NSTextAlignmentRight; |
|||
UILabel *val2 = [self createCardLabel:data[@"value2"] color:[UIColor greenColor] size:14 bold:NO]; |
|||
val2.textAlignment = NSTextAlignmentRight; |
|||
|
|||
[cell.contentView addSubview:name]; |
|||
[cell.contentView addSubview:code]; |
|||
[cell.contentView addSubview:price]; |
|||
[cell.contentView addSubview:val2]; |
|||
|
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[name.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor constant:15], |
|||
[name.topAnchor constraintEqualToAnchor:cell.contentView.topAnchor constant:10], |
|||
[name.trailingAnchor constraintLessThanOrEqualToAnchor:price.leadingAnchor constant:-10], |
|||
|
|||
[code.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor constant:15], |
|||
[code.topAnchor constraintEqualToAnchor:name.bottomAnchor constant:5], |
|||
|
|||
[price.widthAnchor constraintEqualToConstant:60], |
|||
[price.trailingAnchor constraintEqualToAnchor:val2.leadingAnchor constant:-10], |
|||
[price.centerYAnchor constraintEqualToAnchor:cell.contentView.centerYAnchor], |
|||
|
|||
[val2.widthAnchor constraintEqualToConstant:80], |
|||
[val2.trailingAnchor constraintEqualToAnchor:cell.contentView.trailingAnchor constant:-15], |
|||
[val2.centerYAnchor constraintEqualToAnchor:cell.contentView.centerYAnchor] |
|||
]]; |
|||
return cell; |
|||
} |
|||
|
|||
// 接口Cell |
|||
- (UITableViewCell *)permissionCellForIndexPath:(NSIndexPath *)indexPath { |
|||
UITableViewCell *cell = [_permissionTableView dequeueReusableCellWithIdentifier:permCell forIndexPath:indexPath]; |
|||
cell.backgroundColor = [UIColor whiteColor]; |
|||
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
|||
[cell.contentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; |
|||
|
|||
NSDictionary *item = self.permissionDataList[indexPath.row]; |
|||
NSArray *values = @[item[@"name"] ?: @"-", item[@"account"] ?: @"-", item[@"market"] ?: @"-", item[@"postiton"] ?: @"-"]; |
|||
|
|||
for (NSInteger i = 0; i < values.count; i++) { |
|||
UILabel *label = [self createCardLabel:[NSString stringWithFormat:@"%@", values[i]] color:[UIColor blackColor] size:11 bold:NO]; |
|||
label.textAlignment = NSTextAlignmentCenter; |
|||
label.lineBreakMode = NSLineBreakByTruncatingTail; |
|||
[cell.contentView addSubview:label]; |
|||
|
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[label.topAnchor constraintEqualToAnchor:cell.contentView.topAnchor], |
|||
[label.bottomAnchor constraintEqualToAnchor:cell.contentView.bottomAnchor], |
|||
[label.widthAnchor constraintEqualToAnchor:cell.contentView.widthAnchor multiplier:0.25], |
|||
(i == 0) ? [label.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor] : [label.leadingAnchor constraintEqualToAnchor:cell.contentView.subviews[i-1].trailingAnchor] |
|||
]]; |
|||
} |
|||
return cell; |
|||
} |
|||
|
|||
@end |
|||
@ -0,0 +1,29 @@ |
|||
// |
|||
// StockInfoCardView.h |
|||
// HC |
|||
// |
|||
// Created by huilinLi on 2025/12/1. |
|||
// |
|||
|
|||
#import <UIKit/UIKit.h> |
|||
|
|||
NS_ASSUME_NONNULL_BEGIN |
|||
|
|||
@interface StockInfoCardView : UIView |
|||
|
|||
@property (nonatomic, strong, readonly) UILabel *mainPriceLabel; |
|||
@property (nonatomic, strong, readonly) UILabel *changePriceLabel; |
|||
@property (nonatomic, strong, readonly) UILabel *changePercentLabel; |
|||
|
|||
@property (nonatomic, strong, readonly) UILabel *highValueLabel; |
|||
@property (nonatomic, strong, readonly) UILabel *openValueLabel; |
|||
@property (nonatomic, strong, readonly) UILabel *volumeValueLabel; |
|||
@property (nonatomic, strong, readonly) UILabel *lowValueLabel; |
|||
@property (nonatomic, strong, readonly) UILabel *turnoverRateLabel; // 换 |
|||
@property (nonatomic, strong, readonly) UILabel *amountLabel; // 额 (成交额) |
|||
|
|||
- (void)setupView; |
|||
|
|||
@end |
|||
|
|||
NS_ASSUME_NONNULL_END |
|||
@ -0,0 +1,156 @@ |
|||
// |
|||
// StockInfoCardView.m |
|||
// HC |
|||
// |
|||
// Created by huilinLi on 2025/12/1. |
|||
// |
|||
|
|||
#import "StockInfoCardView.h" |
|||
|
|||
@interface StockInfoCardView () |
|||
// 在实现文件中重新定义为 readwrite 方便内部赋值 |
|||
@property (nonatomic, strong, readwrite) UILabel *mainPriceLabel; |
|||
@property (nonatomic, strong, readwrite) UILabel *changePriceLabel; |
|||
@property (nonatomic, strong, readwrite) UILabel *changePercentLabel; |
|||
@property (nonatomic, strong, readwrite) UILabel *highValueLabel; |
|||
@property (nonatomic, strong, readwrite) UILabel *openValueLabel; |
|||
@property (nonatomic, strong, readwrite) UILabel *volumeValueLabel; |
|||
@property (nonatomic, strong, readwrite) UILabel *lowValueLabel; |
|||
@property (nonatomic, strong, readwrite) UILabel *turnoverRateLabel; |
|||
@property (nonatomic, strong, readwrite) UILabel *amountLabel; |
|||
@end |
|||
|
|||
@implementation StockInfoCardView |
|||
|
|||
- (instancetype)initWithFrame:(CGRect)frame { |
|||
self = [super initWithFrame:frame]; |
|||
if (self) { |
|||
// 通常在 init 之后立即调用 setupView |
|||
[self setupView]; |
|||
} |
|||
return self; |
|||
} |
|||
|
|||
- (void)setupView { |
|||
self.backgroundColor = [UIColor colorWithRed:0x20/255.0 green:0x20/255.0 blue:0x20/255.0 alpha:1]; |
|||
|
|||
// 1. 创建左上角价格和变化标签 |
|||
_mainPriceLabel = [self createLabelWithFontSize:15 textColor:[UIColor redColor]]; |
|||
_changePriceLabel = [self createLabelWithFontSize:8 textColor:[UIColor redColor]]; |
|||
_changePercentLabel = [self createLabelWithFontSize:8 textColor:[UIColor redColor]]; |
|||
|
|||
// 2. 创建右侧信息标签(名称和值) |
|||
|
|||
// 标签名称(保持原有的布局比例) |
|||
UILabel *cardLabelHeight = [self createLabelWithText:@"高" fontSize:15 color:[UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1]]; |
|||
UILabel *cardLabelStart = [self createLabelWithText:@"开" fontSize:15 color:[UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1]]; |
|||
UILabel *cardLabelVolume = [self createLabelWithText:@"量" fontSize:15 color:[UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1]]; |
|||
UILabel *cardLabelLow = [self createLabelWithText:@"低" fontSize:15 color:[UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1]]; |
|||
UILabel *cardLabelChange = [self createLabelWithText:@"换" fontSize:15 color:[UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1]]; |
|||
UILabel *cardLabelQuota = [self createLabelWithText:@"额" fontSize:15 color:[UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1]]; |
|||
|
|||
// 标签值(初始内容与原代码相同) |
|||
_highValueLabel = [self createLabelWithText:@"1617.260" fontSize:12 color:[UIColor redColor]]; |
|||
_openValueLabel = [self createLabelWithText:@"1616.750" fontSize:12 color:[UIColor redColor]]; |
|||
_volumeValueLabel = [self createLabelWithText:@"1.511亿" fontSize:12 color:[UIColor whiteColor]]; |
|||
_lowValueLabel = [self createLabelWithText:@"1608.850" fontSize:12 color:[UIColor whiteColor]]; |
|||
_turnoverRateLabel = [self createLabelWithText:@"--" fontSize:12 color:[UIColor whiteColor]]; |
|||
_amountLabel = [self createLabelWithText:@"--" fontSize:12 color:[UIColor whiteColor]]; |
|||
|
|||
// 将所有子视图添加到 CardView |
|||
NSArray *allSubviews = @[_mainPriceLabel, _changePriceLabel, _changePercentLabel, cardLabelHeight, cardLabelStart, cardLabelVolume, cardLabelLow, cardLabelChange, cardLabelQuota, _highValueLabel, _openValueLabel, _volumeValueLabel, _lowValueLabel, _turnoverRateLabel, _amountLabel]; |
|||
|
|||
for (UIView *view in allSubviews) { |
|||
[self addSubview:view]; |
|||
} |
|||
|
|||
// 3. 布局约束 (使用 self 代替原来的 _cardContainer) |
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
// 左侧主要信息 |
|||
[_mainPriceLabel.topAnchor constraintEqualToAnchor:self.topAnchor constant:25], |
|||
[_mainPriceLabel.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:10], |
|||
|
|||
[_changePriceLabel.topAnchor constraintEqualToAnchor:self.topAnchor constant:50], |
|||
[_changePriceLabel.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant: 8], |
|||
|
|||
[_changePercentLabel.topAnchor constraintEqualToAnchor:self.topAnchor constant:50], |
|||
[_changePercentLabel.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant: 45], |
|||
|
|||
// 相对布局的 Label(高, 开, 量) |
|||
[cardLabelHeight.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:-75], |
|||
|
|||
[cardLabelStart.bottomAnchor constraintEqualToAnchor:cardLabelHeight.bottomAnchor], |
|||
[cardLabelVolume.bottomAnchor constraintEqualToAnchor:cardLabelHeight.bottomAnchor], |
|||
|
|||
// 相对布局的 Label(低, 换, 额) |
|||
[cardLabelLow.topAnchor constraintEqualToAnchor:self.bottomAnchor constant:-50], |
|||
[cardLabelLow.leadingAnchor constraintEqualToAnchor:cardLabelHeight.leadingAnchor], |
|||
|
|||
[cardLabelChange.topAnchor constraintEqualToAnchor:self.bottomAnchor constant:-50], |
|||
[cardLabelChange.leadingAnchor constraintEqualToAnchor:cardLabelStart.leadingAnchor], |
|||
|
|||
[cardLabelQuota.topAnchor constraintEqualToAnchor:self.bottomAnchor constant:-50], |
|||
[cardLabelQuota.leadingAnchor constraintEqualToAnchor:cardLabelVolume.leadingAnchor], |
|||
|
|||
// 相对布局的 Value(高, 开, 量) |
|||
[_highValueLabel.bottomAnchor constraintEqualToAnchor:self.topAnchor constant:45], |
|||
[_highValueLabel.leadingAnchor constraintEqualToAnchor:cardLabelHeight.leadingAnchor], |
|||
|
|||
[_openValueLabel.bottomAnchor constraintEqualToAnchor:_highValueLabel.bottomAnchor], |
|||
[_openValueLabel.leadingAnchor constraintEqualToAnchor:cardLabelStart.leadingAnchor], |
|||
|
|||
[_volumeValueLabel.bottomAnchor constraintEqualToAnchor:_highValueLabel.bottomAnchor], |
|||
[_volumeValueLabel.leadingAnchor constraintEqualToAnchor:cardLabelVolume.leadingAnchor], |
|||
|
|||
// 相对布局的 Value(低, 换, 额) |
|||
[_lowValueLabel.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:-10], |
|||
[_lowValueLabel.leadingAnchor constraintEqualToAnchor:cardLabelLow.leadingAnchor], |
|||
|
|||
[_turnoverRateLabel.bottomAnchor constraintEqualToAnchor:_lowValueLabel.bottomAnchor], |
|||
[_turnoverRateLabel.leadingAnchor constraintEqualToAnchor:cardLabelChange.leadingAnchor], |
|||
|
|||
[_amountLabel.bottomAnchor constraintEqualToAnchor:_lowValueLabel.bottomAnchor], |
|||
[_amountLabel.leadingAnchor constraintEqualToAnchor:cardLabelVolume.leadingAnchor] |
|||
]]; |
|||
|
|||
// 关键:基于父视图宽度比例的约束(使用 self 代替 _cardContainer) |
|||
[NSLayoutConstraint constraintWithItem:cardLabelHeight |
|||
attribute:NSLayoutAttributeLeading |
|||
relatedBy:NSLayoutRelationEqual |
|||
toItem:self |
|||
attribute:NSLayoutAttributeTrailing |
|||
multiplier:0.25 |
|||
constant:0].active = YES; |
|||
[NSLayoutConstraint constraintWithItem:cardLabelStart |
|||
attribute:NSLayoutAttributeLeading |
|||
relatedBy:NSLayoutRelationEqual |
|||
toItem:self |
|||
attribute:NSLayoutAttributeTrailing |
|||
multiplier:0.5 |
|||
constant:0].active = YES; |
|||
[NSLayoutConstraint constraintWithItem:cardLabelVolume |
|||
attribute:NSLayoutAttributeLeading |
|||
relatedBy:NSLayoutRelationEqual |
|||
toItem:self |
|||
attribute:NSLayoutAttributeTrailing |
|||
multiplier:0.75 |
|||
constant:0].active = YES; |
|||
} |
|||
|
|||
// 辅助方法:创建 UILabel |
|||
- (UILabel *)createLabelWithFontSize:(CGFloat)size textColor:(UIColor *)color { |
|||
UILabel *label = [[UILabel alloc] init]; |
|||
label.textColor = color; |
|||
label.font = [UIFont systemFontOfSize:size]; |
|||
label.translatesAutoresizingMaskIntoConstraints = NO; |
|||
return label; |
|||
} |
|||
|
|||
// 辅助方法:创建带初始文本的 UILabel |
|||
- (UILabel *)createLabelWithText:(NSString *)text fontSize:(CGFloat)size color:(UIColor *)color { |
|||
UILabel *label = [self createLabelWithFontSize:size textColor:color]; |
|||
label.text = text; |
|||
return label; |
|||
} |
|||
|
|||
@end |
|||
@ -0,0 +1,5 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
|||
<plist version="1.0"> |
|||
<array/> |
|||
</plist> |
|||
@ -1,592 +0,0 @@ |
|||
// ChartViewController.m |
|||
// HC |
|||
// |
|||
// Created by huilinLi on 2025/11/27. |
|||
// |
|||
|
|||
#import "ChartViewController.h" |
|||
#import "StockKLineModel.h" |
|||
|
|||
@interface ChartViewController () |
|||
|
|||
// 上方卡片容器 |
|||
@property (nonatomic, strong) UIView *cardContainer; |
|||
// k线选择项容器 |
|||
@property (nonatomic, strong) UIView *kSelectContainer; |
|||
// K线图表容器 |
|||
@property (nonatomic, strong) UIView *kLineContainer; |
|||
// K线图表滚动视图 |
|||
@property (nonatomic, strong) UIScrollView *kLineScrollView; |
|||
|
|||
// K线数据 |
|||
@property (nonatomic, strong) NSArray *kLineData; |
|||
// 价格刻度标签 |
|||
@property (nonatomic, strong) NSArray *priceLabels; |
|||
// 日期刻度标签 |
|||
@property (nonatomic, strong) NSArray *dateLabels; |
|||
|
|||
// 默认展示的K线数量 |
|||
@property (nonatomic, assign) NSInteger visibleKLineCount; |
|||
|
|||
@end |
|||
|
|||
@implementation ChartViewController |
|||
|
|||
- (void)viewDidLoad { |
|||
[super viewDidLoad]; |
|||
self.view.backgroundColor = [UIColor blackColor]; |
|||
|
|||
self.visibleKLineCount = 40; |
|||
|
|||
[self generateMockData]; |
|||
[self setupSubviews]; |
|||
[self setupConstraints]; |
|||
|
|||
dispatch_async(dispatch_get_main_queue(), ^{ |
|||
[self drawKLineChart]; |
|||
|
|||
if (self.kLineScrollView.contentSize.width > self.kLineScrollView.bounds.size.width) { |
|||
CGPoint offset = CGPointMake(self.kLineScrollView.contentSize.width - self.kLineScrollView.bounds.size.width, 0); |
|||
[self.kLineScrollView setContentOffset:offset animated:NO]; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
#pragma mark - UI Setup |
|||
-(void) setupSubviews{ |
|||
// 卡片容器 |
|||
_cardContainer = [[UIView alloc] init]; |
|||
_cardContainer.backgroundColor = [UIColor colorWithRed:26.0/255.0 |
|||
green:26.0/255.0 |
|||
blue:2.0/255.0 |
|||
alpha:1.0]; |
|||
_cardContainer.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[self.view addSubview:_cardContainer]; |
|||
|
|||
UILabel *cardLabel1 = [[UILabel alloc] init]; |
|||
cardLabel1.text = @"1617.060"; |
|||
cardLabel1.textColor = [UIColor redColor]; |
|||
cardLabel1.font = [UIFont systemFontOfSize:15]; |
|||
cardLabel1.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_cardContainer addSubview:cardLabel1]; |
|||
|
|||
UILabel *cardLabel2 = [[UILabel alloc] init]; |
|||
cardLabel2.text = @"-7.470"; |
|||
cardLabel2.textColor = [UIColor redColor]; |
|||
cardLabel2.font = [UIFont systemFontOfSize:8]; |
|||
cardLabel2.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_cardContainer addSubview:cardLabel2]; |
|||
|
|||
UILabel *cardLabel3 = [[UILabel alloc] init]; |
|||
cardLabel3.text = @"-0.461%"; |
|||
cardLabel3.textColor = [UIColor redColor]; |
|||
cardLabel3.font = [UIFont systemFontOfSize:8]; |
|||
cardLabel3.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_cardContainer addSubview:cardLabel3]; |
|||
|
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[cardLabel1.topAnchor constraintEqualToAnchor:_cardContainer.topAnchor constant:25], |
|||
[cardLabel1.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:10], |
|||
|
|||
[cardLabel2.topAnchor constraintEqualToAnchor:_cardContainer.topAnchor constant:50], |
|||
[cardLabel2.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant: 8], |
|||
|
|||
[cardLabel3.topAnchor constraintEqualToAnchor:_cardContainer.topAnchor constant:50], |
|||
[cardLabel3.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant: 45] |
|||
]]; |
|||
|
|||
UILabel *cardLabelHeight = [[UILabel alloc] init]; |
|||
cardLabelHeight.text = @"高"; |
|||
cardLabelHeight.textColor = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1]; |
|||
cardLabelHeight.font = [UIFont systemFontOfSize:15]; |
|||
cardLabelHeight.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_cardContainer addSubview:cardLabelHeight]; |
|||
|
|||
UILabel *cardLabelStart = [[UILabel alloc] init]; |
|||
cardLabelStart.text = @"开"; |
|||
cardLabelStart.textColor = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1]; |
|||
cardLabelStart.font = [UIFont systemFontOfSize:15]; |
|||
cardLabelStart.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_cardContainer addSubview:cardLabelStart]; |
|||
|
|||
UILabel *cardLabelVolume = [[UILabel alloc] init]; |
|||
cardLabelVolume.text = @"量"; |
|||
cardLabelVolume.textColor = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1]; |
|||
cardLabelVolume.font = [UIFont systemFontOfSize:15]; |
|||
cardLabelVolume.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_cardContainer addSubview:cardLabelVolume]; |
|||
|
|||
UILabel *cardLabelLow = [[UILabel alloc] init]; |
|||
cardLabelLow.text = @"低"; |
|||
cardLabelLow.textColor = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1]; |
|||
cardLabelLow.font = [UIFont systemFontOfSize:15]; |
|||
cardLabelLow.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_cardContainer addSubview:cardLabelLow]; |
|||
|
|||
UILabel *cardLabelChange = [[UILabel alloc] init]; |
|||
cardLabelChange.text = @"换"; |
|||
cardLabelChange.textColor = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1]; |
|||
cardLabelChange.font = [UIFont systemFontOfSize:15]; |
|||
cardLabelChange.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_cardContainer addSubview:cardLabelChange]; |
|||
|
|||
UILabel *cardLabelQuota = [[UILabel alloc] init]; |
|||
cardLabelQuota.text = @"额"; |
|||
cardLabelQuota.textColor = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1]; |
|||
cardLabelQuota.font = [UIFont systemFontOfSize:15]; |
|||
cardLabelQuota.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_cardContainer addSubview:cardLabelQuota]; |
|||
|
|||
UILabel *cardNumHeight = [[UILabel alloc] init]; |
|||
cardNumHeight.text = @"1617.260"; |
|||
cardNumHeight.textColor = [UIColor redColor]; |
|||
cardNumHeight.font = [UIFont systemFontOfSize:12]; |
|||
cardNumHeight.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_cardContainer addSubview:cardNumHeight]; |
|||
|
|||
UILabel *cardNumStart = [[UILabel alloc] init]; |
|||
cardNumStart.text = @"1616.750"; |
|||
cardNumStart.textColor = [UIColor redColor]; |
|||
cardNumStart.font = [UIFont systemFontOfSize:12]; |
|||
cardNumStart.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_cardContainer addSubview:cardNumStart]; |
|||
|
|||
UILabel *cardNumVolume = [[UILabel alloc] init]; |
|||
cardNumVolume.text = @"1.511亿"; |
|||
cardNumVolume.textColor = [UIColor whiteColor]; |
|||
cardNumVolume.font = [UIFont systemFontOfSize:12]; |
|||
cardNumVolume.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_cardContainer addSubview:cardNumVolume]; |
|||
|
|||
UILabel *cardNumLow = [[UILabel alloc] init]; |
|||
cardNumLow.text = @"1608.850"; |
|||
cardNumLow.textColor = [UIColor whiteColor]; |
|||
cardNumLow.font = [UIFont systemFontOfSize:12]; |
|||
cardNumLow.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_cardContainer addSubview:cardNumLow]; |
|||
|
|||
UILabel *cardNumChange = [[UILabel alloc] init]; |
|||
cardNumChange.text = @"--"; |
|||
cardNumChange.textColor = [UIColor whiteColor]; |
|||
cardNumChange.font = [UIFont systemFontOfSize:12]; |
|||
cardNumChange.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_cardContainer addSubview:cardNumChange]; |
|||
|
|||
UILabel *cardNumQuota = [[UILabel alloc] init]; |
|||
cardNumQuota.text = @"--"; |
|||
cardNumQuota.textColor = [UIColor whiteColor]; |
|||
cardNumQuota.font = [UIFont systemFontOfSize:12]; |
|||
cardNumQuota.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_cardContainer addSubview:cardNumQuota]; |
|||
|
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[cardLabelHeight.bottomAnchor constraintEqualToAnchor:_cardContainer.bottomAnchor constant:-75], |
|||
|
|||
[cardLabelStart.bottomAnchor constraintEqualToAnchor:cardLabelHeight.bottomAnchor], |
|||
|
|||
[cardLabelVolume.bottomAnchor constraintEqualToAnchor:cardLabelHeight.bottomAnchor], |
|||
|
|||
[cardLabelLow.topAnchor constraintEqualToAnchor:_cardContainer.bottomAnchor constant:-50], |
|||
[cardLabelLow.leadingAnchor constraintEqualToAnchor:cardLabelHeight.leadingAnchor], |
|||
|
|||
[cardLabelChange.topAnchor constraintEqualToAnchor:_cardContainer.bottomAnchor constant:-50], |
|||
[cardLabelChange.leadingAnchor constraintEqualToAnchor:cardLabelStart.leadingAnchor], |
|||
|
|||
[cardLabelQuota.topAnchor constraintEqualToAnchor:_cardContainer.bottomAnchor constant:-50], |
|||
[cardLabelQuota.leadingAnchor constraintEqualToAnchor:cardLabelVolume.leadingAnchor], |
|||
|
|||
[cardNumHeight.bottomAnchor constraintEqualToAnchor:_cardContainer.topAnchor constant:45], |
|||
[cardNumHeight.leadingAnchor constraintEqualToAnchor:cardLabelHeight.leadingAnchor], |
|||
|
|||
[cardNumStart.bottomAnchor constraintEqualToAnchor:cardNumHeight.bottomAnchor], |
|||
[cardNumStart.leadingAnchor constraintEqualToAnchor:cardLabelStart.leadingAnchor], |
|||
|
|||
[cardNumVolume.bottomAnchor constraintEqualToAnchor:cardNumHeight.bottomAnchor], |
|||
[cardNumVolume.leadingAnchor constraintEqualToAnchor:cardLabelVolume.leadingAnchor], |
|||
|
|||
[cardNumLow.bottomAnchor constraintEqualToAnchor:_cardContainer.bottomAnchor constant:-10], |
|||
[cardNumLow.leadingAnchor constraintEqualToAnchor:cardLabelLow.leadingAnchor], |
|||
|
|||
[cardNumChange.bottomAnchor constraintEqualToAnchor:cardNumLow.bottomAnchor], |
|||
[cardNumChange.leadingAnchor constraintEqualToAnchor:cardLabelChange.leadingAnchor], |
|||
|
|||
[cardNumQuota.bottomAnchor constraintEqualToAnchor:cardNumLow.bottomAnchor], |
|||
[cardNumQuota.leadingAnchor constraintEqualToAnchor:cardLabelVolume.leadingAnchor] |
|||
]]; |
|||
|
|||
[NSLayoutConstraint constraintWithItem:cardLabelHeight |
|||
attribute:NSLayoutAttributeLeading |
|||
relatedBy:NSLayoutRelationEqual |
|||
toItem:_cardContainer |
|||
attribute:NSLayoutAttributeTrailing |
|||
multiplier:0.25 |
|||
constant:0].active = YES; |
|||
[NSLayoutConstraint constraintWithItem:cardLabelStart |
|||
attribute:NSLayoutAttributeLeading |
|||
relatedBy:NSLayoutRelationEqual |
|||
toItem:_cardContainer |
|||
attribute:NSLayoutAttributeTrailing |
|||
multiplier:0.5 |
|||
constant:0].active = YES; |
|||
[NSLayoutConstraint constraintWithItem:cardLabelVolume |
|||
attribute:NSLayoutAttributeLeading |
|||
relatedBy:NSLayoutRelationEqual |
|||
toItem:_cardContainer |
|||
attribute:NSLayoutAttributeTrailing |
|||
multiplier:0.75 |
|||
constant:0].active = YES; |
|||
|
|||
// K线选择项容器 |
|||
_kSelectContainer = [[UIView alloc] init]; |
|||
_kSelectContainer.backgroundColor = [UIColor colorWithRed:26.0/255.0 |
|||
green:26.0/255.0 |
|||
blue:2.0/255.0 |
|||
alpha:1.0]; |
|||
_kSelectContainer.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[self.view addSubview:_kSelectContainer]; |
|||
[self addKSelectOptions]; |
|||
|
|||
// K线图表容器 |
|||
self.kLineContainer = [[UIView alloc] init]; |
|||
self.kLineContainer.backgroundColor = [UIColor colorWithRed:26.0/255.0 green:26.0/255.0 blue:26.0/255.0 alpha:1.0]; |
|||
self.kLineContainer.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[self.view addSubview:self.kLineContainer]; |
|||
|
|||
// K线图表滚动视图 |
|||
self.kLineScrollView = [[UIScrollView alloc] init]; |
|||
self.kLineScrollView.backgroundColor = [UIColor clearColor]; |
|||
self.kLineScrollView.showsHorizontalScrollIndicator = NO; |
|||
self.kLineScrollView.userInteractionEnabled = YES; |
|||
self.kLineScrollView.translatesAutoresizingMaskIntoConstraints = NO; |
|||
|
|||
[self.kLineContainer addSubview:self.kLineScrollView]; |
|||
|
|||
CGFloat priceLabelAreaWidth = 50.0; |
|||
|
|||
// 滚动视图的约束 |
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[self.kLineScrollView.topAnchor constraintEqualToAnchor:self.kLineContainer.topAnchor], |
|||
[self.kLineScrollView.bottomAnchor constraintEqualToAnchor:self.kLineContainer.bottomAnchor], |
|||
[self.kLineScrollView.leadingAnchor constraintEqualToAnchor:self.kLineContainer.leadingAnchor constant:priceLabelAreaWidth], |
|||
[self.kLineScrollView.trailingAnchor constraintEqualToAnchor:self.kLineContainer.trailingAnchor], |
|||
[self.kLineScrollView.heightAnchor constraintEqualToAnchor:self.kLineContainer.heightAnchor] |
|||
]]; |
|||
|
|||
// 边框 |
|||
UIView *borderView = [[UIView alloc] init]; |
|||
borderView.backgroundColor = [UIColor clearColor]; |
|||
borderView.layer.borderWidth = 1; |
|||
borderView.layer.borderColor = [[UIColor colorWithRed:0.4 green:0.4 blue:0.4 alpha:1.0] CGColor]; |
|||
borderView.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[self.kLineContainer addSubview:borderView]; |
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[borderView.leadingAnchor constraintEqualToAnchor:self.kLineContainer.leadingAnchor], |
|||
[borderView.trailingAnchor constraintEqualToAnchor:self.kLineContainer.trailingAnchor], |
|||
[borderView.topAnchor constraintEqualToAnchor:self.kLineContainer.topAnchor], |
|||
[borderView.bottomAnchor constraintEqualToAnchor:self.kLineContainer.bottomAnchor] |
|||
]]; |
|||
|
|||
// 动态计算模型数据的价格区间 |
|||
CGFloat maxPrice = 0; |
|||
CGFloat minPrice = CGFLOAT_MAX; |
|||
for (id item in self.kLineData) { |
|||
if ([item isKindOfClass:[StockKLineModel class]]) { |
|||
StockKLineModel *model = (StockKLineModel *)item; |
|||
maxPrice = MAX(maxPrice, model.high); |
|||
minPrice = MIN(minPrice, model.low); |
|||
} else if ([item isKindOfClass:[NSDictionary class]]) { |
|||
NSDictionary *dict = (NSDictionary *)item; |
|||
maxPrice = MAX(maxPrice, [dict[@"high"] floatValue]); |
|||
minPrice = MIN(minPrice, [dict[@"low"] floatValue]); |
|||
} |
|||
} |
|||
maxPrice = maxPrice + (maxPrice - minPrice) * 0.1; |
|||
minPrice = minPrice - (maxPrice - minPrice) * 0.1; |
|||
|
|||
// 价格刻度 |
|||
NSMutableArray *priceLabelArr = [NSMutableArray array]; |
|||
for (NSInteger i = 0; i < 5; i++) { |
|||
CGFloat price = maxPrice - (maxPrice - minPrice) / 4 * i; |
|||
UILabel *label = [[UILabel alloc] init]; |
|||
label.text = [NSString stringWithFormat:@"%.1f", price]; |
|||
label.textColor = [UIColor lightGrayColor]; |
|||
label.font = [UIFont systemFontOfSize:10]; |
|||
label.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[self.kLineContainer addSubview:label]; |
|||
|
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[label.leadingAnchor constraintEqualToAnchor:self.kLineContainer.leadingAnchor constant:5], |
|||
[label.centerYAnchor constraintEqualToAnchor:self.kLineContainer.centerYAnchor constant:-80 + 40 * i] |
|||
]]; |
|||
[priceLabelArr addObject:label]; |
|||
} |
|||
self.priceLabels = priceLabelArr; |
|||
|
|||
self.dateLabels = @[]; |
|||
} |
|||
|
|||
- (void)addKSelectOptions { |
|||
NSArray *titles = @[@"日k", @"周k", @"月k", @"更多", @"设置"]; |
|||
|
|||
UIStackView *stackView = [[UIStackView alloc] init]; |
|||
stackView.axis = UILayoutConstraintAxisHorizontal; |
|||
stackView.distribution = UIStackViewDistributionFillEqually; |
|||
stackView.alignment = UIStackViewAlignmentCenter; |
|||
stackView.spacing = 5.0; |
|||
stackView.translatesAutoresizingMaskIntoConstraints = NO; |
|||
|
|||
for (NSString *title in titles) { |
|||
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem]; |
|||
[button setTitle:title forState:UIControlStateNormal]; |
|||
button.titleLabel.font = [UIFont systemFontOfSize:14]; |
|||
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; |
|||
|
|||
if ([title isEqualToString:@"日k"]) { |
|||
[button setTitleColor:[UIColor yellowColor] forState:UIControlStateNormal]; |
|||
button.layer.borderColor = [[UIColor yellowColor] CGColor]; |
|||
button.layer.borderWidth = 1.0; |
|||
} else { |
|||
[button setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal]; |
|||
button.layer.borderWidth = 0; |
|||
} |
|||
|
|||
button.layer.cornerRadius = 3.0; |
|||
button.clipsToBounds = YES; |
|||
|
|||
[stackView addArrangedSubview:button]; |
|||
} |
|||
|
|||
[_kSelectContainer addSubview:stackView]; |
|||
|
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[stackView.leadingAnchor constraintEqualToAnchor:_kSelectContainer.leadingAnchor constant:10], |
|||
[stackView.trailingAnchor constraintEqualToAnchor:_kSelectContainer.trailingAnchor constant:-10], |
|||
[stackView.topAnchor constraintEqualToAnchor:_kSelectContainer.topAnchor constant:5], |
|||
[stackView.bottomAnchor constraintEqualToAnchor:_kSelectContainer.bottomAnchor constant:-5] |
|||
]]; |
|||
} |
|||
|
|||
#pragma mark - Constraints |
|||
- (void)setupConstraints{ |
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[_cardContainer.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor], |
|||
[_cardContainer.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], |
|||
[_cardContainer.widthAnchor constraintEqualToAnchor:self.view.widthAnchor], |
|||
[_cardContainer.heightAnchor constraintEqualToConstant:100], |
|||
|
|||
[_kSelectContainer.topAnchor constraintEqualToAnchor:_cardContainer.bottomAnchor constant:5], |
|||
[_kSelectContainer.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], |
|||
[_kSelectContainer.widthAnchor constraintEqualToAnchor:self.view.widthAnchor], |
|||
[_kSelectContainer.heightAnchor constraintEqualToConstant:40], |
|||
|
|||
[self.kLineContainer.topAnchor constraintEqualToAnchor:self.kSelectContainer.bottomAnchor constant:5], |
|||
[self.kLineContainer.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], |
|||
[self.kLineContainer.widthAnchor constraintEqualToAnchor:self.view.widthAnchor], |
|||
[self.kLineContainer.heightAnchor constraintEqualToConstant:200] |
|||
]]; |
|||
} |
|||
|
|||
#pragma mark - KLine Drawing |
|||
- (void)drawKLineChart { |
|||
// 移除旧的 K 线 |
|||
for (UIView *subview in self.kLineScrollView.subviews) { |
|||
if (![subview isKindOfClass:[UILabel class]]) { |
|||
[subview removeFromSuperview]; |
|||
} |
|||
} |
|||
|
|||
CGFloat chartHeight = self.kLineContainer.bounds.size.height; |
|||
if (chartHeight == 0) { |
|||
chartHeight = 200; |
|||
} |
|||
|
|||
// K线宽度计算 |
|||
CGFloat kLineUnitWidth = 5.0; |
|||
CGFloat kLineRatio = 0.8; |
|||
CGFloat kLineWidth = kLineUnitWidth * kLineRatio; |
|||
CGFloat space = kLineUnitWidth * (1.0 - kLineRatio); |
|||
CGFloat totalChartWidth = kLineUnitWidth * self.kLineData.count; |
|||
|
|||
// 设置contentSize |
|||
self.kLineScrollView.contentSize = CGSizeMake(totalChartWidth, chartHeight); |
|||
|
|||
// 动态计算价格区间 |
|||
CGFloat maxPrice = 0; |
|||
CGFloat minPrice = CGFLOAT_MAX; |
|||
for (id item in self.kLineData) { |
|||
if ([item isKindOfClass:[StockKLineModel class]]) { |
|||
StockKLineModel *model = (StockKLineModel *)item; |
|||
maxPrice = MAX(maxPrice, model.high); |
|||
minPrice = MIN(minPrice, model.low); |
|||
} else if ([item isKindOfClass:[NSDictionary class]]) { |
|||
NSDictionary *dict = (NSDictionary *)item; |
|||
maxPrice = MAX(maxPrice, [dict[@"high"] floatValue]); |
|||
minPrice = MIN(minPrice, [dict[@"low"] floatValue]); |
|||
} |
|||
} |
|||
maxPrice = maxPrice + (maxPrice - minPrice) * 0.1; |
|||
minPrice = minPrice - (maxPrice - minPrice) * 0.1; |
|||
CGFloat priceRange = maxPrice - minPrice; |
|||
|
|||
// 绘制K线 |
|||
for (NSInteger i = 0; i < self.kLineData.count; i++) { |
|||
CGFloat open = 0, high = 0, low = 0, close = 0; |
|||
|
|||
if ([self.kLineData[i] isKindOfClass:[StockKLineModel class]]) { |
|||
StockKLineModel *model = (StockKLineModel *)self.kLineData[i]; |
|||
open = model.open; |
|||
high = model.high; |
|||
low = model.low; |
|||
close = model.close; |
|||
} else if ([self.kLineData[i] isKindOfClass:[NSDictionary class]]) { |
|||
NSDictionary *dict = (NSDictionary *)self.kLineData[i]; |
|||
open = [dict[@"open"] floatValue]; |
|||
high = [dict[@"high"] floatValue]; |
|||
low = [dict[@"low"] floatValue]; |
|||
close = [dict[@"close"] floatValue]; |
|||
} |
|||
|
|||
// 坐标转换 |
|||
CGFloat yOpen = chartHeight * (maxPrice - open) / priceRange; |
|||
CGFloat yHigh = chartHeight * (maxPrice - high) / priceRange; |
|||
CGFloat yLow = chartHeight * (maxPrice - low) / priceRange; |
|||
CGFloat yClose = chartHeight * (maxPrice - close) / priceRange; |
|||
|
|||
// 计算X坐标 |
|||
CGFloat xPosition = space/2 + kLineUnitWidth * i; |
|||
|
|||
// 绘制K线实体 |
|||
UIView *bodyView = [[UIView alloc] init]; |
|||
bodyView.backgroundColor = (close >= open) ? [UIColor greenColor] : [UIColor redColor]; |
|||
bodyView.frame = CGRectMake( |
|||
xPosition, |
|||
MIN(yOpen, yClose), |
|||
kLineWidth, |
|||
MAX(1.0, fabs(yClose - yOpen)) |
|||
); |
|||
[self.kLineScrollView addSubview:bodyView]; |
|||
|
|||
// 绘制影线 |
|||
UIView *shadowView = [[UIView alloc] init]; |
|||
shadowView.backgroundColor = bodyView.backgroundColor; |
|||
shadowView.frame = CGRectMake( |
|||
bodyView.center.x - 0.5, |
|||
yHigh, |
|||
1, |
|||
yLow - yHigh |
|||
); |
|||
[self.kLineScrollView addSubview:shadowView]; |
|||
} |
|||
|
|||
// 日期刻度 |
|||
for (UILabel *label in self.dateLabels) { |
|||
[label removeFromSuperview]; |
|||
} |
|||
|
|||
NSMutableArray *dateLabelArr = [NSMutableArray array]; |
|||
NSInteger dateInterval = 10; |
|||
|
|||
for (NSInteger i = 0; i < self.kLineData.count; i += dateInterval) { |
|||
NSString *dateStr = @""; |
|||
if ([self.kLineData[i] isKindOfClass:[StockKLineModel class]]) { |
|||
StockKLineModel *model = (StockKLineModel *)self.kLineData[i]; |
|||
dateStr = model.date; |
|||
} else if ([self.kLineData[i] isKindOfClass:[NSDictionary class]]) { |
|||
NSDictionary *dict = (NSDictionary *)self.kLineData[i]; |
|||
dateStr = dict[@"date"]; |
|||
} |
|||
UILabel *label = [[UILabel alloc] init]; |
|||
label.text = dateStr; |
|||
label.textColor = [UIColor lightGrayColor]; |
|||
label.font = [UIFont systemFontOfSize:10]; |
|||
label.translatesAutoresizingMaskIntoConstraints = YES; |
|||
|
|||
CGFloat xCenter = kLineUnitWidth * (i + 0.5); |
|||
[label sizeToFit]; |
|||
|
|||
label.frame = CGRectMake( |
|||
xCenter - label.bounds.size.width / 2, |
|||
chartHeight - 15, |
|||
label.bounds.size.width, |
|||
label.bounds.size.height |
|||
); |
|||
|
|||
[self.kLineScrollView addSubview:label]; |
|||
[dateLabelArr addObject:label]; |
|||
} |
|||
self.dateLabels = dateLabelArr; |
|||
|
|||
NSLog(@"ScrollView Bounds Width: %f", self.kLineScrollView.bounds.size.width); |
|||
NSLog(@"Content Size Width: %f", self.kLineScrollView.contentSize.width); |
|||
|
|||
if (self.kLineScrollView.contentSize.width <= self.kLineScrollView.bounds.size.width) { |
|||
NSLog(@"⚠️ 警告:ContentSize 不够大,无法滑动。"); |
|||
} |
|||
} |
|||
|
|||
#pragma mark - Mock Data |
|||
- (void) setupData{ |
|||
self.kLineData = @[ |
|||
@{@"date":@"2025/11/18", @"open":@1620.0, @"high":@1643.0, @"low":@1605.0, @"close":@1630.0, @"volume":@2.5}, |
|||
@{@"date":@"2025/11/19", @"open":@1630.0, @"high":@1635.0, @"low":@1615.0, @"close":@1620.0, @"volume":@2.2}, |
|||
@{@"date":@"2025/11/20", @"open":@1620.0, @"high":@1628.0, @"low":@1608.0, @"close":@1615.0, @"volume":@1.8}, |
|||
@{@"date":@"2025/11/21", @"open":@1615.0, @"high":@1625.0, @"low":@1610.0, @"close":@1622.0, @"volume":@2.0}, |
|||
@{@"date":@"2025/11/22", @"open":@1622.0, @"high":@1630.0, @"low":@1618.0, @"close":@1625.0, @"volume":@2.3}, |
|||
@{@"date":@"2025/11/25", @"open":@1625.0, @"high":@1632.0, @"low":@1620.0, @"close":@1628.0, @"volume":@1.9}, |
|||
@{@"date":@"2025/11/26", @"open":@1628.0, @"high":@1635.0, @"low":@1622.0, @"close":@1630.0, @"volume":@2.1}, |
|||
@{@"date":@"2025/11/27", @"open":@1630.0, @"high":@1638.0, @"low":@1625.0, @"close":@1628.0, @"volume":@2.4}, |
|||
@{@"date":@"2025/11/28", @"open":@1628.0, @"high":@1632.0, @"low":@1609.0, @"close":@1610.0, @"volume":@2.6}, |
|||
@{@"date":@"2025/11/29", @"open":@1610.0, @"high":@1620.0, @"low":@1608.0, @"close":@1615.0, @"volume":@1.7} |
|||
]; |
|||
} |
|||
|
|||
- (void)generateMockData { |
|||
NSMutableArray *arr = [NSMutableArray array]; |
|||
CGFloat lastClose = 100.0; |
|||
NSLog(@"生成模拟数据中。。。"); |
|||
|
|||
// 生成200条数据(减少数量,避免contentSize过大) |
|||
for (int i = 0; i < 200; i++) { |
|||
StockKLineModel *model = [[StockKLineModel alloc] init]; |
|||
|
|||
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:-(200 - i) * 24 * 3600]; |
|||
NSDateFormatter *fmt = [[NSDateFormatter alloc] init]; |
|||
[fmt setDateFormat:@"MM-dd"]; |
|||
model.date = [fmt stringFromDate:date]; |
|||
|
|||
// 价格生成逻辑 |
|||
CGFloat volatility = lastClose * 0.02; |
|||
CGFloat randomChange = ((arc4random() % 100) / 100.0 - 0.5) * 2 * volatility; |
|||
|
|||
model.open = lastClose + ((arc4random() % 100) / 100.0 - 0.5) * volatility * 0.5; |
|||
model.close = model.open + randomChange; |
|||
|
|||
CGFloat maxOC = MAX(model.open, model.close); |
|||
CGFloat minOC = MIN(model.open, model.close); |
|||
|
|||
model.high = maxOC + (arc4random() % 100) / 100.0 * 1.0; |
|||
model.low = minOC - (arc4random() % 100) / 100.0 * 1.0; |
|||
|
|||
// 成交量生成逻辑 |
|||
CGFloat baseVolume = 1000000.0; |
|||
CGFloat randomVolFactor = (arc4random() % 200) / 100.0 + 0.5; |
|||
|
|||
if (fabs(model.close - model.open) / model.open > 0.03) { |
|||
randomVolFactor *= 1.5; |
|||
} |
|||
|
|||
model.volume = baseVolume * randomVolFactor; |
|||
|
|||
if (model.low < 0) model.low = 0.01; |
|||
|
|||
[arr addObject:model]; |
|||
|
|||
lastClose = model.close; |
|||
} |
|||
|
|||
NSLog(@"生成模拟数据完毕,共%ld条", arr.count); |
|||
|
|||
self.kLineData = arr; |
|||
} |
|||
|
|||
@end |
|||
@ -1,442 +0,0 @@ |
|||
// |
|||
// MLXYViewController.m |
|||
// HC |
|||
// |
|||
// Created by huilinLi on 2025/11/26. |
|||
// |
|||
#import "MLXYViewController.h" |
|||
#import "ChartViewController.h" |
|||
|
|||
@interface MLXYViewController () <UITableViewDataSource, UITableViewDelegate> |
|||
@property (nonatomic, strong) UIButton *mlxyButton; |
|||
// 大盘指数 |
|||
@property (nonatomic, strong) UILabel *marketTitleLabel; |
|||
@property (nonatomic, strong) UIView *marketView; |
|||
@property (nonatomic, strong) UILabel *marketLine1; |
|||
@property (nonatomic, strong) UILabel *marketLine2; |
|||
@property (nonatomic, strong) UILabel *marketLine3; |
|||
// 板块 |
|||
@property (nonatomic, strong) UILabel *sectorTitleLabel; |
|||
@property (nonatomic, strong) UIButton *sectorBtn; |
|||
// 三个板块卡片(分别配置) |
|||
@property (nonatomic, strong) UIView *sectorCard1; |
|||
@property (nonatomic, strong) UIView *sectorCard2; |
|||
@property (nonatomic, strong) UIView *sectorCard3; |
|||
// 股票 |
|||
@property (nonatomic, strong) UIView *stockContainer; |
|||
@property (nonatomic, strong) UITableView *stockTableView; |
|||
// 股票表头 |
|||
@property (nonatomic, strong) UIView *stockHeaderView; |
|||
// 按钮配置 |
|||
@property (nonatomic, strong) UIButtonConfiguration *buttonConfig; |
|||
// 板块数据配置 |
|||
@property (nonatomic, strong) NSArray *sectorDataConfig; |
|||
@end |
|||
|
|||
@implementation MLXYViewController |
|||
|
|||
- (void)viewDidLoad { |
|||
[super viewDidLoad]; |
|||
self.view.backgroundColor = [UIColor blackColor]; |
|||
|
|||
[self setupButtonConfig]; |
|||
[self setupSectorDataConfig]; |
|||
|
|||
[self setupSubviews]; |
|||
[self setupConstraints]; |
|||
|
|||
[self marketViewClick]; |
|||
} |
|||
|
|||
-(void) marketViewClick{ |
|||
_marketView.userInteractionEnabled = YES; |
|||
UITapGestureRecognizer *click = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(marketViewPush)]; |
|||
[_marketView addGestureRecognizer:click]; |
|||
} |
|||
|
|||
-(void) marketViewPush{ |
|||
ChartViewController *chartViewController = [[ChartViewController alloc] init]; |
|||
[self.navigationController pushViewController:chartViewController animated:YES]; |
|||
} |
|||
|
|||
- (void)setupButtonConfig { |
|||
UIButtonConfiguration *config = [UIButtonConfiguration plainButtonConfiguration]; |
|||
config.title = @"更多"; |
|||
config.attributedTitle = [[NSAttributedString alloc] initWithString:@"更多" attributes:@{ |
|||
NSFontAttributeName: [UIFont systemFontOfSize:14], |
|||
NSForegroundColorAttributeName: [UIColor lightGrayColor] |
|||
}]; |
|||
|
|||
UIImage *image = [[UIImage systemImageNamed:@"chevron.right"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; |
|||
config.image = image; |
|||
config.imagePlacement = NSDirectionalRectEdgeTrailing; // 图片在文字右侧 |
|||
config.imagePadding = 1; |
|||
|
|||
config.baseForegroundColor = [UIColor lightGrayColor]; |
|||
|
|||
_buttonConfig = config; |
|||
} |
|||
|
|||
- (void)setupSectorDataConfig { |
|||
_sectorDataConfig = @[ |
|||
@{@"name":@"Health", @"value1":@"1099.683", @"value2":@"27.236",@"value3":@"2.270%"}, |
|||
@{@"name":@"Others", @"value1":@"2083.783", @"value2":@"0.000",@"value3":@"0.000%"}, |
|||
@{@"name":@"HLKLI", @"value1":@"987.895", @"value2":@"-1.854",@"value3":@"-0.230%"} |
|||
]; |
|||
} |
|||
|
|||
- (void)setupSubviews { |
|||
// 马来西亚按钮 |
|||
_mlxyButton = [UIButton buttonWithType:UIButtonTypeSystem]; |
|||
[_mlxyButton setTitle:@"马来西亚" forState:UIControlStateNormal]; |
|||
[_mlxyButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; |
|||
[_mlxyButton setBackgroundColor:[UIColor blueColor]]; |
|||
_mlxyButton.layer.cornerRadius = 15; |
|||
_mlxyButton.titleLabel.font = [UIFont systemFontOfSize:14]; |
|||
_mlxyButton.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[self.view addSubview:_mlxyButton]; |
|||
|
|||
// 大盘指数 |
|||
_marketTitleLabel = [[UILabel alloc] init]; |
|||
_marketTitleLabel.text = @"大盘指数"; |
|||
_marketTitleLabel.textColor = [UIColor whiteColor]; |
|||
_marketTitleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium]; |
|||
_marketTitleLabel.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[self.view addSubview:_marketTitleLabel]; |
|||
|
|||
// 绿色卡片 |
|||
_marketView = [[UIView alloc] init]; |
|||
_marketView.backgroundColor = [UIColor greenColor]; |
|||
_marketView.layer.cornerRadius = 8; |
|||
_marketView.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[self.view addSubview:_marketView]; |
|||
|
|||
_marketLine1 = [[UILabel alloc] init]; |
|||
_marketLine1.text = @"富时马来西亚KLCI"; |
|||
_marketLine1.textColor = [UIColor whiteColor]; |
|||
_marketLine1.font = [UIFont fontWithName:@"Helvetica-Bold" size:12]; |
|||
_marketLine1.textAlignment = NSTextAlignmentCenter; |
|||
_marketLine1.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_marketView addSubview:_marketLine1]; |
|||
|
|||
_marketLine2 = [[UILabel alloc] init]; |
|||
_marketLine2.text = @"1624.500"; |
|||
_marketLine2.textColor = [UIColor whiteColor]; |
|||
_marketLine2.font = [UIFont boldSystemFontOfSize:17]; |
|||
_marketLine2.textAlignment = NSTextAlignmentCenter; |
|||
_marketLine2.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_marketView addSubview:_marketLine2]; |
|||
|
|||
_marketLine3 = [[UILabel alloc] init]; |
|||
_marketLine3.text = @"12.760 0.792%"; |
|||
_marketLine3.textColor = [UIColor whiteColor]; |
|||
_marketLine3.font = [UIFont boldSystemFontOfSize:12]; |
|||
_marketLine3.textAlignment = NSTextAlignmentCenter; |
|||
_marketLine3.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_marketView addSubview:_marketLine3]; |
|||
|
|||
// 板块 |
|||
_sectorTitleLabel = [[UILabel alloc] init]; |
|||
_sectorTitleLabel.text = @"板块"; |
|||
_sectorTitleLabel.textColor = [UIColor whiteColor]; |
|||
_sectorTitleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium]; |
|||
_sectorTitleLabel.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[self.view addSubview:_sectorTitleLabel]; |
|||
|
|||
_sectorBtn = [UIButton buttonWithConfiguration:self.buttonConfig primaryAction:nil]; |
|||
_sectorBtn.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[self.view addSubview:_sectorBtn]; |
|||
|
|||
_sectorCard1 = [self createSectorCardWithData:self.sectorDataConfig[0]]; |
|||
[self.view addSubview:_sectorCard1]; |
|||
|
|||
_sectorCard2 = [self createSectorCardWithData:self.sectorDataConfig[1]]; |
|||
[self.view addSubview:_sectorCard2]; |
|||
|
|||
_sectorCard3 = [self createSectorCardWithData:self.sectorDataConfig[2]]; |
|||
[self.view addSubview:_sectorCard3]; |
|||
|
|||
// 股票容器 |
|||
_stockContainer = [[UIView alloc] init]; |
|||
_stockContainer.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[self.view addSubview:_stockContainer]; |
|||
|
|||
UILabel *stockTitleLabel = [[UILabel alloc] init]; |
|||
stockTitleLabel.text = @"股票"; |
|||
stockTitleLabel.textColor = [UIColor whiteColor]; |
|||
stockTitleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium]; |
|||
stockTitleLabel.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_stockContainer addSubview:stockTitleLabel]; |
|||
|
|||
UIButton *stockBtn = [UIButton buttonWithConfiguration:self.buttonConfig primaryAction:nil]; |
|||
stockBtn.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_stockContainer addSubview:stockBtn]; |
|||
|
|||
// 股票列表表头 |
|||
_stockHeaderView = [[UIView alloc] init]; |
|||
_stockHeaderView.backgroundColor = [UIColor blackColor]; |
|||
_stockHeaderView.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_stockContainer addSubview:_stockHeaderView]; |
|||
|
|||
NSArray *headerTitles = @[@"名称", @"最新", @"涨幅"]; |
|||
for (NSInteger i = 0; i < headerTitles.count; i++) { |
|||
UILabel *headerLabel = [[UILabel alloc] init]; |
|||
headerLabel.text = headerTitles[i]; |
|||
headerLabel.textColor = [UIColor lightGrayColor]; |
|||
headerLabel.font = [UIFont systemFontOfSize:12]; |
|||
headerLabel.textAlignment = (i == 0) ? NSTextAlignmentLeft : NSTextAlignmentRight; |
|||
headerLabel.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_stockHeaderView addSubview:headerLabel]; |
|||
|
|||
if (i == 0) { |
|||
[headerLabel.leadingAnchor constraintEqualToAnchor:_stockHeaderView.leadingAnchor constant:15].active = YES; |
|||
} else if (i == 1) { |
|||
[headerLabel.widthAnchor constraintEqualToConstant:60].active = YES; |
|||
[headerLabel.trailingAnchor constraintEqualToAnchor:_stockHeaderView.trailingAnchor constant:-110].active = YES; |
|||
} else { |
|||
[headerLabel.widthAnchor constraintEqualToConstant:80].active = YES; |
|||
[headerLabel.trailingAnchor constraintEqualToAnchor:_stockHeaderView.trailingAnchor constant:-30].active = YES; |
|||
} |
|||
[headerLabel.centerYAnchor constraintEqualToAnchor:_stockHeaderView.centerYAnchor].active = YES; |
|||
} |
|||
|
|||
_stockTableView = [[UITableView alloc] init]; |
|||
_stockTableView.backgroundColor = [UIColor blackColor]; |
|||
_stockTableView.separatorStyle = UITableViewCellSeparatorStyleNone;// 隐藏单元格之间的分隔线 |
|||
//SingleLine 默认,1px的分割线 |
|||
_stockTableView.delegate = self; |
|||
_stockTableView.dataSource = self; |
|||
[_stockTableView registerClass:[UITableViewCell class]// 单元格复用类 |
|||
forCellReuseIdentifier:@"StockCell"];// 复用标识 |
|||
_stockTableView.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[_stockContainer addSubview:_stockTableView]; |
|||
} |
|||
|
|||
- (UIView *)createSectorCardWithData:(NSDictionary *)data { |
|||
UIView *card = [[UIView alloc] init]; |
|||
card.backgroundColor = [UIColor darkGrayColor]; |
|||
card.layer.cornerRadius = 8; |
|||
card.translatesAutoresizingMaskIntoConstraints = NO; |
|||
|
|||
// 名称 |
|||
UILabel *nameLabel = [[UILabel alloc] init]; |
|||
nameLabel.text = data[@"name"]; |
|||
nameLabel.textColor = [UIColor whiteColor]; |
|||
nameLabel.font = [UIFont systemFontOfSize:14]; |
|||
nameLabel.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[card addSubview:nameLabel]; |
|||
|
|||
// 数值 |
|||
UILabel *value1Label = [[UILabel alloc] init]; |
|||
value1Label.text = data[@"value1"]; |
|||
value1Label.textColor = [UIColor greenColor]; |
|||
value1Label.font = [UIFont systemFontOfSize:16 weight:UIFontWeightBold]; |
|||
value1Label.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[card addSubview:value1Label]; |
|||
|
|||
// 涨幅 |
|||
UILabel *value2Label = [[UILabel alloc] init]; |
|||
value2Label.text = data[@"value2"]; |
|||
value2Label.textColor = [UIColor greenColor]; |
|||
value2Label.font = [UIFont systemFontOfSize:12]; |
|||
value2Label.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[card addSubview:value2Label]; |
|||
|
|||
UILabel *value3Label = [[UILabel alloc] init]; |
|||
value3Label.text = data[@"value3"]; |
|||
value3Label.textColor = [UIColor greenColor]; |
|||
value3Label.font = [UIFont systemFontOfSize:12]; |
|||
value3Label.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[card addSubview:value3Label]; |
|||
|
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[nameLabel.topAnchor constraintEqualToAnchor:card.topAnchor constant:7], |
|||
[nameLabel.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:10], |
|||
|
|||
[value1Label.topAnchor constraintEqualToAnchor:nameLabel.bottomAnchor constant:10], |
|||
[value1Label.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:10], |
|||
|
|||
[value2Label.topAnchor constraintEqualToAnchor:value1Label.bottomAnchor constant:7], |
|||
[value2Label.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:10], |
|||
|
|||
[value3Label.topAnchor constraintEqualToAnchor:value2Label.bottomAnchor constant:6], |
|||
[value3Label.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:10] |
|||
]]; |
|||
|
|||
return card; |
|||
} |
|||
|
|||
- (void)setupConstraints { |
|||
// 马来西亚按钮 |
|||
NSLayoutConstraint *btnTop = [_mlxyButton.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor]; |
|||
NSLayoutConstraint *btnLeft = [_mlxyButton.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:15]; |
|||
NSLayoutConstraint *btnWidth = [_mlxyButton.widthAnchor constraintEqualToConstant:90]; |
|||
NSLayoutConstraint *btnHeight = [_mlxyButton.heightAnchor constraintEqualToConstant:30]; |
|||
[NSLayoutConstraint activateConstraints:@[btnTop, btnLeft, btnWidth, btnHeight]]; |
|||
|
|||
// 大盘 |
|||
NSLayoutConstraint *marketTitleTop = [_marketTitleLabel.topAnchor constraintEqualToAnchor:_mlxyButton.bottomAnchor constant:10]; |
|||
NSLayoutConstraint *marketTitleLeft = [_marketTitleLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:15]; |
|||
[NSLayoutConstraint activateConstraints:@[marketTitleTop, marketTitleLeft]]; |
|||
|
|||
NSLayoutConstraint *marketCardTop = [_marketView.topAnchor constraintEqualToAnchor:_marketTitleLabel.bottomAnchor constant:10]; |
|||
NSLayoutConstraint *marketCardLeft = [_marketView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:15]; |
|||
NSLayoutConstraint *marketCardWidth = [_marketView.widthAnchor constraintEqualToAnchor:self.view.widthAnchor multiplier:0.3]; |
|||
NSLayoutConstraint *marketCardHeight = [_marketView.heightAnchor constraintEqualToConstant:100]; |
|||
[NSLayoutConstraint activateConstraints:@[marketCardTop, marketCardLeft, marketCardWidth, marketCardHeight]]; |
|||
|
|||
NSLayoutConstraint *line1Top = [_marketLine1.topAnchor constraintEqualToAnchor:_marketView.topAnchor constant:10]; |
|||
NSLayoutConstraint *line1CenterX = [_marketLine1.centerXAnchor constraintEqualToAnchor:_marketView.centerXAnchor]; |
|||
|
|||
NSLayoutConstraint *line2Top = [_marketLine2.topAnchor constraintEqualToAnchor:_marketLine1.bottomAnchor constant:15]; |
|||
NSLayoutConstraint *line2CenterX = [_marketLine2.centerXAnchor constraintEqualToAnchor:_marketView.centerXAnchor]; |
|||
|
|||
NSLayoutConstraint *line3Top = [_marketLine3.topAnchor constraintEqualToAnchor:_marketLine2.bottomAnchor constant:12]; |
|||
NSLayoutConstraint *line3CenterX = [_marketLine3.centerXAnchor constraintEqualToAnchor:_marketView.centerXAnchor]; |
|||
|
|||
[NSLayoutConstraint activateConstraints:@[line1Top, line1CenterX, line2Top, line2CenterX, line3Top, line3CenterX]]; |
|||
|
|||
// 板块 |
|||
NSLayoutConstraint *sectorTitleTop = [_sectorTitleLabel.topAnchor constraintEqualToAnchor:_marketView.bottomAnchor constant:20]; |
|||
NSLayoutConstraint *sectorTitleLeft = [_sectorTitleLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:15]; |
|||
[NSLayoutConstraint activateConstraints:@[sectorTitleTop, sectorTitleLeft]]; |
|||
|
|||
NSLayoutConstraint *sectorBtnTop = [_sectorBtn.centerYAnchor constraintEqualToAnchor:_sectorTitleLabel.centerYAnchor]; |
|||
NSLayoutConstraint *sectorBtnRight = [_sectorBtn.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-15]; |
|||
[NSLayoutConstraint activateConstraints:@[sectorBtnTop, sectorBtnRight]]; |
|||
|
|||
NSLayoutConstraint *card1Top = [_sectorCard1.topAnchor constraintEqualToAnchor:_sectorTitleLabel.bottomAnchor constant:10]; |
|||
NSLayoutConstraint *card1Left = [_sectorCard1.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:8]; |
|||
NSLayoutConstraint *card1Width = [_sectorCard1.widthAnchor constraintEqualToAnchor:self.view.widthAnchor multiplier:0.3]; |
|||
NSLayoutConstraint *card1Height = [_sectorCard1.heightAnchor constraintEqualToConstant:100]; |
|||
[NSLayoutConstraint activateConstraints:@[card1Top, card1Left, card1Width, card1Height]]; |
|||
|
|||
NSLayoutConstraint *card2Top = [_sectorCard2.topAnchor constraintEqualToAnchor:_sectorCard1.topAnchor]; |
|||
NSLayoutConstraint *card2Left = [_sectorCard2.leadingAnchor constraintEqualToAnchor:_sectorCard1.trailingAnchor constant:8]; |
|||
NSLayoutConstraint *card2Width = [_sectorCard2.widthAnchor constraintEqualToAnchor:_sectorCard1.widthAnchor]; |
|||
NSLayoutConstraint *card2Height = [_sectorCard2.heightAnchor constraintEqualToAnchor:_sectorCard1.heightAnchor]; |
|||
[NSLayoutConstraint activateConstraints:@[card2Top, card2Left, card2Width, card2Height]]; |
|||
|
|||
NSLayoutConstraint *card3Top = [_sectorCard3.topAnchor constraintEqualToAnchor:_sectorCard1.topAnchor]; |
|||
NSLayoutConstraint *card3Left = [_sectorCard3.leadingAnchor constraintEqualToAnchor:_sectorCard2.trailingAnchor constant:8]; |
|||
NSLayoutConstraint *card3Right = [_sectorCard3.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-16]; |
|||
// *card3Width = [_sectorCard3.widthAnchor constraintEqualToAnchor:_sectorCard1.widthAnchor];都行 |
|||
NSLayoutConstraint *card3Height = [_sectorCard3.heightAnchor constraintEqualToAnchor:_sectorCard1.heightAnchor]; |
|||
[NSLayoutConstraint activateConstraints:@[card3Top, card3Left, card3Right, card3Height]]; |
|||
|
|||
// 股票容器 |
|||
NSLayoutConstraint *stockTop = [_stockContainer.topAnchor constraintEqualToAnchor:_sectorCard1.bottomAnchor constant:20]; |
|||
NSLayoutConstraint *stockLeft = [_stockContainer.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor]; |
|||
NSLayoutConstraint *stockRight = [_stockContainer.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor]; |
|||
NSLayoutConstraint *stockBottom = [_stockContainer.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor]; |
|||
[NSLayoutConstraint activateConstraints:@[stockTop, stockLeft, stockRight, stockBottom]]; |
|||
|
|||
UILabel *stockTitleLabel = _stockContainer.subviews[0]; |
|||
UIButton *stockBtn = _stockContainer.subviews[1]; |
|||
|
|||
NSLayoutConstraint *stockTitleTop = [stockTitleLabel.topAnchor constraintEqualToAnchor:_stockContainer.topAnchor constant:15]; |
|||
NSLayoutConstraint *stockTitleLeft = [stockTitleLabel.leadingAnchor constraintEqualToAnchor:_stockContainer.leadingAnchor constant:15]; |
|||
|
|||
NSLayoutConstraint *stockBtnTop = [stockBtn.centerYAnchor constraintEqualToAnchor:stockTitleLabel.centerYAnchor]; |
|||
NSLayoutConstraint *stockBtnRight = [stockBtn.trailingAnchor constraintEqualToAnchor:_stockContainer.trailingAnchor constant:-15]; |
|||
|
|||
[NSLayoutConstraint activateConstraints:@[stockTitleTop, stockTitleLeft, stockBtnTop, stockBtnRight]]; |
|||
|
|||
// 股票表头 |
|||
NSLayoutConstraint *headerTop = [_stockHeaderView.topAnchor constraintEqualToAnchor:stockTitleLabel.bottomAnchor constant:10]; |
|||
NSLayoutConstraint *headerLeft = [_stockHeaderView.leadingAnchor constraintEqualToAnchor:_stockContainer.leadingAnchor]; |
|||
NSLayoutConstraint *headerRight = [_stockHeaderView.trailingAnchor constraintEqualToAnchor:_stockContainer.trailingAnchor]; |
|||
NSLayoutConstraint *headerHeight = [_stockHeaderView.heightAnchor constraintEqualToConstant:30]; |
|||
[NSLayoutConstraint activateConstraints:@[headerTop, headerLeft, headerRight, headerHeight]]; |
|||
|
|||
// 股票列表 |
|||
NSLayoutConstraint *tableTop = [_stockTableView.topAnchor constraintEqualToAnchor:_stockHeaderView.bottomAnchor]; |
|||
NSLayoutConstraint *tableLeft = [_stockTableView.leadingAnchor constraintEqualToAnchor:_stockContainer.leadingAnchor]; |
|||
NSLayoutConstraint *tableRight = [_stockTableView.trailingAnchor constraintEqualToAnchor:_stockContainer.trailingAnchor]; |
|||
NSLayoutConstraint *tableBottom = [_stockTableView.bottomAnchor constraintEqualToAnchor:_stockContainer.bottomAnchor]; |
|||
[NSLayoutConstraint activateConstraints:@[tableTop, tableLeft, tableRight, tableBottom]]; |
|||
} |
|||
|
|||
#pragma mark - UITableView |
|||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { |
|||
return 4; |
|||
} |
|||
|
|||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { |
|||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"StockCell" forIndexPath:indexPath]; |
|||
cell.backgroundColor = [UIColor blackColor]; |
|||
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
|||
|
|||
// 清空复用内容 |
|||
[cell.contentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; |
|||
|
|||
NSArray *stocks = @[ |
|||
@{@"name":@"PEGASUS HEIGHTS BERHAD", @"code":@"4464", @"price":@"0.010", @"value2":@"100.000%"}, |
|||
@{@"name":@"SMTRACK BERHAD", @"code":@"0169", @"price":@"0.010", @"value2":@"100.000%"}, |
|||
@{@"name":@"TXCD BERHAD - ICPS 2020/2030", @"code":@"7145PA", @"price":@"0.020", @"value2":@"33.333%"}, |
|||
@{@"name":@"DNONCE TECHNOLOGY BHD", @"code":@"7114", @"price":@"0.040", @"value2":@"33.333%"} |
|||
]; |
|||
NSDictionary *stock = stocks[indexPath.row]; |
|||
|
|||
// 名称 |
|||
UILabel *nameLabel = [[UILabel alloc] init]; |
|||
nameLabel.text = stock[@"name"]; |
|||
nameLabel.textColor = [UIColor whiteColor]; |
|||
nameLabel.font = [UIFont systemFontOfSize:14]; |
|||
nameLabel.numberOfLines = 1; |
|||
nameLabel.lineBreakMode = NSLineBreakByTruncatingTail; |
|||
nameLabel.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[cell.contentView addSubview:nameLabel]; |
|||
|
|||
// 代码 |
|||
UILabel *codeLabel = [[UILabel alloc] init]; |
|||
codeLabel.text = stock[@"code"]; |
|||
codeLabel.textColor = [UIColor lightGrayColor]; |
|||
codeLabel.font = [UIFont systemFontOfSize:12]; |
|||
codeLabel.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[cell.contentView addSubview:codeLabel]; |
|||
|
|||
// 价格 |
|||
UILabel *priceLabel = [[UILabel alloc] init]; |
|||
priceLabel.text = stock[@"price"]; |
|||
priceLabel.textColor = [UIColor greenColor]; |
|||
priceLabel.font = [UIFont systemFontOfSize:14]; |
|||
priceLabel.textAlignment = NSTextAlignmentRight; |
|||
priceLabel.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[cell.contentView addSubview:priceLabel]; |
|||
|
|||
// 涨幅 |
|||
UILabel *value2Label = [[UILabel alloc] init]; |
|||
value2Label.text = stock[@"value2"]; |
|||
value2Label.textColor = [UIColor greenColor]; |
|||
value2Label.font = [UIFont systemFontOfSize:14]; |
|||
value2Label.textAlignment = NSTextAlignmentRight; |
|||
value2Label.translatesAutoresizingMaskIntoConstraints = NO; |
|||
[cell.contentView addSubview:value2Label]; |
|||
|
|||
// 约束 |
|||
[NSLayoutConstraint activateConstraints:@[ |
|||
[nameLabel.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor constant:15], |
|||
[nameLabel.topAnchor constraintEqualToAnchor:cell.contentView.topAnchor constant:10], |
|||
[nameLabel.trailingAnchor constraintLessThanOrEqualToAnchor:priceLabel.leadingAnchor constant:-10], |
|||
|
|||
[codeLabel.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor constant:15], |
|||
[codeLabel.topAnchor constraintEqualToAnchor:nameLabel.bottomAnchor constant:5], |
|||
|
|||
[priceLabel.widthAnchor constraintEqualToConstant:60], |
|||
[priceLabel.trailingAnchor constraintEqualToAnchor:value2Label.leadingAnchor constant:-10], |
|||
[priceLabel.centerYAnchor constraintEqualToAnchor:cell.contentView.centerYAnchor], |
|||
|
|||
[value2Label.widthAnchor constraintEqualToConstant:80], |
|||
[value2Label.trailingAnchor constraintEqualToAnchor:cell.contentView.trailingAnchor constant:-15], |
|||
[value2Label.centerYAnchor constraintEqualToAnchor:cell.contentView.centerYAnchor] |
|||
]]; |
|||
|
|||
return cell; |
|||
} |
|||
|
|||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { |
|||
return 60; |
|||
} |
|||
|
|||
@end |
|||