Blurry UIView with CATransform3D only on RETINA - iphone

I'm displaying a UIView with a UILabel on it and this view&label become blurry as soon as it gets to these lines code:
CATransform3D transform = CATransform3DIdentity;
transform.m34 = (1.0/-500);
view.layer.transform = transform;
Throughout the App I use CA3DRotations and other stuff and this never happened before.
Also, I set the frame of the view and the label only using integers! So it's not a half-pixel problem or something like that, I know that that causes most blurry problems, but not mine!
On the simulator it's not blurry, iPad is not blurry, iPhone3GS is not blurry. Only on an iPhone4 with Retina display it becomes blurry. Even before I do any 3D rotations! Does anybody have a clue before I go insane?

Alright, I've found a solution.
After using a hundred different lines of code using layer properties, such as layer gravity or magnification and tons of other solutions I suddenly stumbled by accident on the following 2 lines:
self.layer.shouldRasterize = TRUE;
self.layer.rasterizationScale = [[UIScreen mainScreen] scale];
This is the solution! For everyone in the future, is your view blurry on retina displays? Use this!

Have you set the contentsScale for the layer to match [UIScreen mainScreen]. scale? Try it.

It's possible your views are 'between pixels' (eg. center is [12.5, 10]). Try rounding their location and see if that helps.

If your final landing position is intended to be flat/untransformed, simply setting the transform to CATransform3D identity will also fix the problem. Depending on how things are animated, setting a final position for one of the 3D transforms to 0.0 can still introduce rounding errors and give a fuzzy appearance.

Related

resizableImageWithCapInsets issue in iOS7

I am following MessagesTableViewController and there is method of starching of UIImage for bubble view to strech as per text size. It is working fine with older ios version but in ios7, it is displaying light color borders as we set UIEdgeInsetsMake as below.
+ (UIImage *)bubbleDefaultIncoming
{
return [[UIImage imageNamed:#"bg-chat-white.png"] makeStretchableDefaultIncoming];
}
- (UIImage *)makeStretchableDefaultIncoming
{
return [self resizableImageWithCapInsets:UIEdgeInsetsMake(15.0f,20.0f, 15.0f, 20.0f)
resizingMode:UIImageResizingModeStretch];
}
Here i attached 2 snapshot for ios6 and ios7 which describe how bubble View is behaving strange with ios7 though code is same.
and
Someone has also same issue and reported in GITHUB HERE
I reviewed code so much and it seems that there is issue with resizableImageWithCapInsets in ios7. It generates borders as we set UIEdgeInsetsMakein the method.
Anyone has idea or solution to remove the borders from bubble view and make same as ios6 bubble view?
Any help would be appreciable. Thanks in advance.
Transparent lines are added in iOS 7 when the width or height is a number with floating point. As a workaround, you can round this numbers
You need to ensure that the CGRect you are drawing the image into is an even number and not a number with a floating point.
In addition to this if you have a UITableView with TabViewCells that have different heights you also need to need to ensure those cells all have heights that are an even number and not a number with a floating point.
i can confirm that both answers are right, but since you are using the same framework as me i going to give you a snippet to help.
just floor or ceil the size of the bubble and you are good to go.
- (CGRect)bubbleFrame
{
CGSize bubbleSize = [JSBubbleView bubbleSizeForText:self.text];
return CGRectMake((self.type == JSBubbleMessageTypeOutgoing ? floor(self.frame.size.width - bubbleSize.width) : 0),
kMarginTop,
floor(bubbleSize.width),
floor(bubbleSize.height));
}
edit: the position too need to be rounded up or down, since the kMarginTop already is you only need for it when it's a outgoing bubble. peace

CALayer - Shadow causes a performance hit?

So I am doing some custom animations on my navigationcontroller and the way it pushes and pops the viewControllers.
Everything runs smooth. As soon as I add the following code (In a subclass of UINavigationController), I face a huge performance hit. After adding a shadow all animations become very laggy. Is this expected or am I doing something wrong in the code?
// This code gets called once during NavigationController initialization.
[self.view setClipsToBounds:NO];
[self.view.layer setCornerRadius:5];
[self.view.layer setShadowOffset:CGSizeMake(0, 20)];
[self.view.layer setShadowColor:[[UIColor yellowColor] CGColor]];
[self.view.layer setShadowRadius:20.0];
[self.view.layer setShadowOpacity:1];
EDIT:
Changed my shadow radius to 1 and it's still slow
self.view.layer.shouldRasterize = YES;
self.view.layer.rasterizationScale = UIScreen.mainScreen.scale;
I was recently having some issues with slow CALayer shadows, and that simple line of code fixed up everything for me!
You should expect a slowdown from adding a shadow. A shadowRadius of 20 is very high and will be especially slow.
The other key to improve shadow rendering speed: set the shadowPath property. It can help dramatically.
Using shadowPath instead of shadowOffset.
theView.layer.shadowPath = [UIBezierPath bezierPathWithRect:theView.bounds].CGPath;
Check this post: iphone - Animation's performance is very poor when view's shadow is on
Yes, shadow's are very expensive (especially a shadow that big -- play with the radius and you'll notice it makes a huge difference in the degree of slowdown you experience). One way to improve performance is to render it once to a CGImageContext and just display that image instead of having the layer re-render the shadow every time it redraws (but this doesn't work if the shadow needs to animate or something).
Swift 5.3. add this code.
myView -> UIView, collectionViewCell or tableViewCell can be.
myview.layer.shadowPath = UIBezierPath(rect: cell.bounds).cgPath
myview.layer.shouldRasterize = true
myview.layer.rasterizationScale = UIScreen.main.scale
I'm coding app with "Neumorphism" style, so much shadow and app so laggy. But use this code below, app very smooth.
viewHasShadow.layer.shouldRasterize = true
viewHasShadow.layer.rasterizationScale = UIScreen.main.scale

Added a shadow to a UIImageView on a UITableView kills performance... why?

I have a UITableView that has three UIImageView views per cell, with three cell displaying on the view at one time (for a total of nine UIImageView views). Think of it as a bookshelf. Sometimes I can have as many as 500 books.
I've added shadow to the UIImageView with code that is this:
UIImageView *itemImageView = [[UIImageView alloc] initWithFrame:CGRectMake(25, 7, 65, 75)];
itemImageView.contentMode = UIViewContentModeScaleAspectFit;
itemImageView.tag = 6;
itemImageView.layer.shadowColor = [UIColor blackColor].CGColor;
itemImageView.layer.shadowOffset = CGSizeMake(3, -1);
itemImageView.layer.shadowOpacity = 0.7;
itemImageView.layer.shadowRadius = 3.0;
itemImageView.clipsToBounds = NO;
[cell.contentView addSubview:itemImageView];
When I add the shadow code, as seen above, scrolling performance is just totally killed and becomes choppy. Each image has a different Rect so the shadow has to be created for each item as it scrolls. Anyone have any tips on how to add shadows to my images on a UITableView without having this issue?
You may see a performance improvement if you add
itemImageView.layer.shadowPath =
[UIBezierPath bezierPathWithRect:itemImageView.layer.bounds].CGPath;
But in general, layer operations like this will kill performance in a table view. I experienced exactly the same issue, and we just eliminated the shadow effect. It wasn't worth it.
You should review the WWDC videos on CoreAnimation best practices. You can request that the rasterized copy of the shadow be cached in memory. Cocoa is definitely fast enough to render these shadows on the fly without falling back to a pre-rendered image.
Example:
itemImageView.layer.shouldRasterize = YES;
Also I up-voted the answer regarding UIBezierPath. This is also mentioned in the best practices, but setting the shadow path of a CALayer is a huge performance boost. You can also create some interesting special effects by manipulating the shadow path.
Shadows are expensive and will kill your performance.
A better approach is to render the shadowed image in the background, cache/save it and display it on the view when its ready.
Edit: You way wish to look at Core Graphics / CGImage routines. Specifically CGContextSetShadowWithColor will draw you a shadow.
http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Reference/CGContext/Reference/reference.html
I have the same problem with adding shadow to labels inside a tableviewcell. I tried to layout the elements inside cellForRowAtIndexPath when the cell will be created:
if(cell == nil){
//layout cell
These optimized the scrolling a little bit, but its quite choppy.
For optimizing your pictures quality you should add also the rasterizationScale if you activated "shouldRasterize":
aLabel.layer.shouldRasterize = YES;
aLabel.layer.rasterizationScale = [[UIScreen mainScreen] scale];
Maybe somebody has some ideas how to optimize the code to get the normal iOS scrolling. thx
Do you activate reusing? If not, the cells will redone each time the view changes (e.g., during scrolling). That would eat a lot of performance for sure.
Check out my same question here: App running slowly because of UIImageViews
I would use this code:
imageView.layer.masksToBounds = NO;
UIBezierPath *path = [UIBezierPath imageView.bounds];
imageView.layer.shadowPath = path.CGPath;

Drawing shadow with Quartz is slow on iPhone & iPad. Another way?

I was pretty excited when I found out just how easy it is to add shadows to my UIViews on the iPhone/iPad.
Just add the framework in Xcode, add the import to the top of the file:
#import <QuartzCore/QuartzCore.h>
Then later:
self.contentView.layer.shadowRadius = 3.0;
self.contentView.layer.shadowOffset = CGSizeMake(-2.0, -3.0);
self.contentView.layer.shadowOpacity = 0.5;
self.contentView.layer.shadowColor = [UIColor blackColor].CGColor;
While this does create a beautiful shadow in my app, it also lags it to death now when the view is shown... even when launched outside of the debugger. Is there something I'm forgetting or is this method just not practical for larger views?
For reference, I posted a screenshot here.
You should set the shadowPath property. It is how CoreGraphics is able to optimize shadows.
For example, if your view is an opaque rectangle:
self.contentView.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.contentView.bounds].CGPath;
Thought I should make an answer because I didn't want this gem to get buried in the comments.
In addition to cobbal's answer above, which helped a lot, occulus also mentioned the following optimization for non-rectangular shadows, such as text shadows.
self.contentView.layer.shouldRasterize = YES;
// Fix visual degradation when rasterizing on high-density screens:
self.contentView.layer.rasterizationScale = [[UIScreen mainScreen] scale];

View not completely covering the entire screen

Once again I've searched for about 45 minutes for an answer to this question and I thought I might have found the answer but then the situation I was reading wasn't exactly like the one I'm running into.
when I add my view it seems that it's not completely covering the window I was able to get rid of the status bar at the top but there is a section of space at the bottom that the view is not covering
alt text http://img177.imageshack.us/img177/8844/picture1zjv.png
as you can see from the screenshot there is an orange bar...it's orange because I know what it actually is under there (it's the viewController's view but everything I try I can't seem to get the added view to cover the screen.
this is the only code that's run
(void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:mainMenu];
}
Any help would be appreciated.
There are a few things that might be going wrong:
Your frame might be too short. In viewDidLoad: you can add a debug statement to check its height:
NSLog(#"Height is %f", self.view.frame.size.height);
It looks like you want that value to be 480, the full height of the screen.
Your origin might be too high. Similarly check your y offset:
NSLog(#"y origin is %f", self.view.frame.origin.y);
It looks like you want that value to be 0.
The origin of your superview might be too high. Assuming you this view only has one superview, this code will help check the absolute coordinates of your view's origin:
CGPoint absoluteOrigin = [self.view convertPoint:self.view.frame.origin
toView:self.view.superview];
NSLog(#"y origin in superview is %f", absoluteOrigin.y);
You can just tag on a few extra .superview's to find out the coordinates in terms of the prior views.
By the way, instead of just using 480 as a magic number in your code, you might want to do something like this to get the full height:
CGRect appFrame = [[UIScreen mainScreen] applicationFrame];
CGFloat screenHeight = appFrame.size.height;
The applicationFrame property takes into account whether or not the status bar is displayed.
You should post your code, but in general, you can make a UIView fill the screen as follows:
UIView *foo = [[UIView alloc] initWithFrame:CGRectMake(0,0,320,480)];
And you can set the frame later:
foo.frame = CGRectMake(0,0,320,480);
To fill the screen, you should use a 320 x 480 Rectangle, at origin (0,0). That's what that CGRectMake function above creates.
I've had this problem before, I fixed it by just moving the view down 20 pixels...
When you use interface builder it has that nifty little "simulate status bar" or "simulate toolbar" feature but I think its kind of finnicky.
Both Andrew and Tylers answers should work though, no shame in doing things programmatically :). If you are creating the stuff in interface builder, just do Andrew's second line though, don't need to reinitialize.
Ok, well I figured it out and now I feel kind of stupid...I went back to IB and realized that the height of each view was set at 460 and not 480...you guys did give me a bunch of good information and I appreciate it all. I wish I could mark them all as answers.
Thank you again,
BWC