« Older: CIKernel Tool « Older
Newer: What 'AddArcToPoint' Does »Newer »
Make a UIButton move with a CALayer
This is surprisingly easy. The solution is to move the UIButton’s layer to be a sublayer of the CALayer that you want. There are a couple of things to watch out for. I’d assume that the buttons layer must be a descendant of the layer of the UIView that you orignially add the button to.
A larger problem is caused by the way that UIViews manage their
subviews. When you call the addSubview:
method the layer of
your button is added as a sublayer of the view. The ‘subviews’
array isn’t updated. This is because the subviews array doesn’t
exist until it is needed. Instead I believe that it is generated
and cached from the sublayers of the views layer.
Here is the view immediately after adding a subview with addsubview:
:
(UIView) $0 = {
UIResponder = {
NSObject = {
isa = UIView
}
}
_layer = 0x08c58af0
_gestureInfo = nil
_gestureRecognizers = nil
_subviewCache = nil
_charge = 0
_tag = 0
_viewDelegate = 0x08c56ca0
_backgroundColorSystemColorName = nil
_countOfMotionEffectsInSubtree = 0
If I now call subviews
on the view it looks like this:
(UIView) $2 = {
UIResponder = {
NSObject = {
isa = UIView
}
}
_layer = 0x08c58af0
_gestureInfo = nil
_gestureRecognizers = nil
_subviewCache = 0x0a13b720 @"3 objects"
_charge = 0
_tag = 0
_viewDelegate = 0x08c56ca0
_backgroundColorSystemColorName = nil
_countOfMotionEffectsInSubtree = 0
If I now do [myView addSubview:(UIView *)[[UIView alloc] initWithFrame:CGRectMake (0.0, 0.0, 128, 128)]]
the view goes back to looking as it did originally
(i.e. _subviewCache = nil
).
The problem with this is that the view uses the _subviewCache
to
work out where its lowest descendant view is, so if your view
isn’t in _subviewCache
then the button won’t work.
If you know that you aren’t going to add any more subviews to your view then you can do something like this:
/* You could add several similar buttons here. */
[myView addSubview:button];
/* force myView to generated it's _subviewCache */
[myView subviews];
// You can now move the subviews layers around the layer
// hierarchy provided it's a descendant of myViews layer.
[[button layer] setPosition:CGPointMake(64, 64)];
[otherLayer addSublayer:[button layer]];
// After this if you call addSubview: on myView it will
// regenerate _subviewCache and your button will stop working.
//
If anything now causes the _subviewCache
to be regenerated then this will
break your buttons.
A better approach may be to subclass the view which you want to add movable layers to like this:
#import "JTAMovableLayersView.h"
@implementation JTAMovableLayersView
@synthesize movableLayerViews;
- (NSArray *)subviews
{
NSMutableArray *array = [[super subviews] mutableCopy];
[array addObjectsFromArray:movableLayerViews];
return array;
}
@end
Then make sure that the movableLayerViews
array contains all
the views where you want to be able to add their layer to a
different layer, and it should all work.
You can find my test project for this here.