51、使用Core Graphics进行绘图开发

使用Core Graphics进行绘图开发

在开发绘图应用时,Core Graphics是一个强大的工具。本文将详细介绍如何使用Core Graphics进行绘图应用的开发,包括随机颜色生成、常量定义、视图骨架实现、界面设置、动作方法实现以及绘图代码添加等方面。

随机颜色生成

为了生成随机颜色,我们使用 arc4random() 函数为每个颜色分量生成一个随机浮点数。每个颜色分量的值需要在0.0到1.0之间,因此我们将随机值除以256取余数,得到0到255之间的数,再除以255。这是因为iOS上的Quartz 2D为每个颜色分量支持256种不同的强度,使用255可以确保我们有机会随机选择其中任何一种。最后,我们使用这三个随机分量创建一个新颜色,并将alpha值设置为1.0,使所有生成的颜色都是不透明的。

定义应用常量

我们定义了两个枚举类型来表示用户可以通过分段控制器选择的选项。一个表示应用中可用的形状选项,另一个表示可用的颜色选项。这些常量的值对应于我们将在应用中创建的两个分段控件的分段。

typedef NS_ENUM(NSInteger, ShapeType) {
  kLineShape = 0,
  kRectShape,
  kEllipseShape,
  kImageShape
};

typedef NS_ENUM(NSInteger, ColorTabIndex) {
  kRedColorTab = 0,
  kBlueColorTab,
  kYellowColorTab,
  kGreenColorTab,
  kRandomColorTab
};
实现QuartzFunView骨架

我们将在 UIView 的子类中进行绘图,因此需要设置这个类。首先,在 QuartzFunView.h 文件中添加必要的代码,导入 Constants.h 头文件,并声明三个属性来跟踪用户想要绘制的形状、是否请求随机颜色以及当前选择的颜色。

#import <UIKit/UIKit.h>
#import "Constants.h"

@interface QuartzFunView : UIView

@property (assign, nonatomic) ShapeType shapeType;
@property (assign, nonatomic) BOOL useRandomColor;
@property (strong, nonatomic) UIColor *currentColor;

@end

然后,在 QuartzFunView.m 文件中进行一些更改。导入 UIColor+Random.h 头文件以生成随机颜色,并创建类扩展,添加三个更多的属性来跟踪用户手指在屏幕上的拖动位置和要绘制的图像。

#import "UIColor+Random.h"

@interface QuartzFunView ()

@property (assign, nonatomic) CGPoint firstTouchLocation;
@property (assign, nonatomic) CGPoint lastTouchLocation;
@property (strong, nonatomic) UIImage *image;

@end

接下来,重写 initWithCoder: 方法,设置初始颜色为红色,初始化 useRandomColor NO ,并加载要绘制的图像文件。

- (id)initWithCoder:(NSCoder*)coder {
    if (self = [super initWithCoder:coder]) {
        _currentColor = [UIColor redColor];
        _useRandomColor = NO;
        _image = [UIImage imageNamed:@"iphone"] ;
    }
    return self;
}

最后,添加三个方法来响应用户的触摸事件。

#pragma mark - Touch Handling

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    if (self.useRandomColor) {
        self.currentColor = [UIColor randomColor];
    }
    UITouch *touch = [touches anyObject];
    self.firstTouchLocation = [touch locationInView:self];
    self.lastTouchLocation = [touch locationInView:self];
    [self setNeedsDisplay];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    self.lastTouchLocation = [touch locationInView:self];

    [self setNeedsDisplay];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    self.lastTouchLocation = [touch locationInView:self];

    [self setNeedsDisplay];
}

这些方法的工作原理如下:
- touchesBegan:withEvent: :当用户的手指首次触摸屏幕时调用。如果用户选择了随机颜色,我们会更改颜色,然后存储当前位置,并指示视图需要重绘。
- touchesMoved:withEvent: :当用户在屏幕上拖动手指时持续调用。我们只需要存储新位置并指示屏幕需要重绘。
- touchesEnded:withEvent: :当用户将手指从屏幕上抬起时调用。我们存储最终位置并指示视图需要重绘。

创建和连接出口和动作

在开始绘图之前,我们需要在GUI中添加分段控件,并连接动作和出口。具体步骤如下:
1. 打开 Main.storyboard ,将视图的类从 UIView 更改为 QuartzFunView
2. 使用对象库找到一个分段控件,将其拖到视图顶部,状态栏下方,并大致居中。
3. 选择分段控件,将分段数量从2更改为5,并依次将每个分段的标签更改为Red、Blue、Yellow、Green和Random。
4. 应用布局约束:
- 在文档大纲中,从分段控件项Control-drag到Quartz Fun View项,释放鼠标并选择Top Space to Top Layout Guide。
- 再次Control-drag,选择Center Horizontally in Container。
- 点击编辑区域底部的Pin按钮,勾选Width复选框并输入290,然后点击Add 1 constraint。
- 在文档大纲中选择视图控制器图标,然后在故事板编辑器中点击Resolve Auto Layout Issues按钮并选择Update Frames。
5. 打开辅助编辑器,选择 ViewController.m 。从文档大纲中的分段控件Control-drag到 ViewController.m 文件,在 @interface @end 之间创建一个新的出口,命名为 colorControl
6. 添加一个动作:选择 Main.storyboard ,从分段控件Control-drag到 ViewController.m 文件,直接在 @end 声明上方创建一个动作,将连接类型更改为Action,名称更改为 changeColor ,类型更改为 UISegmentedControl
7. 添加第二个分段控件用于选择形状:
- 将分段控件拖到视图底部。
- 将分段数量从2更改为4,依次将每个分段的标题更改为Line、Rect、Ellipse和Image。
- 应用布局约束,步骤与颜色选择分段控件类似。
- 打开 ViewController.m ,从新的分段控件Control-drag到 ViewController.m 文件,在 @end 行上方创建一个动作,将连接类型更改为Action,名称更改为 changeShape ,类型更改为 UISegmentedControl

实现动作方法

ViewController.m 文件中,我们需要导入 Constants.h QuartzFunView.h 头文件,然后实现 changeColor: changeShape: 方法。

#import "Constants.h"
#import "QuartzFunView.h"

- (IBAction)changeColor:(UISegmentedControl *)sender {
    QuartzFunView *funView = (QuartzFunView *)self.view;
    ColorTabIndex index = [sender selectedSegmentIndex];
    switch (index) {
        case kRedColorTab:
            funView.currentColor = [UIColor redColor];
            funView.useRandomColor = NO;
            break;
        case kBlueColorTab:
            funView.currentColor = [UIColor blueColor];
            funView.useRandomColor = NO;
            break;
        case kYellowColorTab:
            funView.currentColor = [UIColor yellowColor];
            funView.useRandomColor = NO;
            break;
        case kGreenColorTab:
            funView.currentColor = [UIColor greenColor];
            funView.useRandomColor = NO;
            break;
        case kRandomColorTab:
            funView.useRandomColor = YES;
            break;
        default:
            break;
    }
}

- (IBAction)changeShape:(UISegmentedControl *)sender {
  [(QuartzFunView *)self.view setShapeType:[sender selectedSegmentIndex]];
  self.colorControl.hidden = [sender selectedSegmentIndex] == kImageShape;
}

changeColor: 方法中,我们根据用户选择的分段创建一个新颜色,并设置 currentColor 属性。如果选择了随机颜色,我们将 useRandomColor 属性设置为 YES 。在 changeShape: 方法中,我们根据所选分段设置形状类型,并根据是否选择了Image分段隐藏或显示颜色选择控件。

编译并运行应用,此时虽然还不能在屏幕上绘制形状,但分段控件应该可以正常工作,当点击底部控件中的Image分段时,颜色控件应该会消失。

添加Quartz 2D绘图代码

我们准备添加绘图代码,首先从绘制一条线开始。

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 2.0);
    CGContextSetStrokeColorWithColor(context, self.currentColor.CGColor);

    switch (self.shapeType) {
        case kLineShape:
            CGContextMoveToPoint(context,
                                 self.firstTouchLocation.x,
                                 self.firstTouchLocation.y);
            CGContextAddLineToPoint(context,
                                    self.lastTouchLocation.x,
                                    self.lastTouchLocation.y);
            CGContextStrokePath(context);
            break;
        case kRectShape:
            break;
        case kEllipseShape:
            break;
        case kImageShape:
            break;
        default:
            break;
    }
}

在这个方法中,我们首先获取当前上下文的引用,然后设置线宽和描边颜色。使用 switch 语句根据形状类型跳转到相应的代码。对于 kLineShape ,我们告诉图形上下文从用户首次触摸的位置开始创建路径,然后绘制一条线到用户最后触摸的位置,最后描边路径使线显示在屏幕上。

接下来,我们添加绘制矩形和椭圆的代码:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetLineWidth(context, 2.0);
    CGContextSetStrokeColorWithColor(context, self.currentColor.CGColor);
    CGContextSetFillColorWithColor(context, self.currentColor.CGColor);
    CGRect currentRect = CGRectMake(self.firstTouchLocation.x,
                                    self.firstTouchLocation.y,
                                    self.lastTouchLocation.x -
                                    self.firstTouchLocation.x,
                                    self.lastTouchLocation.y -
                                    self.firstTouchLocation.y);

    switch (self.shapeType) {
        case kLineShape:
            CGContextMoveToPoint(context,
                                 self.firstTouchLocation.x,
                                 self.firstTouchLocation.y);
            CGContextAddLineToPoint(context,
                                    self.lastTouchLocation.x,
                                    self.lastTouchLocation.y);
            CGContextStrokePath(context);
            break;
        case kRectShape:
            CGContextAddRect(context, currentRect);
            CGContextDrawPath(context, kCGPathFillStroke);
            break;
        case kEllipseShape:
            CGContextAddEllipseInRect(context, currentRect);
            CGContextDrawPath(context, kCGPathFillStroke);
            break;
        case kImageShape:
            break;
        default:
            break;
    }
}

因为我们想要绘制椭圆和矩形的轮廓并填充其内部,所以我们添加了一个调用 CGContextSetFillColorWithColor 来设置填充颜色。我们声明了一个 CGRect 变量 currentRect 来保存用户拖动所描述的矩形。对于矩形和椭圆,我们分别使用 CGContextAddRect CGContextAddEllipseInRect 将它们添加到上下文中,然后使用 CGContextDrawPath 进行绘制。

通过以上步骤,我们可以逐步实现一个功能丰富的绘图应用,用户可以选择不同的形状和颜色进行绘图。

使用Core Graphics进行绘图开发(续)

绘制图像

接下来,我们将添加绘制图像的代码到 drawRect: 方法中。在 switch 语句里添加 kImageShape 的处理逻辑。

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetLineWidth(context, 2.0);
    CGContextSetStrokeColorWithColor(context, self.currentColor.CGColor);
    CGContextSetFillColorWithColor(context, self.currentColor.CGColor);
    CGRect currentRect = CGRectMake(self.firstTouchLocation.x,
                                    self.firstTouchLocation.y,
                                    self.lastTouchLocation.x -
                                    self.firstTouchLocation.x,
                                    self.lastTouchLocation.y -
                                    self.firstTouchLocation.y);

    switch (self.shapeType) {
        case kLineShape:
            CGContextMoveToPoint(context,
                                 self.firstTouchLocation.x,
                                 self.firstTouchLocation.y);
            CGContextAddLineToPoint(context,
                                    self.lastTouchLocation.x,
                                    self.lastTouchLocation.y);
            CGContextStrokePath(context);
            break;
        case kRectShape:
            CGContextAddRect(context, currentRect);
            CGContextDrawPath(context, kCGPathFillStroke);
            break;
        case kEllipseShape:
            CGContextAddEllipseInRect(context, currentRect);
            CGContextDrawPath(context, kCGPathFillStroke);
            break;
        case kImageShape:
            if (self.image) {
                [self.image drawInRect:currentRect];
            }
            break;
        default:
            break;
    }
}

kImageShape 的处理中,我们首先检查 self.image 是否存在,如果存在则使用 drawInRect: 方法将图像绘制在由 currentRect 定义的矩形区域内。

总结与回顾

整个绘图应用的开发过程可以总结为以下几个关键步骤,下面用表格展示:
|步骤|操作内容|
| ---- | ---- |
|随机颜色生成|使用 arc4random() 函数为每个颜色分量生成随机浮点数,处理后创建新颜色并设置alpha值为1.0|
|定义应用常量|声明 ShapeType ColorTabIndex 两个枚举类型,对应形状和颜色选项|
|实现QuartzFunView骨架|在 QuartzFunView.h .m 文件中声明属性、重写初始化方法和添加触摸事件处理方法|
|创建和连接出口和动作|在 Main.storyboard 中添加分段控件,设置布局约束,连接出口和动作到 ViewController.m |
|实现动作方法|在 ViewController.m 中实现 changeColor: changeShape: 方法,根据用户选择设置颜色和形状|
|添加绘图代码|在 QuartzFunView.m drawRect: 方法中添加绘制线、矩形、椭圆和图像的代码|

下面是整个开发流程的mermaid流程图:

graph LR
    A[随机颜色生成] --> B[定义应用常量]
    B --> C[实现QuartzFunView骨架]
    C --> D[创建和连接出口和动作]
    D --> E[实现动作方法]
    E --> F[添加绘图代码]

通过以上一系列的操作,我们成功地使用Core Graphics开发了一个绘图应用。用户可以通过分段控件选择不同的形状(线、矩形、椭圆、图像)和颜色(红、蓝、黄、绿、随机)进行绘图。在开发过程中,我们利用了Core Graphics的强大功能,如获取图形上下文、设置线宽和颜色、绘制路径等。同时,通过处理用户的触摸事件,我们能够准确地记录用户的操作位置,从而实现根据用户的手势绘制相应的图形。

这个绘图应用不仅展示了Core Graphics在iOS开发中的应用,还体现了如何通过合理的代码结构和布局约束来实现一个功能丰富且用户体验良好的应用。开发者可以根据自己的需求进一步扩展这个应用,例如添加更多的形状和颜色选项,或者实现图形的编辑和保存功能等。

基于数据驱动的 Koopman 算子的递归神经网络模型线性化,用于纳米定位系统的预测控制研究(Matlab代码实现)内容概要:本文围绕“基于数据驱动的 Koopman 算子的递归神经网络模型线性化,用于纳米定位系统的预测控制研究”展开,提出了一种结合数据驱动方法与Koopman算子理论的递归神经网络(RNN)模型线性化方法,旨在提升纳米定位系统的预测控制精度与动态响应能力。研究通过构建数据驱动的线性化模型,克服了传统非线性系统建模复杂、计算开销大的问题,并在Matlab平台上实现了完整的算法仿真与验证,展示了该方法在高精度定位控制中的有效性与实用性。; 适合人群:具备一定自动化、控制理论或机器学习背景的科研人员与工程技术人员,尤其是从事精密定位、智能控制、非线性系统建模与预测控制相关领域的研究生与研究人员。; 使用场景及目标:①应用于纳米级精密定位系统(如原子力显微镜、半导体制造设备)中的高性能预测控制;②为复杂非线性系统的数据驱动建模与线性化提供新思路;③结合深度学习与经典控制理论,推动智能控制算法的实际落地。; 阅读建议:建议读者结合Matlab代码实现部分,深入理解Koopman算子与RNN结合的建模范式,重点关注数据预处理、模型训练与控制系统集成等关键环节,并可通过替换实际系统数据进行迁移验证,以掌握该方法的核心思想与工程应用技巧。
基于粒子群算法优化Kmeans聚类的居民用电行为分析研究(Matlb代码实现)内容概要:本文围绕基于粒子群算法(PSO)优化Kmeans聚类的居民用电行为分析展开研究,提出了一种结合智能优化算法与传统聚类方法的技术路径。通过使用粒子群算法优化Kmeans聚类的初始聚类中心,有效克服了传统Kmeans算法易陷入局部最优、对初始值敏感的问题,提升了聚类的稳定性和准确性。研究利用Matlab实现了该算法,并应用于居民用电数据的行为模式识别与分类,有助于精细化电力需求管理、用户画像构建及个性化用电服务设计。文档还提及相关应用场景如负荷预测、电力系统优化等,并提供了配套代码资源。; 适合人群:具备一定Matlab编程基础,从事电力系统、智能优化算法、数据分析等相关领域的研究人员或工程技术人员,尤其适合研究生及科研人员。; 使用场景及目标:①用于居民用电行为的高效聚类分析,挖掘典型用电模式;②提升Kmeans聚类算法的性能,避免局部最优问题;③为电力公司开展需求响应、负荷预测和用户分群管理提供技术支持;④作为智能优化算法与机器学习结合应用的教学与科研案例。; 阅读建议:建议读者结合提供的Matlab代码进行实践操作,深入理解PSO优化Kmeans的核心机制,关注参数设置对聚类效果的影响,并尝试将其应用于其他相似的数据聚类问题中,以加深理解和拓展应用能力。
在大数据技术快速发展的背景下,网络爬虫已成为信息收集与数据分析的关键工具。Python凭借其语法简洁和功能丰富的优势,被广泛用于开发各类数据采集程序。本项研究“基于Python的企查查企业信息全面采集系统”即在此趋势下设计,旨在通过编写自动化脚本,实现对企查查平台所公示的企业信用数据的系统化抓取。 该系统的核心任务是构建一个高效、可靠且易于扩展的网络爬虫,能够模拟用户登录企查查网站,并依据预设规则定向获取企业信息。为实现此目标,需重点解决以下技术环节:首先,必须深入解析目标网站的数据组织与呈现方式,包括其URL生成规则、页面HTML架构以及可能采用的JavaScript动态渲染技术。准确掌握这些结构特征是制定有效采集策略、保障数据完整与准确的前提。 其次,针对网站可能设置的反爬虫机制,需部署相应的应对方案。例如,通过配置模拟真实浏览器的请求头部信息、采用多代理IP轮换策略、合理设置访问时间间隔等方式降低被拦截风险。同时,可能需要借助动态解析技术处理由JavaScript加载的数据内容。 在程序开发层面,将充分利用Python生态中的多种工具库:如使用requests库发送网络请求,借助BeautifulSoup或lxml解析网页文档,通过selenium模拟浏览器交互行为,并可基于Scrapy框架构建更复杂的爬虫系统。此外,json库用于处理JSON格式数据,pandas库则协助后续的数据整理与分析工作。 考虑到采集的数据规模可能较大,需设计合适的数据存储方案,例如选用MySQL或MongoDB等数据库进行持久化保存。同时,必须对数据进行清洗、去重与结构化处理,以确保其质量满足后续应用需求。 本系统还需包含运行监控与维护机制。爬虫执行过程中可能遭遇网站结构变更、数据格式调整等意外情况,需建立及时检测与自适应调整的能力。通过定期分析运行日志,评估程序的效率与稳定性,并持续优化其性能表现。 综上所述,本项目不仅涉及核心爬虫代码的编写,还需在反爬应对、数据存储及系统维护等方面进行周密设计。通过完整采集企查查的企业数据,该系统可为市场调研、信用评价等应用领域提供大量高价值的信息支持。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值