我对iOS还是个新手,虽然我不明白为什么这不起作用。
我有两个独立的UIImageView
- 的"背景"。这是一张将被应用图层蒙版的图片,这样你只能看到图片的一部分。
- "蒙版图像"这是它自己的独立图像,可以旋转,平移,缩放。旋转/缩放代码工作正常,是标准代码。
我想做的是创建一个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。从这里,从你的代码,我明白你想做的是:
- 获取一个CGPath,它代表了MaskImage的实际形状,在应用了各种变换之后,从(大概)父视图或高于父视图的角度来看。
- 使用CGPath来定义一个CAShapeLayer,你将使用它作为其他视图(假设是背景UIImageView)的后台层的蒙版。
在这种情况下,你的文本中的MaskImage对应于你的代码的currentView
(因为那是你得到变换的地方),你的文本中的背景对应于你的代码的currentImageView
(因为那是你改变图层的蒙版的地方)。对吧?
如果所有这些都是正确的,那么你的代码有两个关键问题:
-
你想获得一个CGPath表示转换形状的currentView。但你在抓
currentView.frame
。这是错误的。currentView.frame
将表示currentView.superview
坐标中最小的矩形,它可以包含转换后的currentView形状。例如,如果currentView是一个旋转了45度的正方形,那么currentView。frame仍然是一个正方形,只是一个更大的正方形。相反,你应该做的是获得currentView.bounds
,这是rect,代表如何currentView的形状在自己的坐标系,生成路径,然后将currentView.transform
应用到该路径。 -
你没有指定任何地方,在你的代码或文本,如何从
currentView.superview
的坐标系统映射到MaskImage的坐标系统。在将形状应用到MaskImage.layer.mask. 之前也需要这样做
例如,我可以想象一个应用程序,其中屏幕被切割成左右两边相等,在屏幕的左侧显示一个大的照片图像,在屏幕的右侧它允许您平移,旋转和缩放黑色矩形图像。然后,应用程序将使用右侧的黑色矩形来定义一个CAShapeLayer,该CAShapeLayer作为蒙版应用于左侧的图像。这听起来像是你想要的。所以我要说的是,你需要的逻辑步骤是:
- 获取黑色矩形边界的CGPath,这将是一个未转换的矩形。
- 用同样的变换在黑色矩形上变换CGPath
- 在包含屏幕右侧的矩形的坐标系中表示CGPath。由于右手边和左手边是相等的,它现在也可以在坐标系统中直接应用于屏幕的左手边。
- 使用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];