转换工程视图时,iOS图形出错

转换工程视图时,iOS图形出错,ios,uiview,drawrect,Ios,Uiview,Drawrect,我有一个自定义的UIView和UIPangestureRecograiser,用于捕获点和速度。我将这些点和速度存储在NSMutableArray中,然后使用数据创建UIBezierPath,然后将其添加到撤消/重做历史记录中 当用户在屏幕上移动手指时,撤销历史记录会不断添加新的路径,每个新路径都会被绘制到屏幕外的图形上下文中,剪裁到路径的大小,然后在UIView上绘制 我的问题是:现在我正在创建一个“收缩到缩放”功能,并对工程视图应用缩放和平移变换,放大时所做的任何绘图最终都会出现在错误的位置

我有一个自定义的UIView和UIPangestureRecograiser,用于捕获点和速度。我将这些点和速度存储在NSMutableArray中,然后使用数据创建UIBezierPath,然后将其添加到撤消/重做历史记录中

当用户在屏幕上移动手指时,撤销历史记录会不断添加新的路径,每个新路径都会被绘制到屏幕外的图形上下文中,剪裁到路径的大小,然后在UIView上绘制

我的问题是:现在我正在创建一个“收缩到缩放”功能,并对工程视图应用缩放和平移变换,放大时所做的任何绘图最终都会出现在错误的位置,即手指在屏幕上的向上和左侧位置,错误的尺寸会变小。在撤消并重做之前,您实际上无法看到正在绘制的内容。我认为屏幕外上下文或屏幕的错误矩形正在更新,或者由于转换不知道如何称呼,存储在撤消历史记录中的路径点具有不同的原点/参考点

这是我的第一个iOS应用程序,可能会有一些愚蠢的错误,我已经使用了许多不同的教程来达到这一点,但我仍然停留在变换如何影响路径上。以及如何确保以正确的比例在屏幕外上下文的正确位置绘制路径。我试过变换路径,变换点,试着反转变换,但我就是不明白

下面是代码,对数量表示歉意。我包括什么处理捕获点,使路径和绘图到屏幕。。。夹点识别器将放大工程视图,并将其转换为将其放大到夹点的中心

在ViewController中,我以整个屏幕的大小创建了绘图视图VelocityDrawer,并添加了手势识别器:

VelocityDrawer *slv = [[VelocityDrawer alloc] initWithFrame:CGRectMake(0,0,768,1024)];
slv.tag = 100;
drawingView = slv;

drawingView.delegate = self;
drawingView.currentPen = finePen;
然后在VelocityDrawer中初始化WithFrame:CGRectframe:

    self.undoHistory = [[NSMutableArray alloc] init];
    self.redoHistory = [[NSMutableArray alloc] init];

    // create offscreen context
    drawingContext = [self createOffscreenContext:frame.size];

    UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
    panGestureRecognizer.maximumNumberOfTouches = 1;
    [self addGestureRecognizer:panGestureRecognizer];

    UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
    tapGestureRecognizer.numberOfTapsRequired = 1;
    tapGestureRecognizer.numberOfTouchesRequired = 1;
    [self addGestureRecognizer:tapGestureRecognizer];

    [self clearHistoryBitmaps];
屏幕外上下文的创建方式如下:

- (CGContextRef) createOffscreenContext: (CGSize) size  {
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    float scaleFactor = [[UIScreen mainScreen] scale];

    // must use whole numbers so invalid context does not happen
    NSInteger sw = (NSInteger)(size.width * scaleFactor);
    NSInteger sh = (NSInteger)(size.height * scaleFactor);

    CGContextRef context = CGBitmapContextCreate(NULL,
                                                 sw,
                                                 sh,
                                                 8,
                                                 sw * 4,
                                                 colorSpace,
                                                 (CGBitmapInfo)kCGImageAlphaPremultipliedLast);
    CGColorSpaceRelease(colorSpace);

    CGContextScaleCTM(context, scaleFactor, scaleFactor);
    return context;
}
在handlePanGesture。我捕获点,根据手指速度计算extractSize中线条的大小或宽度,然后将信息添加到数组中:

    if (panGestureRecognizer.state == UIGestureRecognizerStateBegan)
    {
        CGPoint point = [panGestureRecognizer locationInView:panGestureRecognizer.view];
        CGPoint prev  = [[points firstObject] pos];
        float size;

        size = currentPen.minWidth/2;

        // Add point to array
        [self addPoint:point withSize:size];

        // Add empty group to history
        [undoHistory addObject:[[NSMutableArray alloc] init]];
    }

    if (panGestureRecognizer.state == UIGestureRecognizerStateChanged) {

        CGPoint point = [panGestureRecognizer locationInView:panGestureRecognizer.view];
        currentPoint = [(LinePoint *)[points lastObject] pos];

        float size = clampf([self extractSize:panGestureRecognizer], currentPen.minWidth, currentPen.maxWidth);
       [self addPoint:point withSize:size];

        NSMutableArray *pArr = [[NSMutableArray alloc] init];
        UIBezierPath *sizer = [[UIBezierPath alloc] init];

        // interpolate points to make smooth variable width line 
        NSMutableArray *interPoints = [self calculateSmoothLinePoints];

        // code continues
I循环插入识别器中最新点和以前点之间的插值点阵列,创建将在屏幕上绘制的路径:

        // other code here
        // loop starts

        CGMutablePathRef path = CGPathCreateMutable();
        CGPathMoveToPoint(path, NULL, mid1.x, mid1.y);

        CGPathAddQuadCurveToPoint(path, NULL, prevT1.x, prevT1.y, mid2.x, mid2.y);
        CGPathAddLineToPoint(path, NULL, mid2b.x, mid2b.y);

        CGPathAddQuadCurveToPoint(path, NULL, prevB1.x, prevB1.y, mid1b.x, mid1b.y);
        CGPathAddLineToPoint(path, NULL, mid1.x, mid1.y);

        UIBezierPath *aPath = [UIBezierPath bezierPath];
        aPath.CGPath = path;

        [sizer appendPath:aPath];
        [pArr addObject:aPath];

        // more code here
        // loop ends

        CGPathRelease(path);
将所有路径添加到pArr后,我将创建一个HistoryItem,并用路径、线条颜色、线条宽度等填充它

    HistoryItem *action = [[HistoryItem alloc] initWithPaths:pArr
                                               andLineColour:self.lineColor
                                                andLineWidth:self.lineWidth
                                                 andDrawMode:self.currentDrawMode
                                                    andScale:self.scale];

    [self addAction:action];
addAction将HistoryItem添加到撤消堆栈。请注意,我记录了self.scale,但不使用它做任何事情。然后,我得到一个边框,并调用setNeedsDisplayInRect

当手势完成后,我会在线条上添加一个圆形的端点。代码省略

最后,drawRect:

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

    // offScreen context
    UIGraphicsPushContext(drawingContext);

    HistoryItem *action = [[undoHistory lastObject] lastObject];

    if(currentDrawMode == DRAW || currentDrawMode == ERASE) {
        for (UIBezierPath *p in action.pathsToDraw) {

            p.lineWidth = 1;
            p.lineCapStyle = kCGLineCapRound;

            [action.lineColor setFill];
            [action.lineColor setStroke];

            [p fill];
            [p stroke];
        }
    }
    if(currentDrawMode == UNDO) {
        CGContextClearRect(drawingContext, self.bounds);

        for (NSArray *actionGroup in undoHistory) {
            for (HistoryItem *undoAction in actionGroup) {
                for (UIBezierPath *p in undoAction.pathsToDraw) {
                    p.lineWidth = 1;
                    p.lineCapStyle = kCGLineCapRound;

                    [undoAction.lineColor setFill];
                    [undoAction.lineColor setStroke];

                    [p fill];
                    [p stroke];  
                }
            }
        }
    }
    // similar code for redo omitted
可能是框架/尺寸有问题

    // Continuation of drawRect: 

    CGImageRef cgImage = CGBitmapContextCreateImage(drawingContext);
    CGContextClipToRect(context, rect);
    CGContextDrawImage(context, CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), cgImage);
    CGImageRelease(cgImage);

    [super drawRect:rect];
}

重新绘制直线时,需要对点应用相同的变换。存储在阵列中的点相对于在其上渲染的画布。我建议您存储原始帧,然后在下一行渲染时缩放原始帧和新大小之间的坐标

请你澄清一下你所说的画布是什么意思?我曾尝试将工程视图的变换应用于工程视图的drawRect中的路径,但没有纠正问题,虽然有所不同,但仍然是错误的。请举例说明你将如何实施你的建议?你有没有找到答案?我也有同样的问题,在任何地方都找不到合适的答案。。。。
    // Continuation of drawRect: 

    CGImageRef cgImage = CGBitmapContextCreateImage(drawingContext);
    CGContextClipToRect(context, rect);
    CGContextDrawImage(context, CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), cgImage);
    CGImageRelease(cgImage);

    [super drawRect:rect];
}