0

I want to create a UIView subclass that masks itself so that any child view's I add to it are cropped by a circle. I want this view and it's child views to be defined in IB, so that I can easily define layout constraints to the children. So far I have the following...

@interface BubbleView ()
// eg: this is an example of a child view that would be "under" a mask
@property(weak,nonatomic) IBOutlet UIImageView *imageView;
@end

@implementation BubbleView

// not really sure if this kind of init is the right pattern, but it seems to work and 
// I don't think this is my current problem??
+ (instancetype)bubbleViewFromNib {
    BubbleView *view = [[NSBundle mainBundle] loadNibNamed:@"BubbleView" owner:nil options:nil][0];

    UIImage *_maskingImage = [UIImage imageNamed:@"circlemask"];
    CALayer *maskingLayer = [CALayer layer];
    [view.layer addSublayer:maskingLayer];

    maskingLayer.contents = (__bridge id _Nullable)(_maskingImage.CGImage);
    view.layer.mask = maskingLayer;

    return view;
}

- (void)layoutSubviews {
    self.layer.mask.frame = self.bounds;
}

(note: I give the view a purple color in IB so I can see what's going on)

This almost works, but when the owning view controller resizes this view, like this...

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    BubbleView *b = (BubbleView *)[self.view viewWithTag:222];
    //b.transform = CGAffineTransformScale(b.transform, 1.2, 1.2);
    b.frame = CGRectInset(b.frame, -30,-30);
}

The mask does something weird: It stays small "jumps" to the upper left corner of the view, then very quickly animates to the correct size (bigger by 30,30). Why would it animate?

Before...

enter image description here

Fast animation like this...

enter image description here

Placing NSLog in layoutSubviews, I notice it gets called twice, which is strange, but still not enough times to explain the quick animation.

Also, when I change the transform instead of the frame, it resizes perfectly, with no animation. But I need to do both frame and transform changes.

Can someone tell me where I've gone wrong?

user1272965
  • 2,814
  • 8
  • 29
  • 49

1 Answers1

0

When setting an animatable property of a layer, unless that layer is a UIView's primary layer, implicit animation is the default. Moreover, the frame property is merely a facade for the bounds and position properties. For this reason, you should never set a layer's frame directly; always set the bounds and position yourself. If you don't want animation, you'll need to turn off implicit animation for these properties; the simplest way is to turn it off entirely for the current CATransaction (setDisableActions to true), but there are more subtle and precise ways to accomplish the same thing.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Thanks very much. To clarify, I should do `[CATransaction begin'`, then `setDisableActions` then layer changes, then `commit`? Also, when trying it, I need to set the position to half the view width and height and the bounds to 0,0,width,height for it to work. Why is that? Would you mind expressing this good advice in code? – user1272965 Jul 27 '17 at 17:19
  • It would be sufficient to call `CATransaction.setDisableActions` if you want to turn off all implicit animations for the rest of this transaction. – matt Jul 27 '17 at 17:20
  • In answer to the second question: that's the transform at work. That's what a transform _is_. That's another reason why you should not be messing with the `frame`; `frame` is meaningless when there's a transform (and the documentation does shout at you about this). – matt Jul 27 '17 at 17:21
  • Ok, thanks. Don't really understand the transform point. But it's working. – user1272965 Jul 27 '17 at 17:22
  • Sorry to bother you, but now when I do want to animate it inside of UIView animation block, a get a different version of the same problem: the view moves down and right by half it's bounds resizes, then animates back. Is there any way I can have no animation when I set bound/frame/transform by assignment, but yes animation when I assign in an animation block? – user1272965 Jul 27 '17 at 17:26
  • That doesn't sound like the same problem at all. You're now talking about UIView animation, not layer animation. And keep in mind that animating / changing a UIView has no effect on its layer. But there's no UIView animation in your question! So I would suggest that this is a completely new question and that you should ask it as a completely new question. – matt Jul 27 '17 at 17:47
  • I removed the block animation from the VC because I thought it was causing the original unwanted jump to the origin. I'll publish a new question and link it here. Thanks. – user1272965 Jul 27 '17 at 19:04
  • I've done further research, and posted a more comprehensive question here. Thanks in advance your help...https://stackoverflow.com/questions/45380247/circular-uiview-from-nib-with-animated-and-non-animated-changes – user1272965 Jul 28 '17 at 18:36