iOS 在 UIImage 上设置动画蒙版
iOS Animate Mask Over UIImage
我正在使用 Swift 1.2,我的目标是在静态 UIImage 上为图像遮罩设置动画。我实现的是 swift 版本的遮罩图像,我最初是在 Objective-C 中找到的。
func maskImage(image: UIImage, mask: UIImage) -> UIImage! {
let maskRef = mask.CGImage;
let mask = CGImageMaskCreate(
CGImageGetWidth(maskRef),
CGImageGetHeight(maskRef),
CGImageGetBitsPerComponent(maskRef),
CGImageGetBitsPerPixel(maskRef),
CGImageGetBytesPerRow(maskRef),
CGImageGetDataProvider(maskRef), nil, false);
let masked = CGImageCreateWithMask(image.CGImage, mask);
let retImage = UIImage(CGImage: masked);
return retImage;
}
效果很好!然而,将其付诸实施是我的挑战。
有没有一种方法可以通过不同的水平偏移量迭代应用蒙版,或者有更好的方法来完全解决这个问题——也许使用 CALayer 实现?
感谢您的帮助!
编辑: 根据发布的答案,我添加了这个:
let image = UIImage(named: "clouds");
let imageView = UIImageView(image: image);
let layer = CALayer();
layer.contents = UIImage(named: "alpha-mask")?.CGImage;
layer.bounds = CGRectMake(0, 0, image.size.width, image.size.height);
// For other folks learning, this did not work
//let animation = CABasicAnimation(keyPath: "bounds.origin.x");
// This does work
let animation = CABasicAnimation(keyPath: "position.x");
animation.duration = 2;
animation.delegate = self;
animation.fillMode = kCAFillModeForwards;
animation.repeatCount = 0;
animation.fromValue = 0.0;
animation.toValue = image.size.width;
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear);
animation.removedOnCompletion = false;
layer.addAnimation(animation, forKey: "transform");
imageView.layer.mask = layer;
self.addSubview(imageView);
我能够正确看到 alpha 蒙版,但动画不工作。有什么想法吗?
编辑: 我修改了上面的代码,它起作用了!我需要制作 keyPath position.x。见上文
您确实想要使用 CALayer - 或者更确切地说,CAShapeLayer。
您可以创建一个 CAShapeLayer 并将其作为蒙版安装在另一层上。
您可以创建一个 CAAnimation 以动画方式更改形状图层的路径,或者您可以以动画方式更改图层的 strokeStart and/or strokeEnd 属性。
如果为路径设置动画,您要遵循的一条规则是确保起始路径和结束路径具有相同数量和类型的控制点。否则动画是"undefined",结果会很奇怪。
我有一个开发博客 post 概述了它是如何完成的:
http://wareto.com/using-core-animation-groups-to-create-animation-sequences-2
它主要是关于使用 CAAnimationGroups,但它也包括一个对用作图像视图层的遮罩的 CAShapeLayer 进行动画更改的工作示例。
下面是它创建的遮罩动画的 GIF - "clock wipe" 显示和隐藏图像视图:
不幸的是,它是用 Objective-C 编写的,但核心动画调用在 Swift 中几乎相同。如果您在弄清楚如何调整它时遇到任何问题,请告诉我。
动画代码的核心是这个方法:
- (IBAction)doMaskAnimation:(id)sender;
{
waretoLogoLarge.hidden = FALSE;//Show the image view
//Create a shape layer that we will use as a mask for the waretoLogoLarge image view
CAShapeLayer *maskLayer = [CAShapeLayer layer];
CGFloat maskHeight = waretoLogoLarge.layer.bounds.size.height;
CGFloat maskWidth = waretoLogoLarge.layer.bounds.size.width;
CGPoint centerPoint;
centerPoint = CGPointMake( maskWidth/2, maskHeight/2);
//Make the radius of our arc large enough to reach into the corners of the image view.
CGFloat radius = sqrtf(maskWidth * maskWidth + maskHeight * maskHeight)/2;
//Don't fill the path, but stroke it in black.
maskLayer.fillColor = [[UIColor clearColor] CGColor];
maskLayer.strokeColor = [[UIColor blackColor] CGColor];
maskLayer.lineWidth = radius; //Make the line thick enough to completely fill the circle we're drawing
CGMutablePathRef arcPath = CGPathCreateMutable();
//Move to the starting point of the arc so there is no initial line connecting to the arc
CGPathMoveToPoint(arcPath, nil, centerPoint.x, centerPoint.y-radius/2);
//Create an arc at 1/2 our circle radius, with a line thickess of the full circle radius
CGPathAddArc(arcPath,
nil,
centerPoint.x,
centerPoint.y,
radius/2,
3*M_PI/2,
-M_PI/2,
YES);
maskLayer.path = arcPath;
//Start with an empty mask path (draw 0% of the arc)
maskLayer.strokeEnd = 0.0;
CFRelease(arcPath);
//Install the mask layer into out image view's layer.
waretoLogoLarge.layer.mask = maskLayer;
//Set our mask layer's frame to the parent layer's bounds.
waretoLogoLarge.layer.mask.frame = waretoLogoLarge.layer.bounds;
//Create an animation that increases the stroke length to 1, then reverses it back to zero.
CABasicAnimation *swipe = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
swipe.duration = 2;
swipe.delegate = self;
[swipe setValue: theBlock forKey: kAnimationCompletionBlock];
swipe.timingFunction = [CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionLinear];
swipe.fillMode = kCAFillModeForwards;
swipe.removedOnCompletion = NO;
swipe.autoreverses = YES;
swipe.toValue = [NSNumber numberWithFloat: 1.0];
[maskLayer addAnimation: swipe forKey: @"strokeEnd"];
}
我在 Swift 中有另一个 IS 的博客条目,它展示了如何使用 CAShapeLayer
创建饼图并制作动画。该项目为形状设置动画,而不是遮罩,但唯一真正的区别是您是将形状层安装为常规内容层还是作为遮罩安装在另一层(如图像视图的背衬层)上。
您可以在 link:
查看该项目
我正在使用 Swift 1.2,我的目标是在静态 UIImage 上为图像遮罩设置动画。我实现的是 swift 版本的遮罩图像,我最初是在 Objective-C 中找到的。
func maskImage(image: UIImage, mask: UIImage) -> UIImage! {
let maskRef = mask.CGImage;
let mask = CGImageMaskCreate(
CGImageGetWidth(maskRef),
CGImageGetHeight(maskRef),
CGImageGetBitsPerComponent(maskRef),
CGImageGetBitsPerPixel(maskRef),
CGImageGetBytesPerRow(maskRef),
CGImageGetDataProvider(maskRef), nil, false);
let masked = CGImageCreateWithMask(image.CGImage, mask);
let retImage = UIImage(CGImage: masked);
return retImage;
}
效果很好!然而,将其付诸实施是我的挑战。
有没有一种方法可以通过不同的水平偏移量迭代应用蒙版,或者有更好的方法来完全解决这个问题——也许使用 CALayer 实现?
感谢您的帮助!
编辑: 根据发布的答案,我添加了这个:
let image = UIImage(named: "clouds");
let imageView = UIImageView(image: image);
let layer = CALayer();
layer.contents = UIImage(named: "alpha-mask")?.CGImage;
layer.bounds = CGRectMake(0, 0, image.size.width, image.size.height);
// For other folks learning, this did not work
//let animation = CABasicAnimation(keyPath: "bounds.origin.x");
// This does work
let animation = CABasicAnimation(keyPath: "position.x");
animation.duration = 2;
animation.delegate = self;
animation.fillMode = kCAFillModeForwards;
animation.repeatCount = 0;
animation.fromValue = 0.0;
animation.toValue = image.size.width;
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear);
animation.removedOnCompletion = false;
layer.addAnimation(animation, forKey: "transform");
imageView.layer.mask = layer;
self.addSubview(imageView);
我能够正确看到 alpha 蒙版,但动画不工作。有什么想法吗?
编辑: 我修改了上面的代码,它起作用了!我需要制作 keyPath position.x。见上文
您确实想要使用 CALayer - 或者更确切地说,CAShapeLayer。
您可以创建一个 CAShapeLayer 并将其作为蒙版安装在另一层上。
您可以创建一个 CAAnimation 以动画方式更改形状图层的路径,或者您可以以动画方式更改图层的 strokeStart and/or strokeEnd 属性。
如果为路径设置动画,您要遵循的一条规则是确保起始路径和结束路径具有相同数量和类型的控制点。否则动画是"undefined",结果会很奇怪。
我有一个开发博客 post 概述了它是如何完成的:
http://wareto.com/using-core-animation-groups-to-create-animation-sequences-2
它主要是关于使用 CAAnimationGroups,但它也包括一个对用作图像视图层的遮罩的 CAShapeLayer 进行动画更改的工作示例。
下面是它创建的遮罩动画的 GIF - "clock wipe" 显示和隐藏图像视图:
不幸的是,它是用 Objective-C 编写的,但核心动画调用在 Swift 中几乎相同。如果您在弄清楚如何调整它时遇到任何问题,请告诉我。
动画代码的核心是这个方法:
- (IBAction)doMaskAnimation:(id)sender;
{
waretoLogoLarge.hidden = FALSE;//Show the image view
//Create a shape layer that we will use as a mask for the waretoLogoLarge image view
CAShapeLayer *maskLayer = [CAShapeLayer layer];
CGFloat maskHeight = waretoLogoLarge.layer.bounds.size.height;
CGFloat maskWidth = waretoLogoLarge.layer.bounds.size.width;
CGPoint centerPoint;
centerPoint = CGPointMake( maskWidth/2, maskHeight/2);
//Make the radius of our arc large enough to reach into the corners of the image view.
CGFloat radius = sqrtf(maskWidth * maskWidth + maskHeight * maskHeight)/2;
//Don't fill the path, but stroke it in black.
maskLayer.fillColor = [[UIColor clearColor] CGColor];
maskLayer.strokeColor = [[UIColor blackColor] CGColor];
maskLayer.lineWidth = radius; //Make the line thick enough to completely fill the circle we're drawing
CGMutablePathRef arcPath = CGPathCreateMutable();
//Move to the starting point of the arc so there is no initial line connecting to the arc
CGPathMoveToPoint(arcPath, nil, centerPoint.x, centerPoint.y-radius/2);
//Create an arc at 1/2 our circle radius, with a line thickess of the full circle radius
CGPathAddArc(arcPath,
nil,
centerPoint.x,
centerPoint.y,
radius/2,
3*M_PI/2,
-M_PI/2,
YES);
maskLayer.path = arcPath;
//Start with an empty mask path (draw 0% of the arc)
maskLayer.strokeEnd = 0.0;
CFRelease(arcPath);
//Install the mask layer into out image view's layer.
waretoLogoLarge.layer.mask = maskLayer;
//Set our mask layer's frame to the parent layer's bounds.
waretoLogoLarge.layer.mask.frame = waretoLogoLarge.layer.bounds;
//Create an animation that increases the stroke length to 1, then reverses it back to zero.
CABasicAnimation *swipe = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
swipe.duration = 2;
swipe.delegate = self;
[swipe setValue: theBlock forKey: kAnimationCompletionBlock];
swipe.timingFunction = [CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionLinear];
swipe.fillMode = kCAFillModeForwards;
swipe.removedOnCompletion = NO;
swipe.autoreverses = YES;
swipe.toValue = [NSNumber numberWithFloat: 1.0];
[maskLayer addAnimation: swipe forKey: @"strokeEnd"];
}
我在 Swift 中有另一个 IS 的博客条目,它展示了如何使用 CAShapeLayer
创建饼图并制作动画。该项目为形状设置动画,而不是遮罩,但唯一真正的区别是您是将形状层安装为常规内容层还是作为遮罩安装在另一层(如图像视图的背衬层)上。
您可以在 link:
查看该项目