diff --git a/HC/AppDelegate.h b/App/HC/AppDelegate.h similarity index 100% rename from HC/AppDelegate.h rename to App/HC/AppDelegate.h diff --git a/HC/AppDelegate.m b/App/HC/AppDelegate.m similarity index 100% rename from HC/AppDelegate.m rename to App/HC/AppDelegate.m diff --git a/HC/Assets.xcassets/AccentColor.colorset/Contents.json b/App/HC/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from HC/Assets.xcassets/AccentColor.colorset/Contents.json rename to App/HC/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/App/HC/Assets.xcassets/AppIcon.appiconset/10241024.jpg b/App/HC/Assets.xcassets/AppIcon.appiconset/10241024.jpg new file mode 100644 index 0000000..5c33dda Binary files /dev/null and b/App/HC/Assets.xcassets/AppIcon.appiconset/10241024.jpg differ diff --git a/HC/Assets.xcassets/AppIcon.appiconset/Contents.json b/App/HC/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 94% rename from HC/Assets.xcassets/AppIcon.appiconset/Contents.json rename to App/HC/Assets.xcassets/AppIcon.appiconset/Contents.json index 2305880..601341d 100644 --- a/HC/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/App/HC/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,6 +1,7 @@ { "images" : [ { + "filename" : "10241024.jpg", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/HC/Assets.xcassets/Contents.json b/App/HC/Assets.xcassets/Contents.json similarity index 100% rename from HC/Assets.xcassets/Contents.json rename to App/HC/Assets.xcassets/Contents.json diff --git a/HC/Assets.xcassets/banner1.imageset/Contents.json b/App/HC/Assets.xcassets/banner1.imageset/Contents.json similarity index 100% rename from HC/Assets.xcassets/banner1.imageset/Contents.json rename to App/HC/Assets.xcassets/banner1.imageset/Contents.json diff --git a/HC/Assets.xcassets/banner1.imageset/banner1.jpg b/App/HC/Assets.xcassets/banner1.imageset/banner1.jpg similarity index 100% rename from HC/Assets.xcassets/banner1.imageset/banner1.jpg rename to App/HC/Assets.xcassets/banner1.imageset/banner1.jpg diff --git a/HC/Assets.xcassets/banner2.imageset/Contents.json b/App/HC/Assets.xcassets/banner2.imageset/Contents.json similarity index 100% rename from HC/Assets.xcassets/banner2.imageset/Contents.json rename to App/HC/Assets.xcassets/banner2.imageset/Contents.json diff --git a/HC/Assets.xcassets/banner2.imageset/banner2.jpg b/App/HC/Assets.xcassets/banner2.imageset/banner2.jpg similarity index 100% rename from HC/Assets.xcassets/banner2.imageset/banner2.jpg rename to App/HC/Assets.xcassets/banner2.imageset/banner2.jpg diff --git a/HC/Assets.xcassets/email.imageset/Contents.json b/App/HC/Assets.xcassets/email.imageset/Contents.json similarity index 100% rename from HC/Assets.xcassets/email.imageset/Contents.json rename to App/HC/Assets.xcassets/email.imageset/Contents.json diff --git a/HC/Assets.xcassets/email.imageset/email.jpg b/App/HC/Assets.xcassets/email.imageset/email.jpg similarity index 100% rename from HC/Assets.xcassets/email.imageset/email.jpg rename to App/HC/Assets.xcassets/email.imageset/email.jpg diff --git a/HC/Assets.xcassets/hqz.imageset/Contents.json b/App/HC/Assets.xcassets/hqz.imageset/Contents.json similarity index 100% rename from HC/Assets.xcassets/hqz.imageset/Contents.json rename to App/HC/Assets.xcassets/hqz.imageset/Contents.json diff --git a/HC/Assets.xcassets/hqz.imageset/hqz.jpg b/App/HC/Assets.xcassets/hqz.imageset/hqz.jpg similarity index 100% rename from HC/Assets.xcassets/hqz.imageset/hqz.jpg rename to App/HC/Assets.xcassets/hqz.imageset/hqz.jpg diff --git a/HC/Assets.xcassets/image.imageset/Contents.json b/App/HC/Assets.xcassets/image.imageset/Contents.json similarity index 100% rename from HC/Assets.xcassets/image.imageset/Contents.json rename to App/HC/Assets.xcassets/image.imageset/Contents.json diff --git a/HC/Assets.xcassets/logo.imageset/20251117-225344.jpg b/App/HC/Assets.xcassets/logo.imageset/20251117-225344.jpg similarity index 100% rename from HC/Assets.xcassets/logo.imageset/20251117-225344.jpg rename to App/HC/Assets.xcassets/logo.imageset/20251117-225344.jpg diff --git a/HC/Assets.xcassets/logo.imageset/Contents.json b/App/HC/Assets.xcassets/logo.imageset/Contents.json similarity index 100% rename from HC/Assets.xcassets/logo.imageset/Contents.json rename to App/HC/Assets.xcassets/logo.imageset/Contents.json diff --git a/HC/Assets.xcassets/logo.jpg b/App/HC/Assets.xcassets/logo.jpg similarity index 100% rename from HC/Assets.xcassets/logo.jpg rename to App/HC/Assets.xcassets/logo.jpg diff --git a/HC/Assets.xcassets/singapore_flag.imageset/Contents.json b/App/HC/Assets.xcassets/singapore_flag.imageset/Contents.json similarity index 100% rename from HC/Assets.xcassets/singapore_flag.imageset/Contents.json rename to App/HC/Assets.xcassets/singapore_flag.imageset/Contents.json diff --git a/HC/Assets.xcassets/singapore_flag.imageset/singapore_flag.jpg b/App/HC/Assets.xcassets/singapore_flag.imageset/singapore_flag.jpg similarity index 100% rename from HC/Assets.xcassets/singapore_flag.imageset/singapore_flag.jpg rename to App/HC/Assets.xcassets/singapore_flag.imageset/singapore_flag.jpg diff --git a/HC/Base.lproj/LaunchScreen.storyboard b/App/HC/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from HC/Base.lproj/LaunchScreen.storyboard rename to App/HC/Base.lproj/LaunchScreen.storyboard diff --git a/HC/Base.lproj/Main.storyboard b/App/HC/Base.lproj/Main.storyboard similarity index 100% rename from HC/Base.lproj/Main.storyboard rename to App/HC/Base.lproj/Main.storyboard diff --git a/HC/CNViewController.h b/App/HC/CNViewController.h similarity index 100% rename from HC/CNViewController.h rename to App/HC/CNViewController.h diff --git a/HC/CNViewController.m b/App/HC/CNViewController.m similarity index 100% rename from HC/CNViewController.m rename to App/HC/CNViewController.m diff --git a/HC/ChartViewController.h b/App/HC/ChartViewController.h similarity index 100% rename from HC/ChartViewController.h rename to App/HC/ChartViewController.h diff --git a/App/HC/ChartViewController.m b/App/HC/ChartViewController.m new file mode 100644 index 0000000..a881cc1 --- /dev/null +++ b/App/HC/ChartViewController.m @@ -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 () + +@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 *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 diff --git a/HC/CommonTabBar.h b/App/HC/CommonTabBar.h similarity index 100% rename from HC/CommonTabBar.h rename to App/HC/CommonTabBar.h diff --git a/HC/CommonTabBar.m b/App/HC/CommonTabBar.m similarity index 100% rename from HC/CommonTabBar.m rename to App/HC/CommonTabBar.m diff --git a/HC/CountryModel.h b/App/HC/CountryModel.h similarity index 100% rename from HC/CountryModel.h rename to App/HC/CountryModel.h diff --git a/HC/CountryModel.m b/App/HC/CountryModel.m similarity index 100% rename from HC/CountryModel.m rename to App/HC/CountryModel.m diff --git a/HC/HC.xcdatamodeld/.xccurrentversion b/App/HC/HC.xcdatamodeld/.xccurrentversion similarity index 100% rename from HC/HC.xcdatamodeld/.xccurrentversion rename to App/HC/HC.xcdatamodeld/.xccurrentversion diff --git a/HC/HC.xcdatamodeld/HC.xcdatamodel/contents b/App/HC/HC.xcdatamodeld/HC.xcdatamodel/contents similarity index 100% rename from HC/HC.xcdatamodeld/HC.xcdatamodel/contents rename to App/HC/HC.xcdatamodeld/HC.xcdatamodel/contents diff --git a/HC/HomeViewController.h b/App/HC/HomeViewController.h similarity index 100% rename from HC/HomeViewController.h rename to App/HC/HomeViewController.h diff --git a/HC/HomeViewController.m b/App/HC/HomeViewController.m similarity index 100% rename from HC/HomeViewController.m rename to App/HC/HomeViewController.m diff --git a/HC/Info.plist b/App/HC/Info.plist similarity index 100% rename from HC/Info.plist rename to App/HC/Info.plist diff --git a/HC/MLXYViewController.h b/App/HC/MLXYViewController.h similarity index 100% rename from HC/MLXYViewController.h rename to App/HC/MLXYViewController.h diff --git a/App/HC/MLXYViewController.m b/App/HC/MLXYViewController.m new file mode 100644 index 0000000..e4e5d5d --- /dev/null +++ b/App/HC/MLXYViewController.m @@ -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 () + +#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 diff --git a/HC/MyViewController.h b/App/HC/MyViewController.h similarity index 100% rename from HC/MyViewController.h rename to App/HC/MyViewController.h diff --git a/HC/MyViewController.m b/App/HC/MyViewController.m similarity index 100% rename from HC/MyViewController.m rename to App/HC/MyViewController.m diff --git a/HC/QuotationViewController.h b/App/HC/QuotationViewController.h similarity index 100% rename from HC/QuotationViewController.h rename to App/HC/QuotationViewController.h diff --git a/HC/QuotationViewController.m b/App/HC/QuotationViewController.m similarity index 95% rename from HC/QuotationViewController.m rename to App/HC/QuotationViewController.m index 012dea1..de9d3b4 100644 --- a/HC/QuotationViewController.m +++ b/App/HC/QuotationViewController.m @@ -50,7 +50,16 @@ vcClassName:NSStringFromClass([MLXYViewController class])], [CountryModel modelWithCode:@"CN" name:@"中国" - vcClassName:NSStringFromClass([CNViewController class])] + vcClassName:NSStringFromClass([CNViewController class])], + [CountryModel modelWithCode:@"CN" + name:@"中国" + vcClassName:NSStringFromClass([CNViewController class])], + [CountryModel modelWithCode:@"CN" + name:@"中国" + vcClassName:NSStringFromClass([CNViewController class])], + [CountryModel modelWithCode:@"CN" + name:@"中国" + vcClassName:NSStringFromClass([CNViewController class])], ]; } diff --git a/HC/SceneDelegate.h b/App/HC/SceneDelegate.h similarity index 100% rename from HC/SceneDelegate.h rename to App/HC/SceneDelegate.h diff --git a/HC/SceneDelegate.m b/App/HC/SceneDelegate.m similarity index 81% rename from HC/SceneDelegate.m rename to App/HC/SceneDelegate.m index 9be9ac3..230823e 100644 --- a/HC/SceneDelegate.m +++ b/App/HC/SceneDelegate.m @@ -22,18 +22,14 @@ if (!windowScene) return; self.window = [[UIWindow alloc] initWithWindowScene:windowScene]; - // 改用窗口场景的屏幕尺寸(兼容 iOS 26+) self.window.frame = windowScene.screen.bounds; self.window.backgroundColor = [UIColor blackColor]; - //ViewController *loginVC = [[ViewController alloc] init]; - //UINavigationController *navVC = [[UINavigationController alloc] initWithRootViewController:loginVC]; - //self.window.rootViewController = navVC; - // 实例化 ChartViewController - ChartViewController *chartVC = [[ChartViewController alloc] init]; - - // **设置 ChartViewController 为根视图控制器** - self.window.rootViewController = chartVC; + ViewController *loginVC = [[ViewController alloc] init]; + UINavigationController *navVC = [[UINavigationController alloc] initWithRootViewController:loginVC]; + self.window.rootViewController = navVC; +// ChartViewController *chartVC = [[ChartViewController alloc] init]; +// self.window.rootViewController = chartVC; [self.window makeKeyAndVisible]; } diff --git a/App/HC/StockInfoCardView.h b/App/HC/StockInfoCardView.h new file mode 100644 index 0000000..587e366 --- /dev/null +++ b/App/HC/StockInfoCardView.h @@ -0,0 +1,29 @@ +// +// StockInfoCardView.h +// HC +// +// Created by huilinLi on 2025/12/1. +// + +#import + +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 diff --git a/App/HC/StockInfoCardView.m b/App/HC/StockInfoCardView.m new file mode 100644 index 0000000..bac1ea1 --- /dev/null +++ b/App/HC/StockInfoCardView.m @@ -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 diff --git a/HC/StockKLineModel.h b/App/HC/StockKLineModel.h similarity index 60% rename from HC/StockKLineModel.h rename to App/HC/StockKLineModel.h index c6aa38f..74ebd21 100644 --- a/HC/StockKLineModel.h +++ b/App/HC/StockKLineModel.h @@ -22,10 +22,20 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) CGFloat low; // 最低价 @property (nonatomic, assign) CGFloat volume; // 成交量 -// 预留给MA均线(后面章节会用到) -@property (nonatomic, assign) CGFloat ma5; -@property (nonatomic, assign) CGFloat ma10; -@property (nonatomic, assign) CGFloat ma20; +// MACD +@property (nonatomic, assign) CGFloat dif; +@property (nonatomic, assign) CGFloat dea; +@property (nonatomic, assign) CGFloat macdBar; + +// MA (均线) +@property (nonatomic, assign) CGFloat MA5; +@property (nonatomic, assign) CGFloat MA10; +@property (nonatomic, assign) CGFloat MA30; + +// KDJ +@property (nonatomic, assign) CGFloat K; +@property (nonatomic, assign) CGFloat D; +@property (nonatomic, assign) CGFloat J; // 辅助方法:判断是否是阳线(涨) - (BOOL)isUp; diff --git a/HC/StockKLineModel.m b/App/HC/StockKLineModel.m similarity index 100% rename from HC/StockKLineModel.m rename to App/HC/StockKLineModel.m diff --git a/HC/ViewController.h b/App/HC/ViewController.h similarity index 100% rename from HC/ViewController.h rename to App/HC/ViewController.h diff --git a/HC/ViewController.m b/App/HC/ViewController.m similarity index 100% rename from HC/ViewController.m rename to App/HC/ViewController.m diff --git a/HC/main.m b/App/HC/main.m similarity index 100% rename from HC/main.m rename to App/HC/main.m diff --git a/HC.xcodeproj/project.pbxproj b/HC.xcodeproj/project.pbxproj index 3288676..3af9026 100644 --- a/HC.xcodeproj/project.pbxproj +++ b/HC.xcodeproj/project.pbxproj @@ -10,22 +10,9 @@ 38BE1CE22ECC0F27004B805B /* HC.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HC.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ -/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - 3895388C2ED53BDF004AECEA /* Exceptions for "HC" folder in "HC" target */ = { - isa = PBXFileSystemSynchronizedBuildFileExceptionSet; - membershipExceptions = ( - Info.plist, - ); - target = 38BE1CE12ECC0F27004B805B /* HC */; - }; -/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ - /* Begin PBXFileSystemSynchronizedRootGroup section */ 38BE1CE42ECC0F27004B805B /* HC */ = { isa = PBXFileSystemSynchronizedRootGroup; - exceptions = ( - 3895388C2ED53BDF004AECEA /* Exceptions for "HC" folder in "HC" target */, - ); path = HC; sourceTree = ""; }; @@ -45,7 +32,7 @@ 38BE1CD92ECC0F27004B805B = { isa = PBXGroup; children = ( - 38BE1CE42ECC0F27004B805B /* HC */, + 38DB04EC2EE6A8EC006CBCAF /* New Group */, 38BE1CE32ECC0F27004B805B /* Products */, ); sourceTree = ""; @@ -58,6 +45,14 @@ name = Products; sourceTree = ""; }; + 38DB04EC2EE6A8EC006CBCAF /* New Group */ = { + isa = PBXGroup; + children = ( + 38BE1CE42ECC0F27004B805B /* HC */, + ); + path = "New Group"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -141,9 +136,12 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + "ASSETCATALOG_COMPILER_APPICON_NAME[sdk=iphoneos*]" = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = S672243B4Z; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = HC/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; @@ -169,8 +167,10 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = S672243B4Z; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = HC/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; diff --git a/HC.xcodeproj/project.xcworkspace/xcuserdata/huilinli.xcuserdatad/IDEFindNavigatorScopes.plist b/HC.xcodeproj/project.xcworkspace/xcuserdata/huilinli.xcuserdatad/IDEFindNavigatorScopes.plist new file mode 100644 index 0000000..5dd5da8 --- /dev/null +++ b/HC.xcodeproj/project.xcworkspace/xcuserdata/huilinli.xcuserdatad/IDEFindNavigatorScopes.plist @@ -0,0 +1,5 @@ + + + + + diff --git a/HC/ChartViewController.m b/HC/ChartViewController.m deleted file mode 100644 index 91208fe..0000000 --- a/HC/ChartViewController.m +++ /dev/null @@ -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 diff --git a/HC/MLXYViewController.m b/HC/MLXYViewController.m deleted file mode 100644 index 505923e..0000000 --- a/HC/MLXYViewController.m +++ /dev/null @@ -1,442 +0,0 @@ -// -// MLXYViewController.m -// HC -// -// Created by huilinLi on 2025/11/26. -// -#import "MLXYViewController.h" -#import "ChartViewController.h" - -@interface MLXYViewController () -@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