Geri Borbás
Geri Borbás

Reputation: 16598

CALayers didn't get resized on its UIView's bounds change. Why?

I have a UIView which has about 8 different CALayer sublayers added to its layer. If I modify the view's bounds (animated), then the view itself shrinks (I checked it with a backgroundColor), but the sublayers' size remains unchanged.

How to solve this?

Upvotes: 112

Views: 90358

Answers (8)

In your custom view, you need declare variable for your custom layer, don't declare variable in scope init. And just init it once time, don't try set null value and reinit

class CustomView:UIView {
    var customLayer:CALayer = CALayer()
    
    override func  layoutSubviews() {
        super.layoutSubviews()
        //        guard let _fillColor = self._fillColor else {return}
        initializeLayout()
    }
    private func initializeLayout() { 
        customLayer.removeFromSuperView()
        customLayer.frame = layer.bounds
                   
        layer.insertSubview(at:0)
    }
}

Upvotes: -1

Fattie
Fattie

Reputation: 12621

2017:

The literal answer to this question:

"CALayers didn't get resized on its UIView's bounds change. Why?"

is that for better or worse:

needsDisplayOnBoundsChange

defaults to false (!!) in CALayer.

solution,

class CircularGradientViewLayer: CALayer {
    
    override init() {
        
        super.init()
        needsDisplayOnBoundsChange = true
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override open func draw(in ctx: CGContext) {
        go crazy drawing in .bounds
    }
}

Indeed, I direct you to this QA

https://stackoverflow.com/a/47760444/294884

which explains, what the hell the critical contentsScale does. You usually need to set that, when you set needsDisplayOnBoundsChange.

Upvotes: 9

Chadwick Wood
Chadwick Wood

Reputation: 2052

I used the same approach that Solin used, but there's a typo in that code. The method should be:

- (void)layoutSubviews {
  [super layoutSubviews];
  // resize your layers based on the view's new bounds
  mylayer.frame = self.bounds;
}

For my purposes, I always wanted the sublayer to be the full size of the parent view. Put that method in your view class.

Upvotes: 163

Runo Sahara
Runo Sahara

Reputation: 715

I used this in the UIView.

-(void)layoutSublayersOfLayer:(CALayer *)layer
{
    if (layer == self.layer)
    {
        _anySubLayer.frame = layer.bounds;
    }

super.layoutSublayersOfLayer(layer)
}

Works for me.

Upvotes: 17

Vinay Krishna Gupta
Vinay Krishna Gupta

Reputation: 475

Swift 3 Version

In Custom cell, Add Following lines

Declare first

let gradientLayer: CAGradientLayer = CAGradientLayer()

Then add following lines

override func layoutSubviews() {
    gradientLayer.frame = self.YourCustomView.bounds
}

Upvotes: 5

DevGansta
DevGansta

Reputation: 5844

As [Ole] wrote CALayer does not support autoresizing on iOS. So you should adjust layout manually. My option was to adjust layer's frame within (iOS 7 and earlier)

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration 

or (as of iOS 8)

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinato

Upvotes: 0

Solin
Solin

Reputation: 756

I had the same problem. In a custom view's layer I added two more sublayers. In order to resize the sublayers (every time the custom view's boundaries change), I implemented the method layoutSubviews of my custom view; inside this method I just update each sublayer's frame to match the current boundaries of my subview's layer.

Something like this:

-(void)layoutSubviews{
   //keep the same origin, just update the width and height
   if(sublayer1!=nil){
      sublayer1.frame = self.layer.bounds;
   }
}

Upvotes: 9

Ole Begemann
Ole Begemann

Reputation: 135548

Since CALayer on the iPhone does not support layout managers, I think you have to make your view's main layer a custom CALayer subclass in which you override layoutSublayers to set the frames of all sublayers. You must also override your view's +layerClass method to return the class of your new CALayer subclass.

Upvotes: 27

Related Questions