【问题标题】:Using mask to show/hide a view with animation使用蒙版显示/隐藏带有动画的视图
【发布时间】:2015-03-11 14:54:54
【问题描述】:

我正在尝试使用遮罩来隐藏和显示视图,但由于某种原因我无法使其正常工作。

我真的很困惑是否应该为蒙版的框架/边界/路径设置动画。

这是我的代码,用于显示按钮操作“GO”的视图:

- (IBAction)go:(id)sender
{
    [UIView animateWithDuration:3.0 animations:^{

        self.testView.layer.mask.bounds = self.testView.layer.bounds;
    }];
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    CGRect rect = CGRectMake(0, 0, self.testView.bounds.size.width, 0);
    CGPathRef path = CGPathCreateWithRect(rect, NULL);

    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.path = path;

    self.testView.layer.mask = shapeLayer;
    self.testView.layer.mask.bounds = rect;
}

谢谢!

【问题讨论】:

  • 顺便说一句,始终遵循“创建规则”,即如果您调用名称中带有“复制”或“创建”的核心图形函数,则您负责释放对象。如果你通过静态分析器运行它(shift+command+B 或从 Xcode 的“产品”菜单中选择“分析”),它会引起你的注意。

标签: ios ios8 calayer mask bounds


【解决方案1】:

您正在更改掩码的bounds,而不是path。您确实需要更改maskpath。从理论上讲,您可以使用 CABasicAnimation 做到这一点,但我个人发现在为路径设置动画时(尤其是蒙版的路径)非常不稳定。

如果可以的话,我会完全取消遮罩并设置 testView 的框架,使其不可见(例如高度为零),然后使用基于块的 @987654327 为该框架的变化设置动画@animateWithDuration。 (注意,如果使用自动布局,那么您可能会为更改了约束的setNeedsLayout 设置动画)。

如果您确实需要使用CAShapeLayer 掩码,您可以尝试在掩码CAShapeLayer 上的path 键的CABAsicAnimationanimateWithKeyPath

就个人而言,在为路径更改设置动画时,我会使用显示链接,例如类似:

- (IBAction)didTapButton:(UIBarButtonItem *)sender {
    [AnimationDisplayLink animateWithDuration:3.0 animationHandler:^(CGFloat percent) {
        self.mask.path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, self.testView.bounds.size.width, self.testView.bounds.size.height * percent)].CGPath;
    } completionHandler:nil];
}

我的AnimationDisplayLink定义如下:

@interface AnimationDisplayLink : NSObject
@property (nonatomic) CGFloat animationDuration;
@property (nonatomic, copy) void(^animationHandler)(CGFloat percent);
@property (nonatomic, copy) void(^completionHandler)();
@end

@interface AnimationDisplayLink ()
@property (nonatomic) CFAbsoluteTime startTime;
@property (nonatomic, strong) CADisplayLink *displayLink;
@end

@implementation AnimationDisplayLink

+ (instancetype)animateWithDuration:(CGFloat)duration animationHandler:(void (^)(CGFloat percent))animationHandler completionHandler:(void (^)(void))completionHandler {
    AnimationDisplayLink *handler = [[self alloc] init];

    handler.animationDuration = duration;
    handler.animationHandler  = animationHandler;
    handler.completionHandler = completionHandler;

    [handler startAnimation];

    return handler;
}

- (void)startAnimation {
    self.startTime = CFAbsoluteTimeGetCurrent();
    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)];
    [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}

- (void)stopAnimation {
    [self.displayLink invalidate];
    self.displayLink = nil;
}

- (void)handleDisplayLink:(CADisplayLink *)displayLink {
    CGFloat elapsed = CFAbsoluteTimeGetCurrent() - self.startTime;
    CGFloat percent = elapsed / self.animationDuration;

    if (percent >= 1.0) {
        [self stopAnimation];
        if (self.animationHandler)  self.animationHandler(1.0);
        if (self.completionHandler) self.completionHandler();
    } else {
        if (self.animationHandler)  self.animationHandler(percent);
    }
}

@end

【讨论】:

  • 谢谢!动画面具的路径,它的工作。顺便说一句,我应该将动画的 removedOnCompletion 设置为 NO 并将 fillMode 设置为 kCAFillModeForwards 以使其保持最终动画状态吗?
  • 如果使用CABasicAnimation,那么,是的,您可以这样做。或者,就在动画之前,将路径设置为最终路径,这样,当动画被移除时,if 将使用动画之前的任何内容。
【解决方案2】:

Rob 答案的 Swift 版本

import UIKit

typealias PercentBasedAnimationHandler = ((_ percent: CGFloat) -> Void)
typealias AnimationCompletionHandler = (() -> Void)

class AnimationDisplayLink: NSObject {
var animationDuration: CGFloat = 0.3

var animationHandler: PercentBasedAnimationHandler!
var completionHandler: AnimationCompletionHandler!

var startTime: CFAbsoluteTime!

var displayLink: CADisplayLink!

static func animateWith(duration: CGFloat, animationHanlder: @escaping PercentBasedAnimationHandler, completionHandler: @escaping AnimationCompletionHandler) -> AnimationDisplayLink {

    let handler = AnimationDisplayLink()
    handler.animationDuration = duration
    handler.animationHandler = animationHanlder
    handler.completionHandler = completionHandler
    handler.startAnimation()

    return handler
}

func startAnimation() {
    self.startTime = CFAbsoluteTimeGetCurrent()
    self.displayLink = CADisplayLink(target: self, selector: #selector(hanldeDisplayLink(displayLink:)))
    self.displayLink.add(to: RunLoop.main, forMode: .commonModes)
}

func stopAnimation() {
    self.displayLink.invalidate()
    self.displayLink = nil
}

@objc func hanldeDisplayLink(displayLink: CADisplayLink) {
    let elapsed = CFAbsoluteTimeGetCurrent() - self.startTime
    let percent = CGFloat(elapsed) / animationDuration

    if percent >= 1.0 {
        stopAnimation()
        self.animationHandler(1.0)
        self.completionHandler()
    } else {
        self.animationHandler(percent)
    }
}
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-09
    • 2015-03-29
    • 2021-03-03
    • 1970-01-01
    • 1970-01-01
    • 2020-06-23
    • 1970-01-01
    相关资源
    最近更新 更多