从UIimageview中创建CGpath,使用旋转和缩放作为图层蒙版



我对iOS还是个新手,虽然我不明白为什么这不起作用。

我有两个独立的UIImageView

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

我想做的是创建一个CGPath,匹配当前位置/旋转/'蒙版图像'的规模

下面是我的代码,用于抓取"蒙版图像"的位置,并创建一个CGPAth用作图层蒙版。

如果你打开Xcode,创建一个新的"Single View Application"项目,这里是ViewController的完整复制粘贴

ViewController.h:

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController <UIGestureRecognizerDelegate>
{
    UIImageView *makskedImageView;
    UIImageView *maskImageView;
}
@end

ViewController.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.jpg &semiTransSquare.png

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

据我所知,尺度与中心一起工作,作为它增长/缩小的点,路径似乎从根x,y坐标缩放。

这可能是由设计(CGPathCreateWithRect每文档),但我看不到一种方法来应用转换到CGPath,并有它出现在相同的地方作为UIImageView '蒙版图像'

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

我错过了什么?

Update:我发现从边界到帧的改变让我有90%的方式:

CGRect maskRect = currentView.frame;

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

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

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

  1. 获取一个CGPath,它代表了MaskImage的实际形状,在应用了各种变换之后,从(大概)父视图或高于父视图的角度来看。
  2. 使用CGPath来定义一个CAShapeLayer,你将使用它作为其他视图(假设是背景UIImageView)的后台层的蒙版。

在这种情况下,你的文本中的MaskImage对应于你的代码的currentView(因为那是你得到变换的地方),你的文本中的背景对应于你的代码的currentImageView(因为那是你改变图层的蒙版的地方)。对吧?

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

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

  2. 你没有指定任何地方,在你的代码或文本,如何从currentView.superview的坐标系统映射到MaskImage的坐标系统。在将形状应用到MaskImage.layer.mask.

  3. 之前也需要这样做

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

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

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

您看过这个函数了吗

CGPathCreateCopyByTransformingPath ?

它需要一个CGRect和一个仿射变换,这应该达到你想要的。

仅供参考,它是iOS 5 +

你可以使用:

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

不等于:

CGFloat scale = [(NSNumber *)[currentView valueForKeyPath:@"layer.transform.scale"] floatValue];

最新更新