Multiple views with shadow and rotation becomes very slow - ios

I'm trying to make multiple stacks of uiview's. Each of those UIView's with a shadow, slight rotation and scale. As a test I'm making 10 stack's of 10 views. Drawing all this is very slow.. Is there a good way to optimise this? I tried making the shadow and background from a image but that was ugly and equally as slow. I put these stacks in a UIScrollView
for (int k = 0; k < 9; k++) {
for (int i = 0; i < 9; i++) {
UIView *stack = [[UIView alloc] initWithFrame:CGRectMake((i * 106), (k * 106), 110, 110)];
for (int i = 0; i < 10; i++) {
CardView *cardView = [[CardView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
//cardView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"card_background.png"]];
cardView.layer.shadowOffset = CGSizeMake(0, 0);
cardView.layer.shadowOpacity = 0.3f;
cardView.layer.shadowPath = [UIBezierPath bezierPathWithRect:(CGRect){CGPointZero, cardView.layer.bounds.size}].CGPath; // Set shadow path, without this the performance is *really* bad
cardView.transform = CGAffineTransformMakeRotation(((arc4random() % 20) - 10.0f) / 100.0f);
cardView.transform = CGAffineTransformScale(cardView.transform, 0.35, 0.35);
cardView.layer.shouldRasterize = YES;
cardView.layer.rasterizationScale = 0.5;
cardView.center = CGPointMake(55, 55);
[stack addSubview:cardView];
}
[_backgroundView addSubview:stack];
}
}
Edit 1; Tried some stuff, disabling rasterzation isn't helping much, disabling the shadow doesn't help much either, rotation and scaling are recourse intensive with this much uiview's too. Would async drawing (one stack at the time) be an option?
Edit 2; Guess making 100 UIView's is just slow anyway. I'll report back if I've found a better (I guess async or something like that) solution

Are you using these images in some sort of animation? If not you can try turning off rasterizing to increase performance.
cardView.layer.shouldRasterize = NO;

Shadows are very computationally intensive. If shouldRasterize doesn't do it, make a UIImage for the shadow, and if your views can change size, load the image with resizableImageWithCapInsets.

Related

Stronger shadow on iOS

I am trying to animate a button with some glow effect.
So far this is what I got:
self.glowLayer = [[CALayer alloc] init];
self.glowLayer.contents = (__bridge id _Nullable)(self.currentBackgroundImage.CGImage);
self.glowLayer.opacity = 0; // set to 1 with animation.
self.glowLayer.shadowColor = [UIColor vtoPinkColor].CGColor;
self.glowLayer.shadowOffset = CGSizeZero;
self.glowLayer.shadowRadius = 5;
self.glowLayer.shadowOpacity = 1;
self.glowLayer.rasterizationScale = [UIScreen mainScreen].scale;
self.glowLayer.shouldRasterize = YES;
[self.layer addSublayer:self.glowLayer];
However, I find the glow effect not strong enough.
Of course I can change the shadow radius to make it wider but it only makes the shadow "dilute" instead of getting stronger.
How should I proceed ?
changing the value of
self.glowLayer.shadowOpacity 1 will work for you.
and also try Translucent = No

UIPageControl: dots with rectangular format

I use native UIPageControl in my objective-c application and I want to have a rectangular dots instead of circular one. I searched about that and for a library providing this but I didn’t found anything.
I tried to do it like this but it has no effect:
-(void)updateDotsFormat {
for (int i = 0; i < [self.pageControl.subviews count]; i++)
{
UIView* dot = [self.pageControl.subviews objectAtIndex:i];
UIView* dotView = [[UIView alloc] init];
dotView.backgroundColor = [UIColor blueColor];
CGRect frame = dotView.frame;
frame.size.width = 50;
frame.size.height = 20;
dotView.frame = frame;
[dot addSubview:dotView];
}
}
How can I set the dots format to rectangle?
and I want to have a rectangular dots instead of circular one. I searched a that and for a library providing this but I didn’t found anything
Well, you would have to design your own view/control, since UIPageControl has no such ability. There are libraries for such a thing, but asking for one is not permitted on Stack Overflow; or you can write it yourself.

skshapenode adding two nodes?

I'm not sure whether there's an issue with the implementation or the way I'm using it.
However, it's presenting over 2,400 nodes when it should be ~ 1,250
-(void)drawWeb {
//get distance of 50 across
int distanceMargin = _background.frame.size.width/50;
NSLog(#"%i", distanceMargin);
__block int xCounter = distanceMargin;
__block int yCounter = 0;
NSArray *alphabet = [[NSArray alloc] initWithObjects:#"A",#"B",#"C",#"D",#"E",#"F",#"G",#"H",#"I",#"J",#"K",#"L",#"M",#"N",#"O",#"P",#"Q",#"R",#"S",#"T",#"U",#"V",#"W",#"X",#"Y",#"Z", nil];
for (int i = 0; i < 25; i++) {
for (int j = 0; j < 50; j++) {
webPoint *shape = [webPoint shapeNodeWithCircleOfRadius:1];
shape.position = CGPointMake(xCounter, _background.frame.size.height - yCounter);
shape.fillColor = [UIColor blackColor];
shape.alpha = 1;
shape.webPoint = [NSString stringWithFormat:#"%#%i", alphabet[i], j];
shape.positionX = xCounter;
shape.positionY = yCounter;
shape.name = #"webPoint";
[_background addChild:shape];
xCounter = xCounter + distanceMargin;
}
xCounter = distanceMargin;
yCounter = yCounter + distanceMargin;
}
}
By default when creating SKShapeNode, strokeColor is already set and it requires 1 node + 1 draw call. In your example you are setting fillColor too, which requires additional node and additional draw pass.
SKShapeNode is not performant solution in many cases (it should be used sparingly), and in your case, if you enable debugging labels you will probably see that there are a lot of draw calls required for scene rendering. Debugging labels can be enabled in view controller (showsDrawCount = YES)
Draw calls are directly affecting on performance in SpriteKit applications and you should try to keep that number as low as possible. One way would be using SKSpriteNode and texture atlases.

CAEmitter any way to insert self-animated particles?

I have a CAEmitterCell :
CAEmitterCell * spriteCell = [CAEmitterCell emitterCell];
spriteCell.contents = (id) [[UIImage imageNamed:#"emitterSprite.png"] CGImage];
I would like the sprite to be an animated png sequence (or so) instead of a single image.
The only technique I know makes use of an UIImageView :
NSMutableArray *animatedImagesArray = [NSMutableArray new];
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(...)];
for (int i = 1; i <= 10; i++)
{[animatedImagesArray addObject:[UIImage imageNamed:[NSString stringWithFormat:#"sequenceNo-%d%#", i, #".png"]]];}
imageView.animationImages = animatedImagesArray;
[view addSubview:imageView];
CAEmitterCell.contents doesn't seem to accept UIImageView. Is there a way to achieve this ?
For reasons of performance in the operating system what you are trying to do is not possible. If you have simple transformations like rotation, scale, colour tint you can edit those values in the properties of your the CAEmitterCell. If you require a more complex animation you will need to use a custom emitter that can handle UIImageViews. But be mindful of the performance implications.

Spawn UIImageView So That It Does Not Intersect With Existing UIImageViews

Basically, I have an app where a user dodges bombs and collects coins. When a user collects a coin, another coin is spawned. I want each coin to not spawn on a bomb. (By the way these are all uiimageviews). In the code below, I have an array of my bombs called bombArray and the UIImageview called "one" is the coin UIImageView. I know the code below doesn't work, but what other method(s) could I use? Thanks, and here is the code:
UIImageView *one = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"goldCoin.png"]];
CGRect rectOne = CGRectMake(arc4random() % (900), arc4random() % (700), 40, 40);
[one setFrame:rectOne];
[self.view addSubview:one];
for (UIImageView* two in bombArray)
{
while (CGRectIntersectsRect(two.frame, one.frame))
{
one.center=CGPointMake(arc4random() % (900), arc4random() % (700));
}
}
*Note: I completely understand why the code above does not work, but I cannot find another solution to my problem.
First of all to make such a thing I'd use the new SpriteKit that gives you physics (and thus collision detection) out of the box.
But if you want to do this with standard UIViews what you can do for being absolutely sure that images don't collide between them is to divide the container in a grid and then loop through each cell of your grid and "flip a coin" to randomly put a coin or not in that cell. Then if you want to fill more space instead of dividing for rectangular cells you can divide for hexagonal cells, in this way you will cover more space.
So a fast and absolutely not too much checked version could be something like this:
int x = 0;
int y = 0;
int side = 40;
int it = 0;
do {
do {
CGRect imageFrame = CGRectMake(x, y, side, side);
UIImageView* imageView = [[UIImageView alloc] initWithFrame:imageFrame];
imageView.layer.borderColor = [UIColor blackColor].CGColor;
imageView.layer.borderWidth = 1.0;
imageView.layer.cornerRadius = side / 2;
imageView.layer.masksToBounds = YES;
[self.containerView addSubview:imageView];
BOOL flip = arc4random() & 0x1;
if (flip) {
[imageView setImage:[UIImage imageNamed:#"coin_image"]];
}
} while ((x+=side) < (self.containerView.frame.size.width - side / 2));
it++;
if (it % 2 != 0) {
x = side / 2;
} else {
x = 0;
}
} while ((y+=(side * 0.85)) < (self.containerView.frame.size.height - side) );
Mind that this is NOT efficient, it is executed in the main thread and doesn't take in account possible (actually probable) memory constraints.

Resources