insertSublayer atIndex puts things on top of other layers - objective-c

I'm using ColorBGView and calling methods inside of it to make gradients. But if I attach two to my layer, it overlaps everything else I attach afterward, and I don't understand why.
CAGradientLayer *bgLayer = [ColorBGView pinkSky1];
bgLayer.frame = CGRectMake(0, 0, 1024, 768);
[viewController.view.layer insertSublayer:bgLayer atIndex:0];
//If I insert this sublayer (even at a lower index)
//it overlaps everything I attach afterward
CAGradientLayer *bgLayer2 = [ColorBGView darkCave];
bgLayer2.frame = CGRectMake(0, 0, 1024, 768);
[viewController.view.layer insertSublayer:bgLayer2 atIndex:-10];
// add custom view
parallaxView = [[ParallaxView alloc] init];
parallaxView.frame = CGRectMake(0, 0, 1024, 768);
[viewController.view insertSubview:parallaxView atIndex:1];
[parallaxView release];

Related

MBProgressHUD with CustomView: Why can't I animate my custom view?

This somehow doesn't work...why? How can I achieve a spinning custom propeller without making a gif out of the animation?
-(UIView *)propTest
{
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 37, 37)];
UIImageView *movablePropeller = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 37 , 37)];
movablePropeller.image = [UIImage imageNamed:#"MovablePropeller"];
[view addSubview:movablePropeller];
movablePropeller.center = view.center;
CABasicAnimation *rotation;
rotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
rotation.fromValue = [NSNumber numberWithFloat:0.0f];
rotation.toValue = [NSNumber numberWithFloat:(2 * M_PI)];
rotation.cumulative = true;
rotation.duration = 1.2f; // Speed
rotation.repeatCount = INFINITY; // Repeat forever. Can be a finite number.
[movablePropeller.layer addAnimation:rotation forKey:#"Spin"];
return view;
}
-(void)presentMyHud
{
MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView:self.view];
[self.view addSubview:hud];
hud.mode = MBProgressHUDModeCustomView;
hud.customView = [self propTest];
hud.detailsLabelText = #"Getting data";
[hud show:YES];
}
But my propeller stays static...
If the propeller is not spinning, that can happen if you didn’t immediately add this view to the view hierarchy. Generally, it’s prudent to add the view to the view hierarchy before you addAnimation.
- (UIView *)addPropellerToView:(UIView *)view {
UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 37, 37)];
// make sure to add it to the view hierarchy
[view addSubview:containerView];
// if you want to center it, set its `center` to be in the middle of the `bounds` ... don't use `center`
containerView.center = CGPointMake(view.bounds.origin.x + view.bounds.size.width / 2, view.bounds.origin.y + view.bounds.size.height / 2);
// I'd just use the `bounds` of the container view; reducing the risk that we get its coordinates wrong
UIImageView *movablePropeller = [[UIImageView alloc] initWithFrame:containerView.bounds];
movablePropeller.image = [UIImage imageNamed:#"MovablePropeller"];
movablePropeller.contentMode = UIViewContentModeScaleAspectFill;
[containerView addSubview:movablePropeller];
// never set the center of a view to the center of its super view ... those are two different coordinate systems.
// in this case, this line isn't needed at all, so I'll remove it.
//
// movablePropeller.center = containerView.center;
CABasicAnimation *rotation;
rotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
rotation.fromValue = #(0);
rotation.toValue = #(2 * M_PI);
rotation.cumulative = true;
rotation.duration = 1.2f;
rotation.repeatCount = INFINITY;
[movablePropeller.layer addAnimation:rotation forKey:#"Spin"];
return containerView;
}
Yielding:
Some unrelated observations:
If you want to center view A in the middle of view B, set view A’s center to coordinates to the midpoint of B’s bounds, not to B’s center. E.g., you never want to do:
A.center = B.center;
What you want is:
A.center = CGPointMake(B.bounds.origin.x + B.bounds.size.width / 2, B.bounds.origin.y + B.bounds.size.height / 2);
I know it looks like it should be the same thing, but it’s not. A’s center is defined, like frame, in the coordinate system of B. But B’s center is defined in the coordinate system of its superview, which might be completely different. Sometimes you won’t notice the difference (specifically if B’s origin is {0, 0}), but it suggests a misunderstanding of the different coordinate systems, and if B isn’t at {0, 0}, then everything will be wrong.
You can use NSNumber literals, replacing [NSNumber numberWithFloat:0.0f] with #(0).
You really don’t need that container view, so you could simplify the routine, like below.
- (UIView *)addPropellerTo:(UIView *)view {
UIImageView *movablePropeller = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 37, 37)];
movablePropeller.image = [UIImage imageNamed:#"MovablePropeller"];
movablePropeller.contentMode = UIViewContentModeScaleAspectFill;
[view addSubview:movablePropeller];
movablePropeller.center = CGPointMake(view.bounds.origin.x + view.bounds.size.width / 2, view.bounds.origin.y + view.bounds.size.height / 2);
CABasicAnimation *rotation;
rotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
rotation.fromValue = #(0);
rotation.toValue = #(2 * M_PI);
rotation.cumulative = true;
rotation.duration = 1.2f;
rotation.repeatCount = INFINITY;
[movablePropeller.layer addAnimation:rotation forKey:#"Spin"];
return movablePropeller;
}

why I get the converse mask with the "CGMutablePathRef" method

I draw a view . The views frame = (0,0,44,44) and the views background color is black.
I want add a mask to make the view looks like this : the mask is a small circle in the middle of the view . but I get a converse result , only the middle of the view is not masked. the wrong result like this
my code is :
aView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 44, 44)];
[aView.layer setBackgroundColor:[[UIColor blackColor] CGColor]];
CAShapeLayer *mask = [[CAShapeLayer alloc] init];
mask.frame = aView.bounds;
CGMutablePathRef p2 = CGPathCreateMutable();
CGPathAddEllipseInRect(p2, NULL, CGRectInset(mask.bounds, 10, 10));
mask.path = p2;
aView.layer.mask = mask;
CGPathRelease(p2);
[self addSubview:aView];
what is wrong with the code ? thanks.
First, add a rectangle to the masking path.
Second, set the fillRule property of shape layer to kCAFillRuleEvenOdd, so that the center part is left hollowed.
aView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 44, 44)];
[aView.layer setBackgroundColor:[[UIColor blackColor] CGColor]];
CAShapeLayer *mask = [[CAShapeLayer alloc] init];
mask.fillRule = kCAFillRuleEvenOdd;
mask.frame = aView.bounds;
CGMutablePathRef p2 = CGPathCreateMutable();
CGPathAddRect(p2, &CGAffineTransformIdentity, mask.bounds);
CGPathCloseSubpath(p2);
CGPathAddEllipseInRect(p2, NULL, CGRectInset(mask.bounds, 10, 10));
mask.path = p2;
aView.layer.mask = mask;
CGPathRelease(p2);

How to add UIView subviews in a view aligned like 3x3 table

I have created a main View and in that I add 9 UIMyView that I subclassed from UIView.
I add instances of that UIMyViews using NSBundle to load the nib file and addSubview in the main view controller, of course all of these views are appeared in the upper left corner of the super view one hiding the other and the last one to be visible, which either I need to position them somehow using points in super view or create something like a table and add to its cells the UIMyViews?!
Imagine you have a table and put 9 deck cards positioned 3 rows and 3 columns (9 cards).
This is what I need to achieve.
[EDIT]
Check this image: http://www.wpclipart.com/recreation/games/card_deck/cards_symbols/playing_card_symbols.png
I just need 3 rows and 3 columns.
Anyone can suggest best practice for this kind of operation?
Afterwards just to have in mind I want to implement various animations and effects on these UIMyViews while draged, touched and reordering etc.
Thank you.
If you want to loop through all the views and position them in a grid, you can so like this:
// Array of 9 views
NSArray *views = [NSArray arrayWithObjects:[[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)], [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)], [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)], [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)], [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)], [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)], [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)], [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)], [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)], nil];
int count = 0;
int maxPerRow = 3;
int row = 0;
float spacing = 100.0;
for(UIView *view in views)
{
// Only added to tell the difference between views, make sure to import QuartzCore or remove the next 3 lines
view.backgroundColor = [UIColor colorWithRed:.5 green:0 blue:0 alpha:1];
view.layer.borderWidth = 1;
view.layer.borderColor = [UIColor whiteColor].CGColor;
// position view
view.frame = CGRectMake(count*spacing, row * spacing, view.frame.size.width, view.frame.size.height);
[self.view addSubview:view];
if(count % maxPerRow == maxPerRow-1)
{
count = 0;
row++;
}
else
{
count++;
}
}
That will give you something like this:
If you want to add them all manually you just set the frame of each of the subviews prior to adding to the main view:
CustomView *view = [[CustomView alloc] initWithFrame:CGRectMake(...)];
There are "smarter" ways to approach this than setting everything manually. iOS6 contains some features for this and there's also gridviews made such as:
GMGridView
DTGridView
AQGridView

Using a CALayer as a mask to a backing layer of a GLKView blocks touch events?

I'm trying to use a CAShapeLayer as a mask to the backing layer of a GLKView like so:
CGRect circleFrame = CGRectMake(0, 0, 800, 800);
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:circleFrame];
CGPoint circleAnchor = CGPointMake(CGRectGetMidX(circleFrame) / CGRectGetMaxX(circleFrame),CGRectGetMidY(circleFrame) / CGRectGetMaxY(circleFrame));
shapeLayerMask = [[CAShapeLayer alloc] init];
shapeLayerMask.path = path.CGPath;
shapeLayerMask.anchorPoint = circleAnchor;
shapeLayerMask.frame = CGRectMake(0, 0,800, 800);
shapeLayerMask.fillColor = [UIColor blackColor].CGColor;
shapeLayerMask.backgroundColor = [UIColor clearColor].CGColor;
shapeLayerMask.opacity = 1.0f;
shapeLayerMask.hidden = NO;
self.view.layer.mask = shapeLayerMask;
But as soon as i do this, all my touch events stop firing. No more touchesBegan, touchesEnded, etc. Anyone knows why? Thanks in advance!

sub CALayer can not be displayed

my code is like this:
// layer
highlightLayer = [[CALayer alloc]init];
highlightLayer.frame = CGRectMake(0, 420, 320, 11);
highlightLayer.backgroundColor = [UIColor greenColor].CGColor;
CALayer *contentLayer = [[CALayer alloc]init];
contentLayer.frame = CGRectMake(0, 420, 80, 11);
contentLayer.backgroundColor = [UIColor redColor].CGColor;
[highlightLayer addSublayer:contentLayer];
[contentLayer release];
but this sublayer does not appear. i don't know why.
Because contentLayer's frame is outside highlightLayer's bounds (0, 0, 320, 11). contentLayer's frame is expressed in highlightLayer's coordinate system. I think you should simply adjust contentLayer's frame origin.