沿着贝塞尔曲线路径放置图像

2024-01-09

有谁知道如何沿着贝塞尔路径放置图像?我可以很好地编写路径并沿着路径为精灵设置动画,但我想让路径成为一系列箭头而不是虚线。我认为一定有一种方法可以在整个路径上添加箭头图像,但找不到它。而且路径都是弯曲的:

UIBezierPath * path = [UIBezierPath bezierPath];
[path moveToPoint:startingPoint];
[path addCurveToPoint:endPoint controlPoint1:controlPoint1 controlPoint2:controlPoint2];

我猜你想要这样的东西:

你可以找到我完整的演示应用程序项目在这个 github 存储库中 https://github.com/mayoff/arrows-along-UIBezierPath.

不管怎样,这是一个有趣的小问题。

您需要沿路径生成一系列点,我假设您希望它们等距。生成这些点并非易事。

幸运的是,Core Graphics 包含一个可以为您完成此操作的函数,但不清楚是哪个函数。函数是CGPathCreateCopyByDashingPath.

首先,我们来做一个UIBezierPath创建虚线副本的类别:

UIBezierPath+Rob_dash.h

#import <UIKit/UIKit.h>

@interface UIBezierPath (Rob_dash)

- (instancetype)Rob_dashedPathWithPattern:(NSArray *)pattern phase:(CGFloat)phase;

@end

UIBezierPath+Rob_dash.m

#import "UIBezierPath+Rob_dash.h"

@implementation UIBezierPath (Rob_dash)

- (instancetype)Rob_dashedPathWithPattern:(NSArray *)pattern phase:(CGFloat)phase {
    CGFloat lengths[pattern.count];
    size_t i = 0;
    for (NSNumber *number in pattern) {
        lengths[i++] = number.doubleValue;
    }
    CGPathRef dashedCGPath = CGPathCreateCopyByDashingPath(self.CGPath, NULL, phase, lengths, pattern.count);
    UIBezierPath *dashedPath = [self.class bezierPathWithCGPath:dashedCGPath];
    CGPathRelease(dashedCGPath);
    return dashedPath;
}

@end

一旦我们有了虚线路径,我们就需要枚举路径的元素(单个命令,例如moveToPoint:, addLineToPoint:, 等等)。唯一的方法是使用另一个 Core Graphics 函数CGPathApply。我们再写一个UIBezierPath使用块来使其更容易的类别。这个有点长:

UIBezierPath+Rob_forEach.h

#import <UIKit/UIKit.h>

typedef void (^Rob_UIBezierPath_moveBlock)(CGPoint destination);
typedef void (^Rob_UIBezierPath_lineBlock)(CGPoint destination);
typedef void (^Rob_UIBezierPath_quadBlock)(CGPoint control, CGPoint destination);
typedef void (^Rob_UIBezierPath_cubicBlock)(CGPoint control0, CGPoint control1, CGPoint destination);
typedef void (^Rob_UIBezierPath_closeBlock)(void);

@interface UIBezierPath (Rob_forEach)

- (void)Rob_forEachMove:(Rob_UIBezierPath_moveBlock)moveBlock line:(Rob_UIBezierPath_lineBlock)lineBlock quad:(Rob_UIBezierPath_quadBlock)quadBlock cubic:(Rob_UIBezierPath_cubicBlock)cubicBlock close:(Rob_UIBezierPath_closeBlock)closeBlock;

@end

UIBezierPath+Rob_forEach.m

#import "UIBezierPath+Rob_forEach.h"

struct ForEachBlocks {
    __unsafe_unretained Rob_UIBezierPath_moveBlock moveBlock;
    __unsafe_unretained Rob_UIBezierPath_lineBlock lineBlock;
    __unsafe_unretained Rob_UIBezierPath_quadBlock quadBlock;
    __unsafe_unretained Rob_UIBezierPath_cubicBlock cubicBlock;
    __unsafe_unretained Rob_UIBezierPath_closeBlock closeBlock;
};

static void applyBlockToPathElement(void *info, const CGPathElement *element) {
    struct ForEachBlocks *blocks = info;
    switch (element->type) {
        case kCGPathElementMoveToPoint:
            if (blocks->moveBlock != nil) {
                blocks->moveBlock(element->points[0]);
            }
            break;
        case kCGPathElementAddLineToPoint:
            if (blocks->lineBlock != nil) {
                blocks->lineBlock(element->points[0]);
            }
            break;
        case kCGPathElementAddQuadCurveToPoint:
            if (blocks->quadBlock) {
                blocks->quadBlock(element->points[0], element->points[1]);
            }
            break;
        case kCGPathElementAddCurveToPoint:
            if (blocks->cubicBlock) {
                blocks->cubicBlock(element->points[0], element->points[1], element->points[2]);
            }
            break;
        case kCGPathElementCloseSubpath:
            if (blocks->closeBlock) {
                blocks->closeBlock();
            }
            break;
    }
}

@implementation UIBezierPath (Rob_forEach)

- (void)Rob_forEachMove:(Rob_UIBezierPath_moveBlock)moveBlock line:(Rob_UIBezierPath_lineBlock)lineBlock quad:(Rob_UIBezierPath_quadBlock)quadBlock cubic:(Rob_UIBezierPath_cubicBlock)cubicBlock close:(Rob_UIBezierPath_closeBlock)closeBlock {
    struct ForEachBlocks blocks = {
        .moveBlock = moveBlock,
        .lineBlock = lineBlock,
        .quadBlock = quadBlock,
        .cubicBlock = cubicBlock,
        .closeBlock = closeBlock
    };
    CGPathApply(self.CGPath, &blocks, applyBlockToPathElement);
}

@end

好的,现在我们想一起使用这两个类别来虚线路径,然后沿着虚线行走并在每个虚线末尾发出点。请注意,虚线可能由多个连续的直线/曲线段组成。我们需要观察移动命令以了解破折号何时结束。此外,为了以正确的角度绘制每个箭头,我们需要知道曲线在每个点的正切,因此我们也将其计算为单位向量。对于直线段,切向量平行于线段。对于曲线,紧邻曲线端点之前的控制点确定端点处的切线。

UIBezierPath+Rob_points.h

#import <UIKit/UIKit.h>

@interface UIBezierPath (Rob_points)

- (void)Rob_forEachPointAtInterval:(CGFloat)interval perform:(void (^)(CGPoint point, CGVector vector))block;

@end

UIBezierPath+Rob_points.m

#import "UIBezierPath+Rob_points.h"
#import "UIBezierPath+Rob_dash.h"
#import "UIBezierPath+Rob_forEach.h"
#import <tgmath.h>

static CGVector vectorFromPointToPoint(CGPoint tail, CGPoint head) {
    CGFloat length = hypot(head.x - tail.x, head.y - tail.y);
    return CGVectorMake((head.x - tail.x) / length, (head.y - tail.y) / length);
}

@implementation UIBezierPath (Rob_points)

- (void)Rob_forEachPointAtInterval:(CGFloat)interval perform:(void (^)(CGPoint, CGVector))block {
    UIBezierPath *dashedPath = [self Rob_dashedPathWithPattern:@[ @(interval * 0.5), @(interval * 0.5) ] phase:0];
    __block BOOL hasPendingSegment = NO;
    __block CGPoint pendingControlPoint;
    __block CGPoint pendingPoint;
    [dashedPath Rob_forEachMove:^(CGPoint destination) {
        if (hasPendingSegment) {
            block(pendingPoint, vectorFromPointToPoint(pendingControlPoint, pendingPoint));
            hasPendingSegment = NO;
        }
        pendingPoint = destination;
    } line:^(CGPoint destination) {
        pendingControlPoint = pendingPoint;
        pendingPoint = destination;
        hasPendingSegment = YES;
    } quad:^(CGPoint control, CGPoint destination) {
        pendingControlPoint = control;
        pendingPoint = destination;
        hasPendingSegment = YES;
    } cubic:^(CGPoint control0, CGPoint control1, CGPoint destination) {
        pendingControlPoint = control1;
        pendingPoint = destination;
        hasPendingSegment = YES;
    } close:nil];
    if (hasPendingSegment) {
        block(pendingPoint, vectorFromPointToPoint(pendingControlPoint, pendingPoint));
    }
}

@end

现在我们可以找到沿路径的点,以及每个点的单位切向量。让我们创建一个使用此功能的自定义视图drawRect::

ArrowView.h

#import <UIKit/UIKit.h>

@interface ArrowView : UIView

@property (nonatomic) CGFloat interval;

@end

ArrowView.m

#import "ArrowView.h"
#import "UIBezierPath+Rob_figureEight.h"
#import "UIBezierPath+Rob_points.h"

@implementation ArrowView

- (void)setInterval:(CGFloat)interval {
    _interval = interval;
    [self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect {
    UIImage *arrow = [UIImage imageNamed:@"right233.png"];
    UIBezierPath *path = [UIBezierPath Rob_figureEightInRect:CGRectInset(self.bounds, 40, 40)];
//    [path stroke];
    [path Rob_forEachPointAtInterval:self.interval perform:^(CGPoint point, CGVector vector) {
        CGContextRef gc = UIGraphicsGetCurrentContext();
        CGContextSaveGState(gc); {
            CGContextTranslateCTM(gc, point.x, point.y);
            CGContextConcatCTM(gc, CGAffineTransformMake(vector.dx, vector.dy, -vector.dy, vector.dx, 0, 0));
            CGContextTranslateCTM(gc, -0.5 * arrow.size.width, -0.5 * arrow.size.height);
//            UIRectFrame((CGRect){ CGPointZero, arrow.size });
            [arrow drawAtPoint:CGPointZero];
        } CGContextRestoreGState(gc);
    }];
}

@end

如果您想沿着路径绘制箭头图像,这就是全部内容。

我的演示应用程序存储库中有一点好处。如果你回到第一次提交,我还实现了一个不同的解决方案:一个采用路径并“箭头化”它的类别,在每个子路径的末尾放置一个箭头。如果将其与破折号结合起来(就像我在该项目版本中所做的那样),您会得到沿路径的箭头。但它最终看起来不如使用箭头图像那么好。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

沿着贝塞尔曲线路径放置图像 的相关文章

  • 编程界面方向更改不适用于 iOS

    因此 我有一个项目 当用户按下按钮时 我需要强制改变方向 我创建了一个github 上的示例应用程序 https github com vradenburg ForcedOrientationChange来演示这个问题 interface
  • 如何从 iOS 本机共享功能中删除某些选项? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在开发一款 iOS 游戏 想知道如何删除 iOS 在自动共享时提供的一些本机选项 例如 分配给联系人 和 打印 我对此做了很多研究 我
  • 确定显示的视图是被推送还是来自导航栏中的后退按钮

    有没有办法判断新控制器是来自导航后退按钮还是被推入堆栈 我只想重新加载数据以推送导航堆栈 而不是按下后退按钮 从 iOS 5 0 开始 您可以执行以下操作 void viewWillAppear BOOL animated super vi
  • 我可以提高当前模态视图控制器的动画速度吗?

    我正在编写一个绘图应用程序 当用户单击工具栏中的项目时 该应用程序会显示工具视图控制器 然而 我的几位 Beta 测试人员报告说 工具面板打开速度太慢 我使用标准的 presentModalViewController animated 调
  • ScrollView 内的 SwiftUI 列表

    我想将列表放在 ScrollView 中 以便可以一起滚动列表行和标题 但我发现 ScrollView 中的 List 不起作用 它什么也没显示 我应该同时使用它们 我应该使用 ScrollView 这样当我滚动行时 我可以使标题 图像或文
  • 在 iOS 7 上需要具有非半透明栏的 WantsFullScreenLayout 行为

    我希望能够让我的视图覆盖整个屏幕 位于导航栏和工具栏后面 并将导航栏和工具栏的半透明属性设置为NO 我想要不透明的条 在 iOS 7 之前 这很容易通过设置来完成wantsFullScreenLayout是 然而 在 iOS 7 中 我找不
  • 具有 3D 旋转功能的 CAEmitterCell

    我正在尝试使用 CAEmitterLayer 和 CAEmitterCell 重现从顶部落下的小纸片效果 到目前为止 我得到了它的 2D 动画 但是我很难让每个单元格在掉落时旋转 如何对每个粒子应用随机旋转 我尝试过 3D Transfor
  • 带有 HTML 的 UIActivityViewController

    当我将包含 HTML 内容的字符串作为数组元素传递给UIActivityViewController initWithActivityItems方法中 它不会在选定的 共享 控制器中将其呈现为 HTML 而是呈现 HTML 源 即标记而不是
  • Objective-c:如何检测视图上的双击?

    我正在开发一个应用程序 其中视图上有多个控件 但我想在用户双击视图时启用它们 您可以以双击为例 但在设备中我想在双击时捕获事件 您需要添加一个UITapGestureRecognizer到您想要点击的视图 像这样 void viewDidL
  • 在 iOS 上提升 Asio SSL 认证

    我正在尝试在 iOS 上使用 Boost Asio 并且已经解决了所有问题 但如何检查我正在连接的服务器的证书 如何使用 Boost Asio 在 iOS 中检查连接服务器的证书 在我的另一个答案中 您可以看到一个简单的 SSL 客户端 在
  • 如何在当前视图上方创建半透明视图层?

    您可能以前见过这种情况 它在 ScoutMob 等消费时尚应用程序中变得非常流行 我正在尝试在启动时实现 60 的透明视图 该视图将覆盖我的主屏幕 解释如何使用主屏幕的功能并在点击时消失 我已经完成了整个应用程序 它从几年前就开始使用 xi
  • Sqlite3 查询在 iOS 8.2 下变得非常慢

    我制作了一个可以在应用程序内使用 sqlite 数据库的应用程序 在 iOS 8 2 之前 它工作得很好 但更新后 查询方法的工作速度慢了大约 100 倍 我试图查找有关此问题的信息 但尚未找到任何信息 有人有同样的经历吗 这是我的方法 到
  • Cordova 构建 iOS 应用程序并打开模拟器,但未安装

    我正在 MacOS 上用 Cordova 制作一个 iOS 应用程序 当我跑步时cordova emulate ios该应用程序会构建并打开 iPhone X iOS 11 4 的模拟器 但它从未在模拟器中安装该应用程序 它仅显示模拟器主屏
  • Objective C - 后台执行选择器并且分离新线程选择器?

    detachNewThreadSelector 和performSelectorInBackground 都用于在后台调用方法 2种方法有什么区别吗 或者它们的工作方式相同吗 它们本质上是相同的 但范式略有不同 他们在幕后做着完全相同的事情
  • 在 UIScrollView Swift 中禁用垂直滚动

    不要将此问题标记为重复 我读了很多问题 但没有找到问题的答案 我的问题如下 我有一个UIScrollView它应该只水平滚动 这里它垂直滚动 20px 奇怪的事实 它似乎与状态栏的高度相同 我尝试打印内容大小 y 偏移量和高度UIScrol
  • 如何在 iOS 上反转使用 CoreGraphics 渲染的 pdf 上的文本颜色?

    我正在使用开源 PDF 查看库 VFR PDF Readerhttps github com vfr Reader https github com vfr Reader 我正在尝试实现 夜间模式 或黑色背景与白色文本 我可以将背景设置为我
  • 如何在iOS中像Android一样实现菜单

    我需要为 iOS 实现菜单 例如android menu表示导航按钮栏 右侧 如果我点击该菜单就会显示 我正在使用故事板 所以你想要这样的东西 甚至可以在风景中使用 我想我要花很长时间来解释 D 基本上我创建了一个自定义 UINavigat
  • Measure 应用程序是否有可用的 URL 方案?

    我想推出新的测量应用程序 在 iOS 12 上 在我的应用程序中 这可能吗 应用程序是否有可用的 URL 方案可用于此目的 或者还有其他方法可以做到吗 Thanks 您似乎无法打开此应用程序 In the 文档存档 https develo
  • 不同形状的图像拼贴

    我对照片编辑应用程序开发完全陌生 我如何开发这样的网格样式 以便我可以添加从相机胶卷中选取的多个图像来创建拼贴画 我浏览了下面的链接 但没有找到实现这一目标的方法 任何建议或链接都 会有帮助 LINK1 https stackoverflo
  • 归档时出现“没有此类模块”错误

    我的 Swift 4 应用程序在所有模拟器上重建并成功运行 除了Generic iOS Device 如果我尝试将其存档或重建Generic iOS Device 我得到一个No such module与我的 Pod 框架之一相关的错误 我

随机推荐

  • 如何在 Angular 中模拟 HTTP 请求?

    我检查了很多文章和答案 但似乎没有找到正确的方法来模拟HTTP Requests对于我的方法 我想测试我的frontend应用程序独立于backend 这是我拥有的方法类型 private getProfile this http get
  • 如何在每次启动时运行我自己的脚本

    我有一个问题 如何在 Ubuntu 中每次启动时运行自己的 bash 脚本 假设我有一个正在执行特定类型工作的脚本 现在我希望它在启动 Ubuntu 系统时自动运行 你应该学习如何使用暴发户 看this http upstart ubunt
  • 从 F# 中使用“params”调用泛型函数 (Observable.StartWith)

    Edit 请注意 正如 Daniel 和 latkin 在下面的回答和评论中指出的那样 这个问题涉及 F 中的一个错误 该错误似乎已于 2014 年初修复 我正在尝试为 Observable StartWith 编写一个咖喱包装器 我正在使
  • WordPress 中的 woocommerce 返回始终与产品类型一样简单

    我尝试获取分组产品的类型 但如果我使用 WC Product Factory woocommerce 返回空或始终 简单 当我使用时 the product new WC Product 2886 echo the product gt p
  • django send_mail() 函数需要几分钟

    我正在尝试在views py 文件中的函数中发送电子邮件 我已按照与此处相同的方式在我的设置文件中设置了电子邮件 Python Django Gmail SMTP 设置 https stackoverflow com questions 1
  • Java 1.5 命令行密码屏蔽

    All 我们的服务器正在运行 Java 1 5 我在尝试屏蔽来自命令行的用户输入时遇到了困难 我正在执行一个 jar 文件 java jar my jar 并通过 printlns 通过命令行提示符进行工作 我无法使用 Java 控制台 T
  • CDI 将服务注入 JPA 托管实体

    我确信这与这个问题 https stackoverflow com q 8512628 206466但是关于这个问题的OP有一些我不确定对DI是否有意义的场景 所以这就是我的理解 尝试将 JPA 实体与 CDI Bean 混合通常不是一个好
  • 图像 getWidth 和 getHeight 不适当地返回 -1

    为什么会这样 URL url MinecraftPlatformGame class getResource images diamondPick png image Toolkit getDefaultToolkit getImage u
  • Jquery 检查值是否为数字

    我想知道是否有人有一个快速而肮脏的 jquery 方法来检查一个值是否是数字 我正在考虑使用正则表达式类型方法来检查值 如果没有 则不要提交表单 我正在使用 jquery 验证 但我在加载 jquery 验证时遇到了问题 我只有一个值 我想
  • Laravel 创建具有两个时间戳列的表时出错

    我在 Laravel 6 6 中创建了一个具有以下定义的表 public function up Schema create quarters function Blueprint table table gt integer quarte
  • 我试图在地图视图上放置多个图钉,但出现错误

    for int i 0 i lt self businessArray count i Business business self businessArray objectAtIndex i MapAnnotation mapAnnota
  • 物理设备无法在 Android Studio 中工作:多个 RSA 密钥指纹,但只有一个 adbkey.pub

    我使用 Pixel 2xl 和 MacBook Pro 进行开发 我有一个问题几个月来一直困扰着我 当我将手机连接到电脑时 我会看到经典的弹出窗口 询问 允许 USB 调试 计算机的 RSA 密钥指纹是 xx xx xx xx xx xx
  • C# 中属性声明中的“new”关键字

    我需要维护一个 NET 项目 我只是浏览代码 我在属性声明中注意到了这一点 public new string navUrl get return set 我想知道什么是new修改器对属性做什么 它隐藏了基类的 navUrl 属性 看新修改
  • 在已设置的图像 swift 4 之上加载图像的问题

    我的 cellForItemAtIndexPath 中遇到问题 我将图像设置为单元格的 UIButton 但每次滚动 collectionView 的单元格时 它都会一次又一次地将图像放置在已设置的图像之上 我可以看出 因为图像的阴影越来越
  • Msbuild ItemGroup 排除不适用于通配符

    该项目组ItemsFromAnotherTarget包含 References AnotherFolder ReferencedAssembly dll bin GeneratedAssembly1 dll bin GeneratedAss
  • React 原生导航 useTheme()

    我正在尝试直接从样式访问 useTheme 但到目前为止我的解决方案似乎不起作用 我没有返回错误 有办法做我想做的事吗 import StyleSheet from react native import useTheme from rea
  • 如何测试 RSpec 中是否调用方法但不覆盖返回值

    已经有问题 https stackoverflow com questions 21262309 rspec test to see if a method was called与此类似 但它们都将返回值重写为nil unless and
  • JavaScript - 在 Safari 上执行代码之前可用的对象定义

    我只需要在页面加载时执行一次的对象和函数被包装在undefined检查物体 在我通常使用的 Windows Linux 上的 Chrome 上 代码运行得很好 即代码只执行一次 但在 iPad 和 MacBook 上的 Safari 上 未
  • 为什么我无法在 C# 中序列化元组? [复制]

    这个问题在这里已经有答案了 可能的重复 为什么 XML Serialized 类需要无参数构造函数 https stackoverflow com questions 267724 why xml serializable class ne
  • 沿着贝塞尔曲线路径放置图像

    有谁知道如何沿着贝塞尔路径放置图像 我可以很好地编写路径并沿着路径为精灵设置动画 但我想让路径成为一系列箭头而不是虚线 我认为一定有一种方法可以在整个路径上添加箭头图像 但找不到它 而且路径都是弯曲的 UIBezierPath path U