如何创建平滑可调整大小的圆形 UIView? [英] How do I create a smoothly resizable circular UIView?

查看:18
本文介绍了如何创建平滑可调整大小的圆形 UIView?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个 UIView,它显示一个半透明的圆圈,在其边界内有一个不透明的边框.我希望能够以两种方式更改边界 - 在 -[UIView animateWithDuration:animations:] 块内和在每秒触发几次的捏合手势识别器操作中.我已经根据 SO 其他地方的答案尝试了三种方法,但没有一种适合.

I'm trying to create a UIView which shows a semitransparent circle with an opaque border inside its bounds. I want to be able to change the bounds in two ways - inside a -[UIView animateWithDuration:animations:] block and in a pinch gesture recogniser action which fires several times a second. I've tried three approaches based on answers elsewhere on SO, and none are suitable.

  1. layoutSubviews 中设置视图层的角半径可提供平滑的平移,但视图在动画期间不会保持圆形;似乎cornerRadius 不可设置动画.

  1. Setting the corner radius of the view's layer in layoutSubviews gives smooth translations, but the view doesn't stay circular during animations; it seems that cornerRadius isn't animatable.

drawRect: 中绘制圆圈给出了一致的圆形视图,但是如果圆圈太大,那么在捏合手势中调整大小会变得不稳定,因为设备花费太多时间重新绘制圈.

Drawing the circle in drawRect: gives a consistently circular view, but if the circle gets too big then resizing in the pinch gesture gets choppy because the device is spending too much time redrawing the circle.

添加 CAShapeLayer 并在 layoutSublayersOfLayer 中设置其路径属性,这不会在 UIView 动画中设置动画,因为路径不是隐式动画.

Adding a CAShapeLayer and setting its path property in layoutSublayersOfLayer, which doesn't animate inside UIView animations since path isn't implicitly animatable.

有没有办法让我创建一个始终保持圆形且可平滑调整大小的视图?我可以使用其他类型的层来利用硬件加速吗?

Is there a way for me to create a view which is consistently circular and smoothly resizable? Is there some other type of layer I could use to take advantage of the hardware acceleration?

更新

当我说我想更改 -[UIView animateWithDuration:animations:] 块内的边界时,一位评论者要求我扩展我的意思.在我的代码中,我有一个包含我的圆视图的视图.圆形视图(使用cornerRadius 的版本)覆盖-[setBounds:] 以设置角半径:

A commenter has asked me to expand on what I mean when I say that I want to change the bounds inside a -[UIView animateWithDuration:animations:] block. In my code, I have a view which contains my circle view. The circle view (the version that uses cornerRadius) overrides -[setBounds:] in order to set the corner radius:

-(void)setBounds:(CGRect)bounds
{
    self.layer.cornerRadius = fminf(bounds.size.width, bounds.size.height) / 2.0;
    [super setBounds:bounds];
}

圆视图的边界在-[layoutSubviews]中设置:

The bounds of the circle view are set in -[layoutSubviews]:

-(void)layoutSubviews
{
    // some other layout is performed and circleRadius and circleCenter are
    // calculated based on the properties and current size of the view.

    self.circleView.bounds = CGRectMake(0, 0, circleRadius*2, circleRadius*2);
    self.circleView.center = circleCenter;
}

视图有时会在动画中调整大小,如下所示:

The view is sometimes resized in animations, like so:

[UIView animateWithDuration:0.33 animations:^(void) {
    myView.frame = CGRectMake(x, y, w, h);
    [myView setNeedsLayout];
    [myView layoutIfNeeded];
}];

但是在这些动画中,如果我使用带有cornerRadius的图层绘制圆形视图,它会变成有趣的形状.我无法将动画持续时间传递给 layoutSubviews,因此我需要在 -[setBounds] 中添加正确的动画.

but during these animations, if I draw the circle view using a layer with a cornerRadius, it goes funny shapes. I can't pass the animation duration in to layoutSubviews so I need to add the right animation within -[setBounds].

推荐答案

非常感谢 David,这是我找到的解决方案.最后,它的关键是使用视图的 -[actionForLayer:forKey:] 方法,因为它在 UIView 块内使用,而不是图层的任何 -[actionForKey] 返回.

With many thanks to David, this is the solution I found. In the end what turned out to be the key to it was using the view's -[actionForLayer:forKey:] method, since that's used inside UIView blocks instead of whatever the layer's -[actionForKey] returns.

@implementation SGBRoundView

-(CGFloat)radiusForBounds:(CGRect)bounds
{
    return fminf(bounds.size.width, bounds.size.height) / 2;
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor clearColor];
        self.opaque = NO;
        self.layer.backgroundColor = [[UIColor purpleColor] CGColor];
        self.layer.borderColor = [[UIColor greenColor] CGColor];
        self.layer.borderWidth = 3;
        self.layer.cornerRadius = [self radiusForBounds:self.bounds];
    }
    return self;
}

-(void)setBounds:(CGRect)bounds
{
    self.layer.cornerRadius = [self radiusForBounds:bounds];
    [super setBounds:bounds];
}

-(id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
{
    id<CAAction> action = [super actionForLayer:layer forKey:event];

    if ([event isEqualToString:@"cornerRadius"])
    {
        CABasicAnimation *boundsAction = (CABasicAnimation *)[self actionForLayer:layer forKey:@"bounds"];
            if ([boundsAction isKindOfClass:[CABasicAnimation class]] && [boundsAction.fromValue isKindOfClass:[NSValue class]])
        {            
            CABasicAnimation *cornerRadiusAction = [CABasicAnimation animationWithKeyPath:@"cornerRadius"];
            cornerRadiusAction.delegate = boundsAction.delegate;
            cornerRadiusAction.duration = boundsAction.duration;
            cornerRadiusAction.fillMode = boundsAction.fillMode;
            cornerRadiusAction.timingFunction = boundsAction.timingFunction;

            CGRect fromBounds = [(NSValue *)boundsAction.fromValue CGRectValue];
            CGFloat fromRadius = [self radiusForBounds:fromBounds];
            cornerRadiusAction.fromValue = [NSNumber numberWithFloat:fromRadius];

            return cornerRadiusAction;
        }
    }

    return action;
}

@end

通过使用视图为边界提供的操作,我能够获得正确的持续时间、填充模式和计时功能,最重要的是委托 - 没有这些,UIView 动画的完成块就不会运行.

By using the action that the view provides for the bounds, I was able to get the right duration, fill mode and timing function, and most importantly delegate - without that, the completion block of UIView animations didn't run.

半径动画几乎在所有情况下都遵循边界动画 - 我试图解决一些边缘情况,但基本上就在那里.还值得一提的是,捏合手势有时仍然很生涩——我想即使是加速绘图仍然很昂贵.

The radius animation follows that of the bounds in almost all circumstances - there are a few edge cases that I'm trying to iron out, but it's basically there. It's also worth mentioning that the pinch gestures are still sometimes jerky - I guess even the accelerated drawing is still costly.

这篇关于如何创建平滑可调整大小的圆形 UIView?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆