15

I have a UIViewController where I create a background gradient with

    CAGradientLayer *gradient = [CAGradientLayer layer];
    ...
    gradient.frame = frame;
    self.backGradient = gradient;
    [self.view.layer insertSublayer:gradient atIndex:0];

it works fine, later I have to send the subview _selectionFrame of my view controller to back:
(I often need to send the _selectionFrame to back and to front, for animation and drawing purpose)

self is the viewController:

[self.view sendSubviewToBack:_selectionFrame]; 

However this sends the _selectionFrame behind the gradient. I want it just above the gradient but below every other subview. The problem is that the gradient is not a view, so i cannot use the functions for the gradient.
I would like to call [self.view sendSubviewToBack:gradient];

But this does not work.

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
AlexWien
  • 28,470
  • 6
  • 53
  • 83

3 Answers3

28

.layer has a sublayers property which is a simple array. So you can insert at any index you want. If you want your new layer to be at the back, just insert it at 0, like this:

let newLayer = CALayer() // your layer that you want at the back
view.layer.insertSublayer(newLayer, at: 0)
Senõr Ganso
  • 1,694
  • 16
  • 23
15

Create a view that contains only the gradient

Khanh Nguyen
  • 11,112
  • 10
  • 52
  • 65
4

The simplest solution is not to insert the gradient layer as a sublayer of self.view.layer. Instead, it should be self.view.layer. A UIView subclass has a class method layerClass that lets you say what class its layer should be.

For example, in your UIView subclass you would add this:

override class var layerClass: AnyClass {
    return CAGradientLayer.self
}

And when referencing self.layer within the view, wrap it a guard let or if let to convert the type like such:

guard let layer = self.layer as? CAGradientLayer else { ... }

Or, it could be the layer of some other subview of self.view, again easily arranged by using the same class method layerClass.

Otherwise, you will have to use layer commands, not view commands, to order the sublayers of self.view. That includes its subviews. In other words, if v is a subview of self.view, then v.layer is a sublayer of self.view.layer and you can order it among its sublayers by rearranging the sublayers array.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • self.view.layer = self.backGradient; does not works self.view.layer is read only – AlexWien Jun 22 '13 at 00:59
  • You didn't read what I said. You must use a custom UIView and implement `layerClass` so that the UIView is "born" with a CAGradientLayer as its layer. – matt Jun 22 '13 at 01:11
  • Thanks interesting, but more complex than necessary (for me) – AlexWien Jun 22 '13 at 01:38
  • Thank you! this was the only answer that ended up solving this for me. I think some code examples of this would greatly help in other using this solution, so I'm going to propose an edit with an example – bplattenburg Nov 05 '19 at 21:51
  • 1
    @bplattenburg Good edit, I just cleaned up the code formatting. – matt Nov 05 '19 at 22:14