How to detect a transform-only UIView? - ios

UIStackView is a transform-only view. This means it is not possible to perform operations on the view's layer.
Is there a general-case way to detect if a UIView is a transform-only view?

Actually, transform-only view is view which has CATransformLayer layer.
So the question should be How to detect a UIView which has transform-only layer? and the answer is very simple, check class's type of view.layer.
For example, when you want to change cornerRadius on stackView.layer, it will through a warning like
changing property cornerRadius in transform-only layer, will have no effect
It's not because something which is called transform-only view, it's because you are changing cornerRadius on a CATransformLayer and cornerRadius property is ignored according to Apple document for CATransformLayer.
Only the sublayers of a transform layer are rendered. The CALayer properties that are rendered by a layer are ignored, including: backgroundColor, contents, border style properties, stroke style properties, etc.
Something you maybe wrong is It's possible to perform operations on the view's layer but not all of them.
How to detect if a UIView is a transform-only view? - Use below extension
extension UIView {
func isTransformOnlyView() -> Bool {
return self.layer.isKind(of: CATransformLayer.self)
}
}
Usage
stackView.isTransformOnlyView()

Related

Swift: remove layer, without removing UIView in UITableViewCell

I have UITableViewCell with UIView in it.
I made some CABasicAnimation and attach it to new CAShapeLayer, then I add this layer to my super layer in my UITableViewCell:
self.layer.addSublayer(myLayer!)
All nice except that myLayer (and his animation) showing above my UIView.
I want that label be below UIView.
I achieve this by adding my UIView layer the same way:
self.layer.addSublayer(myViewLayer!)
In this case, my UIView layer be on the top of the CAShapeLayer with animation.
But I have a problem, I need to remove layer of UIView - myViewLayer because it violates width of the UIView when scroll.
When animation is done, and I need to remove layers, I can remove CAShapeLayer - myLayer without troubles.
myLayer?.removeFromSuperlayer()
But when I try to do the same with myViewLayer, my UIView removed too. But I don't want that, I need my UIView on screen and in my view hierarchy.
I don't understand why, if look to self.layer.sublayers I see this (before adding layers):
[<CALayer: 0x7fd1f0d4fe40>, <CALayer: 0x7fd1f0ddc950>]
And after animation done and myLayer is removed:
[<CALayer: 0x7fd1f0d4fe40>, <CALayer: 0x7fd1f0ddc950>, <CAGradientLayer: 0x7fd1f0de3660>]
As you can see CAGradientLayer is a layer of my UIView. So, I haven't it before I manually add it to sublayers array. How I can remove it, without removing my UIView?
OR how can I add myLayer below UIView?
EDIT
In few words I have this layer hierarchy before animation:
[<CALayer: 0x7fd1f0d4fe40> <- (I think this is UITableViewCell layer), <CALayer: 0x7fd1f0ddc950> <- (then, this is UITableViewCell content view layer)]
In the same time, I have this view hierarchy:
UITableViewCell -> Content View -> UIView
I need to add new layer with animation, below UIView (I want that UIView partially cover that new layer with animation). How I can do this?
I haven't my UIView layer in the UITableView layer hierarchy, so I can't just add layer with animation using addLayer:below: and so on.
I found the problem. I made a simple mistake. I used self.layer.sublayers, but I must use self.contentView.layer.sublayers this is my mistake.
When I found that, I was able to fix another issues and use addLayer:below:.
So, if you don't see a layer that must be in sublayer of your object, means that you're looking at the wrong place, think where it can be else and you'll find it, like in UITableView you need to work with contentView.
You can add myLayer below UIView.layer by setting
myLayer.zPosition = -1
The example below could have fixed your problem as well.
First solution:
self.avPlayerLayer.zPosition = 0 // or -1
self.view.layer.addSublayer(self.avPlayerLayer)
Or second solution
self.view.layer.insertSublayer(self.avPlayerLayer, below: self.button1.layer)

UILabel text getting compressed/stretched upon applying scale transform

Am applying a scale transform to UIView (CGAffineTransformMakeScale) which contains UILabel as subview, as I apply transform, the label text gets stretched/compressed as shown in attachment, How can I make the UILabel to render the text correctly even after applying transform.
Apply the transform on the UIView and then add the UILabel as subview. The way you are doing will always transform the UIView and all his subviews. You can also change the UIView frame instead of applying a transform.

Why adding sublayer overlaps subviews?

For example I have a view with UILabel as subview. If at some time I add sublayer with some background color to this view's layer, then this label will disappear. Can someone explain this behavior?
When you add your UILabel as a subview of your view it is adding your UILabel's layer as a sublayer of your view's layer. So when you add another sublayer to your view's layer it will be on top of your UILabel's layer.
You can either add your background layer before you add the UILabel or do:
Swift
view.layer.insertSublayer(backgroundLayer, below: yourLabel.layer)
Objective C
[view.layer insertSublayer:backgroundLayer below:yourLabel.layer]
and it should put the background behind the label.
What worked for me was to insert the layer at a specific index like this:
view.layer.insertSublayer(backgroundLayer, at: 0)

Autorotation issue with UITableViewCell and CALayer

I have a UITableViewCell subclass with backgroundView set to my own UIView object. This UIView object contains three CALayer layers. I implemented - (void)layoutSubviews where I update all my CALayer layers. The problem is autorotation.
When I rotate from landscape to portrait mode there's this cosmetic issue:
During the animation, all my CALayer layers are as narrow as in portrait mode.
It seems that this guy is right:
When layoutSubviews gets called during an orientation change, the view's bounds are already set to what they will be at the conclusion of the rotation.
Source: How to achieve smooth animation when using single-step rotation / How do I get the new frame size at the start of rotation?
So, where should I update my layers to achieve proper autorotation? The view is already rotating them, so I suppose there's no need to do any custom animations, just adjust the size. Right?
Figured it out:
subclass CALayer + add sublayers into it
implement resizing of sublayers in CALayer's - (void)layoutSublayers method
subclass UIView + override + (Class)layerClass in it:
+ (Class)layerClass {
return [SubclassedCALayer class];
}
set the subclassed UIView as backgroundView of UITableViewCell

What is the CALayer equivalent to UIView's sizeToFit-method?

I'm searching for a way to let a CALayer resize itself whenever its sublayers change (which means either when the bounds of any sublayer change or when the sublayer array itself changes).
When i worked with views before, i managed that through implementing sizeThatFits in my custom UIView subclass, which was called automatically by sizeToFit whenever the view's subviews changed.
Since CALayer has the sizeThatFits-equivalent-method preferredSize, i was surprised not to find a sizeToFit-equivalent.
I think you need to implement(override) the - (void)layoutSublayers
...
*Subclasses can override this to
* provide their own layout algorithm, which should set the frame of
* each sublayer
- (void)layoutSublayers
{
// self.frame = aNewSubLayer.bounds
// basically do algorithm for setting frame and bounds for this(self) layer and subLayers here
}

Resources