首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何创建一个平滑的可调整大小的循环UIView?

如何创建一个平滑的可调整大小的循环UIView?
EN

Stack Overflow用户
提问于 2012-05-19 21:42:05
回答 4查看 8.2K关注 0票数 10

我试图创建一个UIView,它显示了一个半透明的圆圈,在它的边界内有一个不透明的边框。我希望能够以两种方式改变边界-在一个-[UIView animateWithDuration:animations:]块内和在一个捏手势识别器动作,这几次触发每秒。我尝试了三种基于其他答案的方法,没有一种是合适的。

  1. layoutSubviews中设置视图层的角半径可以提供平滑的翻译,但是视图在动画过程中不会保持循环,似乎cornerRadius是不可动的。
  2. drawRect:中绘制圆圈会给出一个一致的圆形视图,但是如果圆圈变得太大,那么在紧要关头调整大小就会变得不稳定,因为设备花了太多的时间重新绘制圆圈。
  3. 添加一个CAShapeLayer并在layoutSublayersOfLayer中设置它的path属性,因为path不是隐式可动画的,所以它不会在UIView动画中动画。

有没有一种方法可以让我创建一个连续的圆形和平滑的可调整大小的视图?还有其他类型的图层可以用来利用硬件加速吗?

更新

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

代码语言:javascript
运行
复制
-(void)setBounds:(CGRect)bounds
{
    self.layer.cornerRadius = fminf(bounds.size.width, bounds.size.height) / 2.0;
    [super setBounds:bounds];
}

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

代码语言:javascript
运行
复制
-(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;
}

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

代码语言:javascript
运行
复制
[UIView animateWithDuration:0.33 animations:^(void) {
    myView.frame = CGRectMake(x, y, w, h);
    [myView setNeedsLayout];
    [myView layoutIfNeeded];
}];

但是在这些动画中,如果我用一个带有cornerRadius的图层绘制圆圈视图,它会变成奇怪的形状。我不能将动画持续时间传递给layoutSubviews,所以我需要在-[setBounds]中添加正确的动画。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-06-05 17:33:17

非常感谢大卫,这是我找到的解决办法。最后,关键是使用视图的-[actionForLayer:forKey:]方法,因为这是在UIView块中使用的,而不是层的-[actionForKey]返回的任何东西。

代码语言:javascript
运行
复制
@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动画的完成块就无法运行。

在几乎所有情况下,radius动画都遵循边界--有几个边缘情况是我正在尝试解决的,但它基本上是存在的。还值得一提的是,捏手势有时仍然是不稳定的--我想即使是加速抽签也是代价高昂的。

票数 10
EN

Stack Overflow用户

发布于 2012-05-20 14:02:11

正如动画组在“iOS视图编程指南”中所说的那样

UIKit和核心动画都提供对动画的支持,但是每种技术提供的支持级别不同。在UIKit中,动画使用UIView对象执行。

可以使用旧的属性()动画的属性的完整列表

代码语言:javascript
运行
复制
[UIView beginAnimations:context:];
[UIView setAnimationDuration:];
// Change properties here...
[UIView commitAnimations];

或者是新的

代码语言:javascript
运行
复制
[UIView animateWithDuration:animations:];

(您正在使用的)是:

  • 框架
  • 居中
  • 转换(CGAffineTransform,而不是CATransform3D)
  • alpha
  • backgroundColor
  • contentStretch

令人困惑的是,您还可以在UIView动画块内的层上动画化相同的属性,即帧、边界、位置、不透明度、backgroundColor。

同一节接着说:

在您希望执行更复杂的动画或UIView类不支持的动画的地方,您可以使用核心动画和视图的底层来创建动画。因为视图和层对象是复杂地链接在一起的,所以对视图层的更改会影响视图本身。

在下面的几行中,您可以从下面看到的核心动画可动画属性列表中看到:

  • 图层的边框(包括图层的角是否是圆形的)

要达到你所追求的效果,至少有两个好的选择:

  • 动拐角半径
  • 使用CAShapeLayer并动画路径

这两种方法都要求你用核心动画来制作动画。如果需要多个动画作为一个整体运行,您可以创建一个CAAnimationGroup并向其添加一个动画数组。

更新:

用尽可能少的代码更改来修复一些事情,可以通过在图层上做“同时”的角半径动画来完成。我在同一时间放置引号,因为不能保证不属于同一组的动画会在完全相同的时间完成。根据您正在做的其他动画,最好只使用基本动画和动画组。如果您要对同一视图动画块中的许多不同视图应用更改,那么也许可以查看CATransactions。

下面的代码动画框架和角半径,很像你所描述的。

代码语言:javascript
运行
复制
UIView *circle = [[UIView alloc] initWithFrame:CGRectMake(30, 30, 100, 100)];
[[circle layer] setCornerRadius:50];
[[circle layer] setBorderColor:[[UIColor orangeColor] CGColor]];
[[circle layer] setBorderWidth:2.0];
[[circle layer] setBackgroundColor:[[[UIColor orangeColor] colorWithAlphaComponent:0.5] CGColor]];
[[self view] addSubview:circle];

CGFloat animationDuration = 4.0; // Your duration
CGFloat animationDelay = 3.0; // Your delay (if any)

CABasicAnimation *cornerRadiusAnimation = [CABasicAnimation animationWithKeyPath:@"cornerRadius"];
[cornerRadiusAnimation setFromValue:[NSNumber numberWithFloat:50.0]]; // The current value
[cornerRadiusAnimation setToValue:[NSNumber numberWithFloat:10.0]]; // The new value
[cornerRadiusAnimation setDuration:animationDuration];
[cornerRadiusAnimation setBeginTime:CACurrentMediaTime() + animationDelay];

// If your UIView animation uses a timing funcition then your basic animation needs the same one
[cornerRadiusAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];

// This will keep make the animation look as the "from" and "to" values before and after the animation
[cornerRadiusAnimation setFillMode:kCAFillModeBoth];
[[circle layer] addAnimation:cornerRadiusAnimation forKey:@"keepAsCircle"];
[[circle layer] setCornerRadius:10.0]; // Core Animation doesn't change the real value so we have to.

[UIView animateWithDuration:animationDuration
                      delay:animationDelay
                    options:UIViewAnimationOptionCurveEaseInOut 
                 animations:^{
                     [[circle layer] setFrame:CGRectMake(50, 50, 20, 20)]; // Arbitrary frame ...
                     // You other UIView animations in here...
                 } completion:^(BOOL finished) {
                     // Maybe you have your completion in here...
                 }]; 
票数 17
EN

Stack Overflow用户

发布于 2017-06-22 19:59:24

从iOS 11开始,如果在动画块中更改cornerRadius,则UIKit会激活它。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10669051

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档