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

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(使用前将#替换为@)

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

  • 修改 SCNParticleEventBlock 中的 SCNParticleSystem 颜色不起作用

    鉴于提供的示例代码handle forProperties handler https developer apple com documentation scenekit scnparticlesystem 1523251 handle
  • 在 iOS 中发送音频文件和 JSON 字符串

    我正在尝试将音频文件和 JSON 字符串发送到 PHP 服务器 使用以下代码将发送 JSON 但文件到达时为空 如果我将 JSON 块放在音频块下方 文件将完全到达 但 JSON 为空 有什么想法吗 void sendFile NSStri
  • iOS - UIBarButtonItem - 后退按钮标题垂直位置不调整

    我正在设置自定义字体UIBarButtonItem对象使用UIAppearance 这工作正常并正确设置字体 但是 我确实需要调整按钮标题的垂直位置以适应新字体的大小 UIBarButtonItem appearance setTitleT
  • UIWebView:在 Safari 中打开一些链接,一些在视图中打开

    我的应用程序具有在 UIWebView 中呈现的内容 出于文本格式原因 内容中有一些链接 其中一些链接应在移动 Safari 中打开其目标 而其他链接则应在内容中导航 到目前为止 我已经使用 UIWebView 委托捕获了链接请求 在我的实
  • CSS 安全区域属性在 iPhone X 上不起作用

    就我而言 我正在 iPhone X 上运行一个 Web 应用程序 我尝试在顶部添加一个填充 以使用 Webkit 的安全区域 css 属性将我的身体推到安全区域padding top constant safe area inset top
  • iOS8 自签名证书已安装但仍不受信任

    由于我无法控制的原因 我需要使用自签名证书针对平台进行 iOS 开发 它是一个在 SAN 中具有特定 IP 地址的根证书 当证书安装在 OSX 系统帐户下时 所有浏览器现在将正常信任对给定 IP 地址的任何访问 通过电子邮件将同一证书发送到
  • Objective c RSA 与 OAEP 填充 sha256 之前的 ios 10

    我正在研究一种在iPhone中使用RSA加密方法的加密方法 到目前为止我可以用这种方法实现获取加密字符串 该字符串被服务器成功解密 SecKeyRef keyRef self addPublicKey pubKey SecKeyAlgori
  • 以编程方式更改自动布局约束后视图未更新

    事先我必须说 我实际上得到了我想要的可见效果 但不是以令人满意的方式 因为现在需要 打破 约束而不是正确更新 我有一个 ViewController 其中包含一个 UITableView 那个的高度tableView可以从 0 不可见 到它
  • 尽早检测有问题的 XIB 视图

    我的笔尖名称有一个拼写错误 当我推向导航控制器时 它在代码中被破坏了 弄清楚它并没有花太长时间 但我认为最好尽早断言格式良好 以便更容易弄清楚 问题是它不是零 它只是无法从笔尖正确地形成自己 在 initWithNib 之后是否有更好的断言
  • ios GPUImage,小尺寸图像处理效果不好?

    我正在尝试为 OCR 准备图像 我使用 GPUImage 来完成 代码工作正常 直到我裁剪图像 裁剪后我得到了糟糕的结果 作物面积 https www dropbox com s e3mlp25sl6m55yk IMG 0709 PNG h
  • iPad 横向框架宽度和高度混合

    我已经完成了这个问题所说的 横向模式仅适用于 iPhone 或 iPad https stackoverflow com questions 2647786 landscape mode only for iphone or ipad 但v
  • Swift 字典映射 - 闭包中的 init

    我有 Swift 字典 private var params String AnyObject 这包含查询项目 例如 lat 40 lon 100 我想将这本词典映射到NSURLQueryItem大批 我想让它 迅速 params map
  • 在 Swift 中计算两个 CLLocation 点之间的方位角 [重复]

    这个问题在这里已经有答案了 我正在尝试计算仅 swift 代码中两个 CLLocation 点之间的方位 我遇到了一些困难 并假设这是一个非常简单的函数 堆栈溢出似乎没有列出任何内容 func d2r degrees Double gt D
  • 如何动态添加XCTestCase

    我正在为一个白标签项目编写 UI 测试 其中每个应用程序都有一组不同的菜单项 测试点击每个菜单项并截取屏幕截图 使用快车道快照 https docs fastlane tools actions snapshot 目前这一切都发生在一个内部
  • Textview 中心文本对齐 IOS 7

    void observeValueForKeyPath NSString keyPath ofObject id object change NSDictionary change context void context NSLog He
  • 块如何捕获其封闭范围之外的变量?

    我知道 Objective C 块可以捕获并设置其封闭范围之外的变量值 它是如何做到的 它实际上相当简单 并在 Clang 的块实现规范中进行了描述 在 导入变量 http clang llvm org docs Block ABI App
  • 在 OSX 上检测 Objective C 或 C++ 中的文件夹访问(如 fs_usage 命令)

    我正在 OSX 上开发实时病毒扫描程序 OSX 的命令行命令fs usage可以通过以下方式确定文件夹访问权限 并且只能以 root 用户身份运行 fs usage w f pathname grep Users Documents Use
  • 如何顺序访问字典?

    我想以元素存储在字典中的方式访问字典 有人可以帮我做这件事吗 提前致谢 字典将其值存储在由键或更精确地由键的哈希值索引的结构中 这就是他们速度很快的原因 他们不需要搜索值 他们只需获取键的值并直接查找值 在大多数情况下 只有在发生冲突的键哈
  • iOS 上的推送通知渐进式 Web 应用程序

    我需要开发一个集成了推送通知的渐进式网络应用程序 在网上搜索我发现了关于这个主题的不同意见 如果我理解正确的话 目前我们无法在移动版 safari 中推送通知 但仅限桌面版 这样对吗 你有什么建议来获得相同的结果吗 我不是iOS专家 我想知
  • 为什么我收到 com.facebook.sdk.login 错误 308?

    我正在使用 Xcode 7 0 在 iOS 9 0 2 上进行测试并使用 Facebook SDK 4 7 0 当我登录用户时 大多数时候一切都正常 但有时我不断收到此错误 但我不知道为什么 操作无法完成 com facebook sdk

随机推荐

  • 如何在 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