在 iOS 中沿路径均匀地绘制图像

Posted

技术标签:

【中文标题】在 iOS 中沿路径均匀地绘制图像【英文标题】:Draw images evenly spaced along a path in iOS 【发布时间】:2011-08-05 03:42:17 【问题描述】:

我想要做的是在屏幕上移动我的手指 (touchesMoved),然后沿着 touchesMoved 生成的点绘制均匀分布的图像(可能是 CGImageRefs)。我可以画线,但我想要生成的是看起来像这样的东西(对于这个例子,我使用的是箭头的图像,但它可以是任何图像,可以是我的狗的照片:))主要是以在 iPhone 或 iPad 上用手指绘图时获得均匀分布的图像。

【问题讨论】:

【参考方案1】:

首先,巨大的道具送给肯德尔。因此,根据他的回答,这里是获取 UIImage 的代码,根据触摸之间的距离沿路径(不是真正的 pathRef,只是由点创建的逻辑路径)在屏幕上绘制它,然后旋转图像正确地基于当前点和先前点的向量。我希望你喜欢它:

首先,您需要反复加载要用作 CGImage 的图像:

NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"arrow.png" ofType:nil];
  UIImage *img = [UIImage imageWithContentsOfFile:imagePath];
  image = CGImageRetain(img.CGImage);

确保在你调用的 dealloc 中

 CGImageRelease(image);

然后在 touchesBegan 中,只需将起点存储在方法范围之外的 var 中(像这样在标题中声明它:)在这种情况下,我正在绘制 UIView

@interface myView : UIView 
    CGPoint lastPoint;
    
@end

然后开始接触:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

UITouch *touch = [touches anyObject];
lastPoint = [touch locationInView:self];

最后在 touchesMoved 中,将位图绘制到屏幕上,然后当您的距离移动足够大时(在我的情况下是 73,因为我的图像是 73 像素 x 73 像素)将该图像绘制到屏幕上,保存新图像并设置 lastPoint 等于 currentPoint

     -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
    

        UITouch *touch = [touches anyObject];   
        currentPoint = [touch locationInView:self];

        double deltaX = lastPoint.x - currentPoint.x;
        double deltaY = lastPoint.y - currentPoint.y;

        double powX = pow(deltaX,2);
        double powY = pow(deltaY,2);

        double distance = sqrt(powX + powY);
        if (distance >= 73)

            lastPoint = currentPoint;

             UIGraphicsBeginImageContext(self.frame.size);
            [drawImage.image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
            CGContextSaveGState(UIGraphicsGetCurrentContext());

            float angle = atan2(deltaX, deltaY);
            angle *= (M_PI / 180);


            CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(currentPoint.x, currentPoint.y, 73, 73),[self CGImageRotatedByAngle:image angle:angle * -1]);
            CGContextRestoreGState(UIGraphicsGetCurrentContext());
             drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
            distance = 0;

        

    


    - (CGImageRef)CGImageRotatedByAngle:(CGImageRef)imgRef angle:(CGFloat)angle

    CGFloat angleInRadians = angle * (M_PI / 180);
    CGFloat width = CGImageGetWidth(imgRef);
    CGFloat height = CGImageGetHeight(imgRef);

    CGRect imgRect = CGRectMake(0, 0, width, height);
    CGAffineTransform transform = CGAffineTransformMakeRotation(angleInRadians);
    CGRect rotatedRect = CGRectApplyAffineTransform(imgRect, transform);

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef bmContext = CGBitmapContextCreate(NULL,
                                                   rotatedRect.size.width,
                                                   rotatedRect.size.height,
                                                   8,
                                                   0,
                                                   colorSpace,
                                                   kCGImageAlphaPremultipliedFirst);
    CGContextSetAllowsAntialiasing(bmContext, FALSE);
    CGContextSetInterpolationQuality(bmContext, kCGInterpolationNone);
    CGColorSpaceRelease(colorSpace);
    CGContextTranslateCTM(bmContext,
                          +(rotatedRect.size.width/2),
                          +(rotatedRect.size.height/2));
    CGContextRotateCTM(bmContext, angleInRadians);
    CGContextTranslateCTM(bmContext,
                          -(rotatedRect.size.width/2),
                          -(rotatedRect.size.height/2));
    CGContextDrawImage(bmContext, CGRectMake(0, 0,
                                             rotatedRect.size.width,
                                             rotatedRect.size.height),
                       imgRef);

    CGImageRef rotatedImage = CGBitmapContextCreateImage(bmContext);
    CFRelease(bmContext);
    [(id)rotatedImage autorelease];

    return rotatedImage;

这将创建一个看起来像这样的图像:

要添加以下内容(对上面的代码进行一些更改,以便尝试填充 touchesMoved 在您快速移动时缺少一些点的空白:

CGPoint point1 = CGPointMake(100, 200);

CGPoint point2 = CGPointMake(300, 100);



double deltaX = point2.x - point1.x;
double deltaY = point2.y - point1.y;

double powX = pow(deltaX,2);
double powY = pow(deltaY,2);

double distance = sqrt(powX + powY);

distance = 0;


for (int j = 1; j * 73 < distance; j++ )

    double x =  (point1.x + ((deltaX / distance) * 73 * j));
    double y =  (point1.y + ((deltaY / distance) * 73 * j));


    NSLog(@"My new point is x: %f y :%f", x, y);

【讨论】:

要添加以下内容(对上面的代码进行一些更改,以便尝试填补 touchesMoved 在快速移动时缺少一些点的空白: 感谢您的帮助,我如何绘制 x 和 y 点请建议我 为什么在for循环前的最低码框里设置distance=0?最底部的代码框如何帮助快速运动?【参考方案2】:

假设您已经有代码在用户在屏幕上移动触摸时跟踪他们的触摸,听起来您想检测他们何时移动了等于图像长度的距离,此时您想要在他们的触摸下绘制您的图像的另一个副本。

要实现这一点,我认为您需要:

计算图片的长度(宽度) 实现代码以将图像副本绘制到视图上,并旋转到任意角度 每次用户的触摸移动时(例如在 touchesMoved 中:): 计算每次触摸的增量并生成该增量的“长度”(例如 sqrt(dx^2 + dy^2)) 累积距​​离上一张图片绘制后的距离 如果距离已达到图像的长度,则在触摸的当前位置下绘制图像的副本,并适当旋转(可能根据从最后一张图像的位置到当前位置的矢量)

听起来怎么样?

【讨论】:

让我试一试。我从来没有想过要根据距离放置图像。我在想我需要在路径上添加或调整点。 正是我正在寻找的,当我清理它时将发布工作代码。 (你不知道如何计算一个合适的向量吗:)) 在实现这一点并发布您的代码方面做得很好。看起来您需要稍微偏移绘图以使箭头的尖端指向下一个的底部。尝试计算另一个向量,这次从图像的方向旋转 90 度,长度等于图像宽度的一半,然后将该向量添加到 currentPoint.x 和 .y 中,看看它的外观。 我几乎是 Forest Gumped 穿过矢量的东西。那么图像方向是当前角度吗?还是我创建一个任意点,距离我的图像宽度的一半,但在右侧 90 度......好吧,你看我对向量的理解只有 45 分钟 :) 是的!正如您所知道的,“矢量”只是指 (x, y) 位置,所以现在您需要做的就是找到正确的偏移量,以使您的一系列箭头看起来平滑。我没有对此进行测试,但在我看来,将每个箭头“左”或“右”移动其宽度的一半应该可以很好地对齐它们,这就是 90 度旋转的来源:“左”/“右” shift 必须相对于箭头的方向。

以上是关于在 iOS 中沿路径均匀地绘制图像的主要内容,如果未能解决你的问题,请参考以下文章

如何在qml中沿特定路径剪辑图像

在 SwiftUI 中沿路径填充颜色渐变

我想用以下路径绘制网格&用户可以填充它,如 ios 中的图像所示

在 3d 中沿贝塞尔路径移动对象:旋转问题

从简单的图像 ios 创建贝塞尔路径

如何沿 x 轴均匀地绘制日期数据?