1

我对 iOS 还是很陌生,虽然我不知道为什么这不起作用。

我有 2 个单独的 UIImageView

  1. 的背景'。这是一张将应用图层蒙版的图片,因此您只能看到图像的一部分。
  2. “蒙版图像”是它自己的独立图像,可以旋转、平移、捏缩放。旋转/缩放的代码工作正常,是标准代码。

我要做的是创建一个与“蒙版图像”的当前位置/旋转/比例相匹配的 CGPath

这是我的代码,用于获取“蒙版图像”的位置并创建一个 CGPAth 以用作图层蒙版。

如果你去 Xcode 并创建一个新的“单视图应用程序”项目,这里是 ViewController 的完整复制粘贴

视图控制器.h:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <UIGestureRecognizerDelegate>
{
    UIImageView *makskedImageView;
    UIImageView *maskImageView;
}

@end

视图控制器.m

#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    UIImage *sourceImage = [UIImage imageNamed:@"forest.jpg"];
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, (sourceImage.size.width/2), (sourceImage.size.height/2))];
    imageView.image = sourceImage;

    [self.view addSubview:imageView];

    makskedImageView = imageView;

    //    Movable Mask
    UIImage *frameBorder = [UIImage imageNamed:@"semiTransSquare.png"];
    maskImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, (frameBorder.size.width/2), (frameBorder.size.height/2))];
    maskImageView.image = frameBorder;

    UIPanGestureRecognizer *panGesture2 = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
    [panGesture2 setDelegate:self];
    [maskImageView addGestureRecognizer:panGesture2];
    UIPinchGestureRecognizer *pinchGesture2 = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)];
    [pinchGesture2 setDelegate:self];
    [maskImageView addGestureRecognizer:pinchGesture2];
    UIRotationGestureRecognizer *rotateGesture2 = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(handleRotate:)];
    [rotateGesture2 setDelegate:self];
    [maskImageView addGestureRecognizer:rotateGesture2];

    UITapGestureRecognizer *tapGR =[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleTap:)];
    [tapGR setDelegate:self];
    [tapGR setNumberOfTapsRequired:1];
    [maskImageView addGestureRecognizer:tapGR];

    [maskImageView setUserInteractionEnabled:YES];
    // Add to view to make visible;
    [self.view addSubview:maskImageView];

    // apply layer mask to makskedImageView
    [self grabTransform:maskImageView];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)grabTransform:(UIView *)currentView
{
    // build the transform you want
    CGAffineTransform t = CGAffineTransformIdentity;
    CGFloat angle = [(NSNumber *)[currentView valueForKeyPath:@"layer.transform.rotation.z"] floatValue];
    CGFloat scale = [(NSNumber *)[currentView valueForKeyPath:@"layer.transform.scale"] floatValue];
    t = CGAffineTransformConcat(t, CGAffineTransformMakeScale(scale, scale));
    t = CGAffineTransformConcat(t, CGAffineTransformMakeRotation(angle));
    NSLog(@"angle: %f, scale: %f", angle, scale);

    // Create a mask layer
    // Create a mask layer and the frame to determine what will be visible in the view.
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
    // CGRect maskRect = currentView.bounds;
    // Frame works a lot better.
    CGRect maskRect = currentView.frame;

    maskRect.origin.x = currentView.frame.origin.x;
    maskRect.origin.y = currentView.frame.origin.y;

    // Make a Path friendly transform.

    CGPoint center = CGPointMake(CGRectGetMidX(currentView.frame), CGRectGetMidY(currentView.frame));
    CGAffineTransform transform = CGAffineTransformIdentity;

    transform = CGAffineTransformTranslate(transform, center.x, center.y);
    transform = CGAffineTransformScale(transform, scale, scale);
    transform = CGAffineTransformRotate(transform, angle);
    transform = CGAffineTransformTranslate(transform, -center.x, -center.y);

    // Create a path and from the rectangle in it.
    CGPathRef path = CGPathCreateWithRect(maskRect, &transform);
    // Set the path to the mask layer.
    [maskLayer setPath:path];
    // Release the path since it's not covered by ARC.
    CGPathRelease(path);
    // Set the mask of the view.
    makskedImageView.layer.mask = maskLayer;


}

#pragma mark - Gestures

-(void)handlePan:(UIPanGestureRecognizer *)recognizer
{
    CGPoint translation = [recognizer translationInView:self.view];
    recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,
                                         recognizer.view.center.y + translation.y);
    [recognizer setTranslation:CGPointMake(0, 0) inView:self.view];
    [self grabTransform:[recognizer view]];
}

-(void)handlePinch:(UIPinchGestureRecognizer *)recognizer
{
    recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale);

    recognizer.scale = 1;
    [self grabTransform:[recognizer view]];
}

-(void)handleRotate:(UIRotationGestureRecognizer *)recognizer
{
    recognizer.view.transform = CGAffineTransformRotate(recognizer.view.transform, recognizer.rotation);
    recognizer.rotation = 0;
valueForKeyPath:@"layer.transform.rotation"] floatValue];
    [self grabTransform:[recognizer view]];
}

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}

- (void)handleTap:(UIGestureRecognizer *)recognizer
{
    [self grabTransform:[recognizer view]];

}

@end

加 2 张图片:Forest.jpgsemiTransSquare.png

现在,当它运行时,它适用于平移,直到我缩放。旋转图像。(由于其他一些代码,现在轮换工作)。

据我所知,比例与中心一起作为它增长/收缩的点,路径似乎从根 x,y 坐标向外扩展。

这可能是设计使然(根据文档 CGPathCreateWithRect),但我看不到将转换应用于 CGPath 并让它与 UIImageView 'Mask Image' 出现在同一位置的方法

性能是一个大问题,使用 CIImage 的类似解决方案非常滞后,因此希望在 CGPath 创建图层蒙版的情况下解决这个问题,如果可能的话,或者同样快的东西。

我错过了什么?对仅限 iOS 6 的解决方案感到满意。

更新:我发现从边界更改为框架让我达到了 90% 的目标:

CGRect maskRect = currentView.frame;

路径现在将匹配视图的旋转和位置。剩下的唯一问题是规模是错误的。它与原始大小匹配,但如果你缩小它就会变得太小,或者如果你增长它就会变得太大。看起来比例不是1:1。现在这么近。

4

3 回答 3

0

你看过函数吗

CGPathCreateCopyByTransformingPath?

它需要一个CGRect仿射变换,它应该可以实现你所追求的。

仅供参考,它是 iOS 5 +

于 2013-03-21T21:05:10.263 回答
0

我不确定我是否完全理解您对问题的描述,所以一个虚拟应用程序真的很有帮助。但这是我现在所看到的分析:

你说你有两个 UIImageView,Background 和 MaskImage。从这个,从你的代码,我明白你想要做的是:

  1. 在应用了各种变换之后,从(可能)一个超级视图或某个高于超级视图的角度来看,得到一个表示 MaskImage 实际形状的 CGPath。
  2. 使用该 CGPath 定义一个 CAShapeLayer,您将使用它作为其他视图的支持层上的掩码,大概是背景 UIImageView

在这种情况下,文本中的 MaskImage 对应于您的代码currentView(因为那是您获得转换的地方),而文本中的 Background 对应于您的代码currentImageView(因为那是您要更改图层蒙版的地方)。正确的?

如果所有这些都是正确的,那么您的代码存在两个关键问题:

  1. 你想得到一个 CGPath 代表 currentView 的变换形状。但你在抢currentView.frame。这是错误的。currentView.frame将表示最小的矩形,在 的坐标中currentView.superview,它可以包含当前视图的变换形状。因此,例如,如果 currentView 是一个旋转了 45 度的正方形,那么 currentView.frame 仍然是一个正方形,只是一个更大的正方形。相反,您应该做的是获取currentView.bounds,它是表示 currentView 在其自己的坐标系中的形状的矩形,从中生成路径,然后应用于currentView.transform该路径。

  2. 您没有在代码或文本中的任何地方指定如何从坐标系映射currentView.superview到 MaskImage 的坐标系。您还需要在将形状应用到 MaskImage.layer.mask 之前执行此操作。

例如,我可以想象一个应用程序,屏幕被分成左右两个相等的边,它在屏幕的左侧呈现了一个大的照片图像,在屏幕的右侧它允许你平移、旋转,并缩放黑色矩形的图像。然后,该应用程序将使用右侧的黑色矩形来定义一个 CAShapeLayer,该 CAShapeLayer 用作左侧图像上的蒙版。这听起来像你想要的。所以我要说的是,你需要的逻辑步骤是:

  1. 获取黑色矩形边界的 CGPath,这将是一个未转换的矩形。
  2. 使用应用于黑色矩形的相同变换来变换此 CGPath。
  3. 在仅包含屏幕右侧的矩形的坐标系中表示此 CGPath。由于左右两边是全等的,所以现在也在坐标系中,可以直接应用到屏幕的左边。
  4. 使用 CGPath 定义一个 CALayer,将此 CALayer 应用于代表屏幕左侧的视图,并在该视图的图层上调用 setNeedsDisplay。

PS 顺便说一下,还有第三个问题。请注意,从技术上讲,当您向图层添加 .mask 时,该图层不应具有超级图层。所以你不应该在视图层次结构中添加一个 .mask 到 UIView 的支持层,因为它将有一个超级层。所以,我相信,你应该做的是暂时从视图层次结构中删除 UIView,添加遮罩层,然后将视图重新添加到视图层次结构中。

于 2013-01-18T12:23:17.027 回答
0

您可以使用以下方法获得有效的比例因子:

CGFloat zoomScale = sqrtf( powf(currentView.transform.a, 2) + powf(currentView.transform.c, 2) );

不等于:

CGFloat scale = [(NSNumber *)[currentView valueForKeyPath:@"layer.transform.scale"] floatValue];
于 2014-11-26T06:58:45.073 回答