How do I create a smoothly resizable circular UIView?

前端 未结 4 1609
温柔的废话
温柔的废话 2020-12-13 22:09

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 <

4条回答
  •  既然无缘
    2020-12-13 22:55

    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)actionForLayer:(CALayer *)layer forKey:(NSString *)event
    {
        id 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
    

    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.

提交回复
热议问题