文章的目的为了记录使用Objective-C 进行IOS app 开发学习的经历。本职为嵌入式软件开发,公司安排开发app,临时学习,完成app的开发。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。
相关链接:
开源 Objective-C IOS 应用开发(一)macOS 的使用
开源 Objective-C IOS 应用开发(二)Xcode安装
开源 Objective-C IOS 应用开发(三)第一个iPhone的APP
开源 Objective-C IOS 应用开发(四)Xcode工程文件结构
开源 Objective-C IOS 应用开发(五)iOS操作(action)和输出口(Outlet)
开源 Objective-C IOS 应用开发(六)Objective-C 和 C语言
开源 Objective-C IOS 应用开发(七)Objective-C核心代码示例
开源 Objective-C IOS 应用开发(八)常见控件UI
开源 Objective-C IOS 应用开发(九)复杂控件-tableview
开源 Objective-C IOS 应用开发(十)数据持久化--文件
开源 Objective-C IOS 应用开发(十一)数据持久化--sqlite
开源 Objective-C IOS 应用开发(十二)通讯--ble
开源 Objective-C IOS 应用开发(十三)通讯--Http访问
开源 Objective-C IOS 应用开发(十四)传感器--陀螺仪和gps
开源 Objective-C IOS 应用开发(十五)通讯--蓝牙ble扫描
开源 Objective-C IOS 应用开发(十六)Storyboard模式下的纯代码界面
开源 Objective-C IOS 应用开发(十七)CAF音频的录制
开源 Objective-C IOS 应用开发(十八)音频的播放
开源 Objective-C IOS 应用开发(十九)视频的播放
开源 Objective-C IOS 应用开发(二十)多线程处理
开源 Objective-C IOS 应用开发(二十一)自定义控件--示波器
开源 Objective-C IOS 应用开发(二十二)自定义控件--车速仪表盘
推荐链接:
开源 Arkts 鸿蒙应用 开发(一)工程文件分析-CSDN博客
开源 Arkts 鸿蒙应用 开发(二)封装库.har制作和应用-CSDN博客
开源 Arkts 鸿蒙应用 开发(三)Arkts的介绍-CSDN博客
开源 Arkts 鸿蒙应用 开发(四)布局和常用控件-CSDN博客
开源 Arkts 鸿蒙应用 开发(五)控件组成和复杂控件-CSDN博客
开源 Arkts 鸿蒙应用 开发(六)数据持久--文件和首选项存储-CSDN博客
开源 Arkts 鸿蒙应用 开发(七)数据持久--sqlite关系数据库-CSDN博客
开源 Arkts 鸿蒙应用 开发(八)多媒体--相册和相机-CSDN博客
开源 Arkts 鸿蒙应用 开发(九)通讯--tcp客户端-CSDN博客
开源 Arkts 鸿蒙应用 开发(十)通讯--Http-CSDN博客
开源 Arkts 鸿蒙应用 开发(十一)证书和包名修改-CSDN博客
开源 Arkts 鸿蒙应用 开发(十二)传感器的使用-CSDN博客
开源 Arkts 鸿蒙应用 开发(十三)音频--MP3播放_arkts avplayer播放音频 mp3-CSDN博客
开源 Arkts 鸿蒙应用 开发(十四)线程--任务池(taskpool)-CSDN博客
开源 Arkts 鸿蒙应用 开发(十五)自定义绘图控件--仪表盘-CSDN博客
开源 Arkts 鸿蒙应用 开发(十六)自定义绘图控件--波形图-CSDN博客
开源 Arkts 鸿蒙应用 开发(十七)通讯--http多文件下载-CSDN博客
开源 Arkts 鸿蒙应用 开发(十八)通讯--Ble低功耗蓝牙服务器-CSDN博客
推荐链接:
开源 java android app 开发(一)开发环境的搭建-CSDN博客
开源 java android app 开发(二)工程文件结构-CSDN博客
开源 java android app 开发(三)GUI界面布局和常用组件-CSDN博客
开源 java android app 开发(四)GUI界面重要组件-CSDN博客
开源 java android app 开发(五)文件和数据库存储-CSDN博客
开源 java android app 开发(六)多媒体使用-CSDN博客
开源 java android app 开发(七)通讯之Tcp和Http-CSDN博客
开源 java android app 开发(八)通讯之Mqtt和Ble-CSDN博客
开源 java android app 开发(九)后台之线程和服务-CSDN博客
开源 java android app 开发(十)广播机制-CSDN博客
开源 java android app 开发(十一)调试、发布-CSDN博客
开源 java android app 开发(十二)封库.aar-CSDN博客
本章内容主要是iphone自定义控件汽车仪表盘的实现。
目录:
1.手机演示
2.所有源码
3.源码分析
一、手机演示

二、所有源码
AppDelegate.h文件
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
AppDelegate.m文件
#import "AppDelegate.h"
#import "MainViewController.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 创建窗口
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
// 创建主视图控制器
MainViewController *mainVC = [[MainViewController alloc] init];
// 设置窗口的根视图控制器
self.window.rootViewController = mainVC;
// 显示窗口
[self.window makeKeyAndVisible];
return YES;
}
// 其他已有的方法保持不变...
@end
SpeedometerView.h文件
//
// SpeedometerView.h
// dashboard
//
// Created by Mixic2025 on 2025/11/20.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface SpeedometerView : UIView
@property (nonatomic, assign) CGFloat currentSpeed;
@property (nonatomic, assign) CGFloat maxSpeed;
@property (nonatomic, strong) UIColor *needleColor;
@property (nonatomic, strong) UIColor *speedTextColor;
- (void)setSpeed:(CGFloat)speed animated:(BOOL)animated;
- (instancetype)initWithFrame:(CGRect)frame maxSpeed:(CGFloat)maxSpeed;
@end
NS_ASSUME_NONNULL_END
SpeedometerView.m文件
#import "SpeedometerView.h"
@interface SpeedometerView ()
@property (nonatomic, strong) CAShapeLayer *backgroundLayer;
@property (nonatomic, strong) CAShapeLayer *speedArcLayer;
@property (nonatomic, strong) CAShapeLayer *needleLayer;
@property (nonatomic, strong) CAGradientLayer *centerCircleLayer;
@property (nonatomic, strong) CAGradientLayer *outerGlowLayer; // 外发光效果
@property (nonatomic, strong) UILabel *speedLabel;
@property (nonatomic, strong) UILabel *unitLabel;
@property (nonatomic, assign) CGFloat startAngle;
@property (nonatomic, assign) CGFloat endAngle;
@property (nonatomic, assign) BOOL isViewSetup;
@end
@implementation SpeedometerView
- (instancetype)initWithFrame:(CGRect)frame maxSpeed:(CGFloat)maxSpeed {
self = [super initWithFrame:frame];
if (self) {
_maxSpeed = maxSpeed;
_currentSpeed = 0.0;
_startAngle = M_PI * 0.8;
_endAngle = M_PI * 2.2;
_isViewSetup = NO;
// 使用深空蓝作为背景,营造科幻感
self.backgroundColor = [UIColor colorWithRed:8.0/255.0 green:12.0/255.0 blue:28.0/255.0 alpha:1.0];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
if (!_isViewSetup) {
[self setupView];
_isViewSetup = YES;
} else {
[self updateViewLayout];
}
}
- (void)setupView {
[self.layer.sublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)];
[self.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
// 创建外发光效果
[self createOuterGlow];
[self createBackgroundArc];
[self createSpeedArc];
[self createCenterCircle];
[self createNeedle];
[self createSpeedLabel];
[self createScaleMarks];
[self setSpeed:_currentSpeed animated:NO];
}
- (void)updateViewLayout {
[self updateOuterGlow];
[self updateBackgroundArc];
[self updateSpeedArc];
[self updateCenterCircle];
[self updateNeedle];
[self updateSpeedLabel];
[self updateScaleMarks];
}
- (void)createOuterGlow {
self.outerGlowLayer = [CAGradientLayer layer];
[self updateOuterGlow];
// 外发光渐变色 - 科幻蓝紫光晕
UIColor *outerColor = [UIColor colorWithRed:100.0/255.0 green:200.0/255.0 blue:255.0/255.0 alpha:0.3];
UIColor *innerColor = [UIColor colorWithRed:50.0/255.0 green:100.0/255.0 blue:200.0/255.0 alpha:0.1];
UIColor *clearColor = [UIColor clearColor];
self.outerGlowLayer.colors = @[
(__bridge id)clearColor.CGColor,
(__bridge id)innerColor.CGColor,
(__bridge id)outerColor.CGColor,
(__bridge id)clearColor.CGColor
];
self.outerGlowLayer.startPoint = CGPointMake(0.5, 0.5);
self.outerGlowLayer.endPoint = CGPointMake(1.0, 1.0);
self.outerGlowLayer.type = kCAGradientLayerRadial;
[self.layer addSublayer:self.outerGlowLayer];
}
- (void)updateOuterGlow {
if (!self.outerGlowLayer) return;
CGFloat centerX = CGRectGetMidX(self.bounds);
CGFloat centerY = CGRectGetMidY(self.bounds);
CGFloat radius = MIN(self.bounds.size.width, self.bounds.size.height) * 0.45;
self.outerGlowLayer.frame = CGRectMake(centerX - radius, centerY - radius, radius * 2, radius * 2);
}
- (void)createBackgroundArc {
self.backgroundLayer = [CAShapeLayer layer];
[self updateBackgroundArc];
self.backgroundLayer.lineWidth = 18.0;
// 外圆弧颜色 - 半透明科技蓝
UIColor *backgroundArcColor = [UIColor colorWithRed:64.0/255.0 green:156.0/255.0 blue:255.0/255.0 alpha:0.2];
self.backgroundLayer.strokeColor = backgroundArcColor.CGColor;
self.backgroundLayer.fillColor = [UIColor clearColor].CGColor;
self.backgroundLayer.lineCap = kCALineCapButt;
// 添加发光效果
self.backgroundLayer.shadowColor = [UIColor colorWithRed:64.0/255.0 green:156.0/255.0 blue:255.0/255.0 alpha:0.5].CGColor;
self.backgroundLayer.shadowRadius = 3.0;
self.backgroundLayer.shadowOpacity = 0.8;
self.backgroundLayer.shadowOffset = CGSizeZero;
[self.layer addSublayer:self.backgroundLayer];
}
- (void)updateBackgroundArc {
if (!self.backgroundLayer) return;
UIBezierPath *arcPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds))
radius:MIN(self.bounds.size.width, self.bounds.size.height) * 0.4
startAngle:self.startAngle
endAngle:self.endAngle
clockwise:YES];
self.backgroundLayer.path = arcPath.CGPath;
}
- (void)createSpeedArc {
self.speedArcLayer = [CAShapeLayer layer];
[self updateSpeedArc];
self.speedArcLayer.lineWidth = 18.0;
// 速度圆弧颜色 - 清新青蓝渐变
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = self.bounds;
UIColor *startColor = [UIColor colorWithRed:0.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:1.0]; // 青色
UIColor *endColor = [UIColor colorWithRed:0.0/255.0 green:150.0/255.0 blue:255.0/255.0 alpha:1.0]; // 蓝色
gradientLayer.colors = @[
(__bridge id)startColor.CGColor,
(__bridge id)endColor.CGColor
];
gradientLayer.startPoint = CGPointMake(0, 0.5);
gradientLayer.endPoint = CGPointMake(1, 0.5);
// 使用速度圆弧作为遮罩
gradientLayer.mask = self.speedArcLayer;
// 添加发光效果
gradientLayer.shadowColor = [UIColor colorWithRed:0.0/255.0 green:200.0/255.0 blue:255.0/255.0 alpha:0.8].CGColor;
gradientLayer.shadowRadius = 6.0;
gradientLayer.shadowOpacity = 1.0;
gradientLayer.shadowOffset = CGSizeZero;
[self.layer addSublayer:gradientLayer];
// 保存对速度圆弧图层的引用
self.speedArcLayer.strokeColor = [UIColor whiteColor].CGColor; // 遮罩需要不透明颜色
self.speedArcLayer.fillColor = [UIColor clearColor].CGColor;
self.speedArcLayer.lineCap = kCALineCapButt;
self.speedArcLayer.strokeEnd = 0;
}
- (void)updateSpeedArc {
if (!self.speedArcLayer) return;
UIBezierPath *arcPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds))
radius:MIN(self.bounds.size.width, self.bounds.size.height) * 0.4
startAngle:self.startAngle
endAngle:self.endAngle
clockwise:YES];
self.speedArcLayer.path = arcPath.CGPath;
}
- (void)createCenterCircle {
self.centerCircleLayer = [CAGradientLayer layer];
[self updateCenterCircle];
// 中心圆渐变色 - 深空紫到宇宙蓝
UIColor *centerColor1 = [UIColor colorWithRed:25.0/255.0 green:10.0/255.0 blue:80.0/255.0 alpha:1.0];
UIColor *centerColor2 = [UIColor colorWithRed:10.0/255.0 green:30.0/255.0 blue:120.0/255.0 alpha:1.0];
UIColor *centerColor3 = [UIColor colorWithRed:5.0/255.0 green:15.0/255.0 blue:60.0/255.0 alpha:1.0];
self.centerCircleLayer.colors = @[
(__bridge id)centerColor1.CGColor,
(__bridge id)centerColor2.CGColor,
(__bridge id)centerColor3.CGColor
];
self.centerCircleLayer.startPoint = CGPointMake(0.2, 0.2);
self.centerCircleLayer.endPoint = CGPointMake(0.8, 0.8);
self.centerCircleLayer.locations = @[@0.0, @0.5, @1.0];
// 添加内发光效果
self.centerCircleLayer.shadowColor = [UIColor colorWithRed:100.0/255.0 green:100.0/255.0 blue:255.0/255.0 alpha:0.5].CGColor;
self.centerCircleLayer.shadowRadius = 10.0;
self.centerCircleLayer.shadowOpacity = 0.6;
self.centerCircleLayer.shadowOffset = CGSizeZero;
CAShapeLayer *maskLayer = [CAShapeLayer layer];
CGFloat centerX = CGRectGetMidX(self.bounds);
CGFloat centerY = CGRectGetMidY(self.bounds);
CGFloat radius = MIN(self.bounds.size.width, self.bounds.size.height) * 0.4;
CGFloat circleRadius = radius * (2.0/5.0);
maskLayer.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, circleRadius * 2, circleRadius * 2)].CGPath;
self.centerCircleLayer.mask = maskLayer;
[self.layer addSublayer:self.centerCircleLayer];
}
- (void)updateCenterCircle {
if (!self.centerCircleLayer) return;
CGFloat centerX = CGRectGetMidX(self.bounds);
CGFloat centerY = CGRectGetMidY(self.bounds);
CGFloat radius = MIN(self.bounds.size.width, self.bounds.size.height) * 0.4;
CGFloat circleRadius = radius * (2.0/5.0);
self.centerCircleLayer.frame = CGRectMake(centerX - circleRadius, centerY - circleRadius, circleRadius * 2, circleRadius * 2);
CAShapeLayer *maskLayer = (CAShapeLayer *)self.centerCircleLayer.mask;
if (maskLayer) {
maskLayer.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, circleRadius * 2, circleRadius * 2)].CGPath;
}
}
- (void)createNeedle {
self.needleLayer = [CAShapeLayer layer];
[self updateNeedle];
self.needleLayer.lineWidth = 4.0;
// 指针颜色 - 荧光青
UIColor *needleColor = [UIColor colorWithRed:0.0/255.0 green:255.0/255.0 blue:200.0/255.0 alpha:1.0];
self.needleLayer.strokeColor = needleColor.CGColor;
self.needleLayer.fillColor = [UIColor clearColor].CGColor;
// 指针发光效果
self.needleLayer.shadowColor = [UIColor colorWithRed:0.0/255.0 green:255.0/255.0 blue:200.0/255.0 alpha:0.8].CGColor;
self.needleLayer.shadowRadius = 4.0;
self.needleLayer.shadowOpacity = 1.0;
self.needleLayer.shadowOffset = CGSizeZero;
[self.layer addSublayer:self.needleLayer];
}
- (void)updateNeedle {
if (!self.needleLayer) return;
CGFloat currentAngle = self.startAngle + (self.endAngle - self.startAngle) * (self.currentSpeed / self.maxSpeed);
UIBezierPath *needlePath = [UIBezierPath bezierPath];
CGFloat centerX = CGRectGetMidX(self.bounds);
CGFloat centerY = CGRectGetMidY(self.bounds);
CGFloat radius = MIN(self.bounds.size.width, self.bounds.size.height) * 0.4;
CGFloat centerCircleRadius = radius * (2.0/5.0);
CGFloat startX = centerX + radius * cos(currentAngle);
CGFloat startY = centerY + radius * sin(currentAngle);
CGFloat endX = centerX + centerCircleRadius * cos(currentAngle);
CGFloat endY = centerY + centerCircleRadius * sin(currentAngle);
[needlePath moveToPoint:CGPointMake(startX, startY)];
[needlePath addLineToPoint:CGPointMake(endX, endY)];
self.needleLayer.path = needlePath.CGPath;
}
- (void)createSpeedLabel {
CGFloat centerX = CGRectGetMidX(self.bounds);
CGFloat centerY = CGRectGetMidY(self.bounds);
self.speedLabel = [[UILabel alloc] initWithFrame:CGRectMake(centerX - 80, centerY - 25, 160, 50)];
self.speedLabel.text = @"0";
// 速度文字颜色 - 荧光白
self.speedLabel.textColor = [UIColor colorWithRed:220.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:1.0];
self.speedLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:42];
self.speedLabel.textAlignment = NSTextAlignmentCenter;
// 文字发光效果
self.speedLabel.layer.shadowColor = [UIColor colorWithRed:100.0/255.0 green:200.0/255.0 blue:255.0/255.0 alpha:0.6].CGColor;
self.speedLabel.layer.shadowRadius = 3.0;
self.speedLabel.layer.shadowOpacity = 1.0;
self.speedLabel.layer.shadowOffset = CGSizeZero;
[self addSubview:self.speedLabel];
self.unitLabel = [[UILabel alloc] initWithFrame:CGRectMake(centerX - 40, centerY + 30, 80, 20)];
self.unitLabel.text = @"km/h";
self.unitLabel.textColor = [UIColor colorWithRed:150.0/255.0 green:200.0/255.0 blue:255.0/255.0 alpha:0.8];
self.unitLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:14];
self.unitLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:self.unitLabel];
}
- (void)updateSpeedLabel {
if (!self.speedLabel || !self.unitLabel) return;
CGFloat centerX = CGRectGetMidX(self.bounds);
CGFloat centerY = CGRectGetMidY(self.bounds);
self.speedLabel.frame = CGRectMake(centerX - 80, centerY - 25, 160, 50);
self.unitLabel.frame = CGRectMake(centerX - 40, centerY + 30, 80, 20);
}
- (void)createScaleMarks {
CGFloat centerX = CGRectGetMidX(self.bounds);
CGFloat centerY = CGRectGetMidY(self.bounds);
CGFloat radius = MIN(self.bounds.size.width, self.bounds.size.height) * 0.4;
for (int i = 0; i <= 10; i++) {
CGFloat value = self.maxSpeed * i / 10.0;
CGFloat angle = self.startAngle + (self.endAngle - self.startAngle) * (value / self.maxSpeed);
CAShapeLayer *scaleLayer = [CAShapeLayer layer];
UIBezierPath *scalePath = [UIBezierPath bezierPath];
CGFloat innerRadius = radius - 25;
CGFloat outerRadius = radius - (i % 5 == 0 ? 8 : 12);
[scalePath moveToPoint:CGPointMake(centerX + innerRadius * cos(angle),
centerY + innerRadius * sin(angle))];
[scalePath addLineToPoint:CGPointMake(centerX + outerRadius * cos(angle),
centerY + outerRadius * sin(angle))];
scaleLayer.path = scalePath.CGPath;
scaleLayer.lineWidth = i % 5 == 0 ? 3.0 : 1.5;
// 刻度颜色 - 淡蓝色
scaleLayer.strokeColor = [UIColor colorWithRed:100.0/255.0 green:180.0/255.0 blue:255.0/255.0 alpha:0.8].CGColor;
// 刻度发光效果
if (i % 5 == 0) {
scaleLayer.shadowColor = [UIColor colorWithRed:100.0/255.0 green:180.0/255.0 blue:255.0/255.0 alpha:0.5].CGColor;
scaleLayer.shadowRadius = 2.0;
scaleLayer.shadowOpacity = 1.0;
}
[self.layer addSublayer:scaleLayer];
if (i % 5 == 0) {
UILabel *scaleLabel = [[UILabel alloc] init];
scaleLabel.text = [NSString stringWithFormat:@"%.0f", value];
scaleLabel.textColor = [UIColor colorWithRed:150.0/255.0 green:200.0/255.0 blue:255.0/255.0 alpha:0.9];
scaleLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:12];
scaleLabel.textAlignment = NSTextAlignmentCenter;
[scaleLabel sizeToFit];
CGFloat labelRadius = radius - 42;
CGFloat labelX = centerX + labelRadius * cos(angle) - scaleLabel.bounds.size.width / 2;
CGFloat labelY = centerY + labelRadius * sin(angle) - scaleLabel.bounds.size.height / 2;
scaleLabel.frame = CGRectMake(labelX, labelY, scaleLabel.bounds.size.width, scaleLabel.bounds.size.height);
[self addSubview:scaleLabel];
}
}
}
- (void)updateScaleMarks {
for (CALayer *layer in self.layer.sublayers) {
if ([layer isKindOfClass:[CAShapeLayer class]] && layer != self.backgroundLayer && layer != self.speedArcLayer && layer != self.needleLayer) {
[layer removeFromSuperlayer];
}
}
for (UIView *view in self.subviews) {
if (view != self.speedLabel && view != self.unitLabel) {
[view removeFromSuperview];
}
}
[self createScaleMarks];
}
- (void)setSpeed:(CGFloat)speed animated:(BOOL)animated {
speed = MAX(0, MIN(speed, self.maxSpeed));
if (!_isViewSetup) {
_currentSpeed = speed;
return;
}
_currentSpeed = speed;
self.speedLabel.text = [NSString stringWithFormat:@"%.0f", speed];
self.speedArcLayer.strokeEnd = speed / self.maxSpeed;
[self updateNeedle];
}
- (void)setCurrentSpeed:(CGFloat)currentSpeed {
_currentSpeed = currentSpeed;
if (_isViewSetup) {
[self setSpeed:currentSpeed animated:NO];
}
}
@end
MainViewController.h文件
//
// MainViewController.h
// dashboard
//
// Created by Mixic2025 on 2025/11/20.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface MainViewController : UIViewController
@end
NS_ASSUME_NONNULL_END
MainViewController.m文件
//
// MainViewController.m
// dashboard
//
// Created by Mixic2025 on 2025/11/20.
//
#import "MainViewController.h"
#import "SpeedometerView.h"
@interface MainViewController ()
@property (nonatomic, strong) SpeedometerView *speedometer;
@property (nonatomic, strong) UIButton *accelerateButton;
@property (nonatomic, strong) UIButton *decelerateButton;
@property (nonatomic, strong) NSTimer *speedTimer;
@property (nonatomic, assign) CGFloat targetSpeed;
@end
@implementation MainViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blackColor];
self.targetSpeed = 0.0;
[self setupSpeedometer];
[self setupControlButtons];
}
- (void)setupSpeedometer {
// 创建速度表 - 居中显示,大小为300x300
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat speedometerSize = 300;
CGFloat x = (screenWidth - speedometerSize) / 2;
self.speedometer = [[SpeedometerView alloc] initWithFrame:CGRectMake(x, 100, speedometerSize, speedometerSize) maxSpeed:240];
[self.view addSubview:self.speedometer];
// 延迟设置初始速度,确保视图已布局完成
dispatch_async(dispatch_get_main_queue(), ^{
[self.speedometer setSpeed:0 animated:YES];
});
}
- (void)setupControlButtons {
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat buttonWidth = 120;
CGFloat buttonHeight = 50;
CGFloat spacing = 20;
CGFloat totalWidth = buttonWidth * 2 + spacing;
CGFloat startX = (screenWidth - totalWidth) / 2;
CGFloat y = CGRectGetMaxY(self.speedometer.frame) + 80;
// 加速按钮
self.accelerateButton = [UIButton buttonWithType:UIButtonTypeSystem];
self.accelerateButton.frame = CGRectMake(startX, y, buttonWidth, buttonHeight);
[self.accelerateButton setTitle:@"加速 +" forState:UIControlStateNormal];
[self.accelerateButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
self.accelerateButton.backgroundColor = [UIColor systemGreenColor];
self.accelerateButton.layer.cornerRadius = 10;
self.accelerateButton.titleLabel.font = [UIFont boldSystemFontOfSize:18];
[self.accelerateButton addTarget:self action:@selector(startAccelerating) forControlEvents:UIControlEventTouchDown];
[self.accelerateButton addTarget:self action:@selector(stopChangingSpeed) forControlEvents:UIControlEventTouchUpInside];
[self.accelerateButton addTarget:self action:@selector(stopChangingSpeed) forControlEvents:UIControlEventTouchUpOutside];
[self.view addSubview:self.accelerateButton];
// 减速按钮
self.decelerateButton = [UIButton buttonWithType:UIButtonTypeSystem];
self.decelerateButton.frame = CGRectMake(startX + buttonWidth + spacing, y, buttonWidth, buttonHeight);
[self.decelerateButton setTitle:@"减速 -" forState:UIControlStateNormal];
[self.decelerateButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
self.decelerateButton.backgroundColor = [UIColor systemRedColor];
self.decelerateButton.layer.cornerRadius = 10;
self.decelerateButton.titleLabel.font = [UIFont boldSystemFontOfSize:18];
[self.decelerateButton addTarget:self action:@selector(startDecelerating) forControlEvents:UIControlEventTouchDown];
[self.decelerateButton addTarget:self action:@selector(stopChangingSpeed) forControlEvents:UIControlEventTouchUpInside];
[self.decelerateButton addTarget:self action:@selector(stopChangingSpeed) forControlEvents:UIControlEventTouchUpOutside];
[self.view addSubview:self.decelerateButton];
}
- (void)startAccelerating {
[self.speedTimer invalidate];
self.speedTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(increaseSpeed) userInfo:nil repeats:YES];
}
- (void)startDecelerating {
[self.speedTimer invalidate];
self.speedTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(decreaseSpeed) userInfo:nil repeats:YES];
}
- (void)stopChangingSpeed {
[self.speedTimer invalidate];
self.speedTimer = nil;
}
- (void)increaseSpeed {
CGFloat currentSpeed = self.speedometer.currentSpeed;
if (currentSpeed < self.speedometer.maxSpeed) {
CGFloat newSpeed = currentSpeed + 2.0;
[self.speedometer setSpeed:newSpeed animated:YES];
}
}
- (void)decreaseSpeed {
CGFloat currentSpeed = self.speedometer.currentSpeed;
if (currentSpeed > 0) {
CGFloat newSpeed = currentSpeed - 2.0;
[self.speedometer setSpeed:newSpeed animated:YES];
}
}
- (void)dealloc {
[self.speedTimer invalidate];
self.speedTimer = nil;
}
@end
info.plist中删除原来的mainifest配置
三、源码分析
-
AppDelegate.h
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
功能:应用委托的头文件,声明窗口属性。
-
AppDelegate.m
-
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 创建窗口
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];// 创建主视图控制器
MainViewController *mainVC = [[MainViewController alloc] init];// 设置窗口的根视图控制器
self.window.rootViewController = mainVC;// 显示窗口
[self.window makeKeyAndVisible];return YES;
}
-
功能:应用启动入口,创建窗口和主视图控制器。
-
SpeedometerView.h
@interface SpeedometerView : UIView
@property (nonatomic, assign) CGFloat currentSpeed;
@property (nonatomic, assign) CGFloat maxSpeed;
@property (nonatomic, strong) UIColor *needleColor;
@property (nonatomic, strong) UIColor *speedTextColor;- (void)setSpeed:(CGFloat)speed animated:(BOOL)animated;
- (instancetype)initWithFrame:(CGRect)frame maxSpeed:(CGFloat)maxSpeed;
@end
功能:仪表盘视图的公共接口,定义属性和方法。
- SpeedometerView.m - 核心类
4.1 初始化方法
- (instancetype)initWithFrame:(CGRect)frame maxSpeed:(CGFloat)maxSpeed {
self = [super initWithFrame:frame];
if (self) {
_maxSpeed = maxSpeed;
_currentSpeed = 0.0;
_startAngle = M_PI * 0.8; // 144度
_endAngle = M_PI * 2.2; // 396度
_isViewSetup = NO;
// 深空蓝背景
self.backgroundColor = [UIColor colorWithRed:8.0/255.0 green:12.0/255.0 blue:28.0/255.0 alpha:1.0];
}
return self;
}
功能:初始化仪表盘参数和背景色。
4.2 布局管理方法
- (void)layoutSubviews {
[super layoutSubviews];
if (!_isViewSetup) {
[self setupView]; // 首次创建
_isViewSetup = YES;
} else {
[self updateViewLayout]; // 更新布局
}
}
功能:自动布局回调,管理视图创建和更新。
- (void)setupView {
[self.layer.sublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)];
[self.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
// 按层次创建所有组件
[self createOuterGlow];
[self createBackgroundArc];
[self createSpeedArc];
[self createCenterCircle];
[self createNeedle];
[self createSpeedLabel];
[self createScaleMarks];
[self setSpeed:_currentSpeed animated:NO];
}
功能:首次设置所有UI组件。
- (void)updateViewLayout {
[self updateOuterGlow];
[self updateBackgroundArc];
[self updateSpeedArc];
[self updateCenterCircle];
[self updateNeedle];
[self updateSpeedLabel];
[self updateScaleMarks];
}
功能:更新所有组件布局。
4.3 外发光效果方法
- (void)createOuterGlow {
self.outerGlowLayer = [CAGradientLayer layer];
[self updateOuterGlow];
// 蓝紫光晕渐变
UIColor *outerColor = [UIColor colorWithRed:100.0/255.0 green:200.0/255.0 blue:255.0/255.0 alpha:0.3];
UIColor *innerColor = [UIColor colorWithRed:50.0/255.0 green:100.0/255.0 blue:200.0/255.0 alpha:0.1];
self.outerGlowLayer.colors = @[clearColor, innerColor, outerColor, clearColor];
self.outerGlowLayer.type = kCAGradientLayerRadial; // 径向渐变
[self.layer addSublayer:self.outerGlowLayer];
}
功能:创建仪表盘外圈的发光光晕效果。
4.4 背景圆弧方法
- (void)createBackgroundArc {
self.backgroundLayer = [CAShapeLayer layer];
[self updateBackgroundArc];
self.backgroundLayer.lineWidth = 18.0;
// 半透明科技蓝
UIColor *backgroundArcColor = [UIColor colorWithRed:64.0/255.0 green:156.0/255.0 blue:255.0/255.0 alpha:0.2];
self.backgroundLayer.strokeColor = backgroundArcColor.CGColor;
// 添加发光效果
self.backgroundLayer.shadowColor = [UIColor colorWithRed:64.0/255.0 green:156.0/255.0 blue:255.0/255.0 alpha:0.5].CGColor;
self.backgroundLayer.shadowRadius = 3.0;
self.backgroundLayer.shadowOpacity = 0.8;
}
功能:创建静态的背景圆弧。
4.5 速度圆弧方法(核心技术)
- (void)createSpeedArc {
self.speedArcLayer = [CAShapeLayer layer];
[self updateSpeedArc];
self.speedArcLayer.lineWidth = 18.0;
// 创建渐变图层
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = self.bounds;
// 青蓝渐变
UIColor *startColor = [UIColor colorWithRed:0.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:1.0];
UIColor *endColor = [UIColor colorWithRed:0.0/255.0 green:150.0/255.0 blue:255.0/255.0 alpha:1.0];
gradientLayer.colors = @[(__bridge id)startColor.CGColor, (__bridge id)endColor.CGColor];
// 关键:使用速度圆弧作为渐变的遮罩
gradientLayer.mask = self.speedArcLayer;
[self.layer addSublayer:gradientLayer];
self.speedArcLayer.strokeEnd = 0; // 初始长度为0
}
功能:创新性地使用遮罩技术,实现渐变速度圆弧效果。
4.6 中心圆方法
- (void)createCenterCircle {
self.centerCircleLayer = [CAGradientLayer layer];
[self updateCenterCircle];
// 深空紫到宇宙蓝的三色渐变
UIColor *centerColor1 = [UIColor colorWithRed:25.0/255.0 green:10.0/255.0 blue:80.0/255.0 alpha:1.0];
UIColor *centerColor2 = [UIColor colorWithRed:10.0/255.0 green:30.0/255.0 blue:120.0/255.0 alpha:1.0];
UIColor *centerColor3 = [UIColor colorWithRed:5.0/255.0 green:15.0/255.0 blue:60.0/255.0 alpha:1.0];
self.centerCircleLayer.colors = @[centerColor1, centerColor2, centerColor3];
self.centerCircleLayer.locations = @[@0.0, @0.5, @1.0];
// 使用圆形遮罩
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.path = [UIBezierPath bezierPathWithOvalInRect:...].CGPath;
self.centerCircleLayer.mask = maskLayer;
}
功能:创建带渐变和发光效果的中心圆。
4.7 指针方法
- (void)createNeedle {
self.needleLayer = [CAShapeLayer layer];
[self updateNeedle];
self.needleLayer.lineWidth = 4.0;
// 荧光青指针
UIColor *needleColor = [UIColor colorWithRed:0.0/255.0 green:255.0/255.0 blue:200.0/255.0 alpha:1.0];
self.needleLayer.strokeColor = needleColor.CGColor;
// 指针发光效果
self.needleLayer.shadowColor = [UIColor colorWithRed:0.0/255.0 green:255.0/255.0 blue:200.0/255.0 alpha:0.8].CGColor;
self.needleLayer.shadowRadius = 4.0;
}
- (void)updateNeedle {
CGFloat currentAngle = self.startAngle + (self.endAngle - self.startAngle) * (self.currentSpeed / self.maxSpeed);
// 计算起点(圆弧边缘)和终点(中心圆边缘)
CGFloat startX = centerX + radius * cos(currentAngle);
CGFloat startY = centerY + radius * sin(currentAngle);
CGFloat endX = centerX + centerCircleRadius * cos(currentAngle);
CGFloat endY = centerY + centerCircleRadius * sin(currentAngle);
// 创建指针路径
[needlePath moveToPoint:CGPointMake(startX, startY)];
[needlePath addLineToPoint:CGPointMake(endX, endY)];
self.needleLayer.path = needlePath.CGPath;
}
功能:创建和更新指针位置。
4.8 标签和刻度方法
- (void)createSpeedLabel {
self.speedLabel = [[UILabel alloc] initWithFrame:...];
self.speedLabel.text = @"0";
// 荧光白文字
self.speedLabel.textColor = [UIColor colorWithRed:220.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:1.0];
self.speedLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:42];
// 文字发光效果
self.speedLabel.layer.shadowColor = [UIColor colorWithRed:100.0/255.0 green:200.0/255.0 blue:255.0/255.0 alpha:0.6].CGColor;
self.speedLabel.layer.shadowRadius = 3.0;
}
功能:创建速度数字和单位标签。
- (void)createScaleMarks {
for (int i = 0; i <= 10; i++) {
CGFloat value = self.maxSpeed * i / 10.0;
CGFloat angle = self.startAngle + (self.endAngle - self.startAngle) * (value / self.maxSpeed);
// 创建刻度线
CAShapeLayer *scaleLayer = [CAShapeLayer layer];
// 主刻度加粗,次刻度细
scaleLayer.lineWidth = i % 5 == 0 ? 3.0 : 1.5;
// 每5个刻度显示数值
if (i % 5 == 0) {
UILabel *scaleLabel = [[UILabel alloc] init];
scaleLabel.text = [NSString stringWithFormat:@"%.0f", value];
// 添加标签...
}
}
}
功能:创建刻度线和数值标签。
4.9 核心速度控制方法
- (void)setSpeed:(CGFloat)speed animated:(BOOL)animated {
speed = MAX(0, MIN(speed, self.maxSpeed)); // 限制范围
if (!_isViewSetup) {
_currentSpeed = speed;
return;
}
_currentSpeed = speed;
self.speedLabel.text = [NSString stringWithFormat:@"%.0f", speed];
self.speedArcLayer.strokeEnd = speed / self.maxSpeed; // 关键:控制圆弧长度
[self updateNeedle]; // 更新指针位置
}
功能:核心方法,设置速度并更新所有相关UI。
- MainViewController.m - 主控制器
5.1 视图加载方法
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blackColor];
self.targetSpeed = 0.0;
[self setupSpeedometer];
[self setupControlButtons];
}
功能:控制器初始化,设置背景色和创建UI。
5.2 仪表盘设置方法
- (void)setupSpeedometer {
// 计算居中位置
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat speedometerSize = 300;
CGFloat x = (screenWidth - speedometerSize) / 2;
// 创建仪表盘
self.speedometer = [[SpeedometerView alloc] initWithFrame:CGRectMake(x, 100, speedometerSize, speedometerSize) maxSpeed:240];
[self.view addSubview:self.speedometer];
// 延迟设置初始速度
dispatch_async(dispatch_get_main_queue(), ^{
[self.speedometer setSpeed:0 animated:YES];
});
}
功能:创建并定位仪表盘视图。
5.3 控制按钮设置方法
- (void)setupControlButtons {
// 计算按钮布局
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat buttonWidth = 120;
CGFloat buttonHeight = 50;
CGFloat spacing = 20;
CGFloat totalWidth = buttonWidth * 2 + spacing;
CGFloat startX = (screenWidth - totalWidth) / 2;
CGFloat y = CGRectGetMaxY(self.speedometer.frame) + 80;
// 创建加速按钮
self.accelerateButton = [UIButton buttonWithType:UIButtonTypeSystem];
self.accelerateButton.frame = CGRectMake(startX, y, buttonWidth, buttonHeight);
[self.accelerateButton setTitle:@"加速 +" forState:UIControlStateNormal];
self.accelerateButton.backgroundColor = [UIColor systemGreenColor];
self.accelerateButton.layer.cornerRadius = 10;
// 绑定触摸事件
[self.accelerateButton addTarget:self action:@selector(startAccelerating) forControlEvents:UIControlEventTouchDown];
[self.accelerateButton addTarget:self action:@selector(stopChangingSpeed) forControlEvents:UIControlEventTouchUpInside];
// 类似创建减速按钮...
}
功能:创建加速和减速按钮,并绑定触摸事件。
5.4 速度控制方法
- (void)startAccelerating {
[self.speedTimer invalidate];
self.speedTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(increaseSpeed) userInfo:nil repeats:YES];
}
功能:开始加速,启动定时器。
- (void)increaseSpeed {
CGFloat currentSpeed = self.speedometer.currentSpeed;
if (currentSpeed < self.speedometer.maxSpeed) {
CGFloat newSpeed = currentSpeed + 2.0;
[self.speedometer setSpeed:newSpeed animated:YES];
}
}
功能:定时器回调,每次增加2单位速度。
- (void)stopChangingSpeed {
[self.speedTimer invalidate];
self.speedTimer = nil;
}
功能:停止速度变化,取消定时器。
项目架构总结:
-
AppDelegate:应用入口,窗口管理
-
SpeedometerView:自定义仪表盘组件
-
MainViewController:主界面控制器,用户交互处理