Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIKit Dynamics and CALayers decoupled from UIView

Is it possible to not use UIViews when playing with UIKit Dynamics, and instead bind the physics properties, anchors, forces etc to CALayers all within one UIView?

According to this page by Ash Farrow:

UIDynamicItem is a protocol that defines a centre, a bounds, and a transform (only two-dimensional transforms are used). UIView conforms to this protocol, and is the most common use of UIDynamicBehaviour. You can also use UICollectionViewLayoutAttributes with UIDynamicBehaviours, but we’re not going to cover that today.

Which seems to indicate that anything can be made to conform to the protocol UIDynamicItem, but I can't find anything about others playing around with making CALayers.

Can it be done more directly than this approach:

https://www.invasivecode.com/weblog/uikit-dynamics-layer-constraint/

like image 364
Confused Avatar asked Feb 07 '15 20:02

Confused


1 Answers

As the linked article notes, CALayer has conflicting types for these methods, so it can't directly conform to UIDynamicItem. What you'd need would be some kind of adapter layer that converted the CALayer methods into the types and meanings needed by UIDynamicItem.

Luckily, UIKit offers exactly such an adapter layer. It's called UIView. Just put each layer in a view and nest the views.

I had long thought of views as very heavyweight, particularly coming from the OS X world. So I always thought "if you want this to be fast, you'd better do it with a layer." But it turns out that views are actually quite thin and there isn't a lot of overhead vs a layer in most cases, particularly for the numbers you're talking about in iOS. See the TearOff demo for an example that does UIKItDynamics with many dozens of views without trouble.

If you really do need layers, then yes, you can definitely still do it. Just create the adapter yourself. It just needs to hold a CALayer and conform to UIKitDynamics. It doesn't have to do anything else. Something like this should work:

public class DynamicLayerItem : NSObject, UIDynamicItem {
    let layer: CALayer
    init(layer: CALayer) { self.layer = layer}

    public var center: CGPoint { get {
        return layer.position
        }
        set {
            layer.position = center
        }
    }

    public var bounds: CGRect { return layer.bounds }
    public var transform: CGAffineTransform {
        get {
            return layer.affineTransform()
        }
        set {
            layer.transform = CATransform3DMakeAffineTransform(newValue)
        }
    }
}

You'd just animate this object, and that causes the layer to animate equivalently.

like image 158
Rob Napier Avatar answered Sep 24 '22 12:09

Rob Napier