iOS如何将形状从圆形动画/变形为正方形?

13

我试图将圆形变成正方形,反之亦然,目前已经接近成功。但是它没有预期的动画效果。我希望正方形的所有角落都能同时进行动画/变形,但我得到的只是以下内容:

enter image description here

我使用CAShapeLayerCABasicAnimation来动画化形状属性。

这是我如何创建圆形路径的方法:

- (UIBezierPath *)circlePathWithCenter:(CGPoint)center radius:(CGFloat)radius
{    
    UIBezierPath *circlePath = [UIBezierPath bezierPath];
    [circlePath addArcWithCenter:center radius:radius startAngle:0 endAngle:M_PI/2 clockwise:YES];
    [circlePath addArcWithCenter:center radius:radius startAngle:M_PI/2 endAngle:M_PI clockwise:YES];
    [circlePath addArcWithCenter:center radius:radius startAngle:M_PI endAngle:3*M_PI/2 clockwise:YES];
    [circlePath addArcWithCenter:center radius:radius startAngle:3*M_PI/2 endAngle:M_PI clockwise:YES];
    [circlePath closePath];
    return circlePath;
}

这是一个正方形路径:

- (UIBezierPath *)squarePathWithCenter:(CGPoint)center size:(CGFloat)size
{
    CGFloat startX = center.x-size/2;
    CGFloat startY = center.y-size/2;

    UIBezierPath *squarePath = [UIBezierPath bezierPath];
    [squarePath moveToPoint:CGPointMake(startX, startY)];
    [squarePath addLineToPoint:CGPointMake(startX+size, startY)];
    [squarePath addLineToPoint:CGPointMake(startX+size, startY+size)];
    [squarePath addLineToPoint:CGPointMake(startX, startY+size)];
    [squarePath closePath];
    return squarePath;
}

我将圆形路径应用于我的一个视图层,并设置填充等参数。它完美地绘制出来了。 然后,在我的gestureRecognizer选择器中,我创建并运行以下动画:

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
    animation.duration = 1;
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

 animation.fromValue = (__bridge id)(self.stateLayer.path);
        animation.toValue = (__bridge id)(self.stopPath.CGPath);
self.stateLayer.path = self.stopPath.CGPath;

[self.stateLayer addAnimation:animation forKey:@"animatePath"];

正如您可以在 circlePathWithCenter:radius: squarePathWithCenter:size:中注意到的那样,我遵循了这里的建议(具有相同数量的线段和控制点):平滑形状转换动画

动画看起来比上面的帖子更好,但它仍然不是我想要实现的 :(

我知道我可以使用简单的 CALayer ,然后设置适当级别的 cornerRadius 将正方形/矩形变成圆形,之后通过动画 cornerRadius 属性将其从圆形变为正方形,但我仍然非常好奇是否可以使用 CAShapeLayer path 动画来实现。

提前感谢您的帮助!


看起来你遇到了与这个问题相同的问题[https://dev59.com/UXTYa4cB1Zd3GeqPpgCW],基于动画效果——值得一看。 - username tbd
嗯...也许是这样-我知道问题所在,并按照被接受的答案建议进行了跟进,但对我来说仍然是个死胡同。我有相同数量的线段/控制点,我认为这有所帮助,因为我的动画比那里的动画稍微好一些,但仍然不知道如何改进它。 - fgeorgiew
5个回答

12
在游乐场玩了一会儿之后,我注意到每个弧都会向贝塞尔路径添加两个线段,而不仅仅是一个。此外,调用 closePath 会再添加两个线段。因此,为了保持线段数的一致性,我最终在我的正方形路径中添加了虚假线段。代码是用Swift编写的,但我认为这并不重要。
func circlePathWithCenter(center: CGPoint, radius: CGFloat) -> UIBezierPath {
    let circlePath = UIBezierPath()
    circlePath.addArcWithCenter(center, radius: radius, startAngle: -CGFloat(M_PI), endAngle: -CGFloat(M_PI/2), clockwise: true)
    circlePath.addArcWithCenter(center, radius: radius, startAngle: -CGFloat(M_PI/2), endAngle: 0, clockwise: true)
    circlePath.addArcWithCenter(center, radius: radius, startAngle: 0, endAngle: CGFloat(M_PI/2), clockwise: true)
    circlePath.addArcWithCenter(center, radius: radius, startAngle: CGFloat(M_PI/2), endAngle: CGFloat(M_PI), clockwise: true)
    circlePath.closePath()
    return circlePath
}

func squarePathWithCenter(center: CGPoint, side: CGFloat) -> UIBezierPath {
    let squarePath = UIBezierPath()
    let startX = center.x - side / 2
    let startY = center.y - side / 2
    squarePath.moveToPoint(CGPoint(x: startX, y: startY))
    squarePath.addLineToPoint(squarePath.currentPoint)
    squarePath.addLineToPoint(CGPoint(x: startX + side, y: startY))
    squarePath.addLineToPoint(squarePath.currentPoint)
    squarePath.addLineToPoint(CGPoint(x: startX + side, y: startY + side))
    squarePath.addLineToPoint(squarePath.currentPoint)
    squarePath.addLineToPoint(CGPoint(x: startX, y: startY + side))
    squarePath.addLineToPoint(squarePath.currentPoint)
    squarePath.closePath()
    return squarePath
}

在此输入图片描述


在发现这两点方面做得不错,已经制作出了一个Objective-C版本。 - Johnny Rockex

7

我知道这是一个老问题,但这可能会对某些人有所帮助。

我认为最好通过动画圆角来实现这个效果。

    let v1 = UIView(frame: CGRect(x: 100, y: 100, width: 50, height: 50))
    v1.backgroundColor = UIColor.green
    view.addSubview(v1)

动画:

    let animation = CABasicAnimation(keyPath: "cornerRadius")
    animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
    animation.fillMode = kCAFillModeForwards
    animation.isRemovedOnCompletion = false
    animation.fromValue = v1.layer.cornerRadius
    animation.toValue = v1.bounds.width/2
    animation.duration = 3
    v1.layer.add(animation, forKey: "cornerRadius")

1
根据@Danchoys的回答,这是Objective-C的代码。 info是一个60像素的按钮,infoVC是即将被推出的下一个视图控制器:
    UIBezierPath * maskPath = [UIBezierPath new];
    [maskPath addArcWithCenter:info.center radius:15.0f startAngle:DEGREES_TO_RADIANS(180) endAngle:DEGREES_TO_RADIANS(270) clockwise:true];
    [maskPath addArcWithCenter:info.center radius:15.0f startAngle:DEGREES_TO_RADIANS(270) endAngle:DEGREES_TO_RADIANS(0) clockwise:true];
    [maskPath addArcWithCenter:info.center radius:15.0f startAngle:DEGREES_TO_RADIANS(0) endAngle:DEGREES_TO_RADIANS(90) clockwise:true];
    [maskPath addArcWithCenter:info.center radius:15.0f startAngle:DEGREES_TO_RADIANS(90) endAngle:DEGREES_TO_RADIANS(180) clockwise:true];
    [maskPath closePath];

    UIBezierPath * endPath = [UIBezierPath new];
    [endPath moveToPoint:CGPointMake(0,0)];
    [endPath addLineToPoint:endPath.currentPoint];
    [endPath addLineToPoint:CGPointMake(w, 0)];
    [endPath addLineToPoint:endPath.currentPoint];
    [endPath addLineToPoint:CGPointMake(w, h)];
    [endPath addLineToPoint:endPath.currentPoint];
    [endPath addLineToPoint:CGPointMake(0, h)];
    [endPath addLineToPoint:endPath.currentPoint];
    [endPath closePath];

    CAShapeLayer *maskLayer = [CAShapeLayer layer];
    maskLayer.frame = info.bounds;
    maskLayer.path = maskPath.CGPath;
    _infoVC.view.layer.mask = maskLayer;

    CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"path"];
    animation.fromValue = (id)maskPath.CGPath;
    animation.toValue = (id)endPath.CGPath;
    animation.duration = 0.3f;
    [maskLayer addAnimation:animation forKey:nil];
    [maskLayer setPath:endPath.CGPath];

0
我强烈推荐使用CanvasPod库,它还具有预定义的“变形”动画,并且易于集成。你也可以在canvaspod.io网站上查看一些示例。

0
这对于制作录音按钮并希望获得原生iOS动画效果的人很方便。
在您想要放置按钮的UIViewController中实例化此类。添加一个UITapGestureRecogniser,当被点击时调用animateRecordingButton。
import UIKit

class RecordButtonView: UIView {

    enum RecordingState {
        case ready
        case recording
    }
            
    var currentState: RecordingState = .ready

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.isUserInteractionEnabled = true
        self.backgroundColor = .white
        // My button is 75 points in height and width so this
        // Will make a circle
        self.layer.cornerRadius = 35
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
    
    func animateRecordingButton() {
       
        var newRadius: CGFloat
        var scale: CGFloat
        
        switch currentState {
        case .ready:
            // The square radius
            newRadius = 10
            // Lets scale it down to 70%
            scale = 0.7
            currentState = .recording
        case .recording:
            // Animate back to cirlce 
            newRadius = 35
            currentState = .ready
            // Animate back to full scale
            scale = 1
        }
                    
        UIView.animate(withDuration: 1) {
            self.layer.cornerRadius = newRadius
            self.transform = CGAffineTransform(scaleX: scale, y: scale)
        }
        
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接