Line is not smooth on layer.border when its rotated - ios

I need to rotate an UIImageView:
UIImageView *img = [[UIImageView alloc] init];
img.layer.borderWidth = 3.0;
img.layer.borderColor = UIColorFromRGB(0xffffff).CGColor;
img.layer.shadowColor = UIColorFromRGB(0x000000).CGColor;
CATransform3D transform = CATransform3DMakeRotation ([Helper degreesToRadians:(5)], 1, 1, 1);
img.layer.shadowRadius = 2.0;
img.layer.shouldRasterize = YES;
The problem is that the border is not smooth at all:

Try adding a transparent border (1px may be) around your image. See this link.
Check UIImage+Alpha section in that page.

I see most of the answer for this problem is to add 1px transparent border but it only works if we are not using any border around UIImageView, if UIImageView itself has the border and we rotate it then the border is not smooth. I am stil trying to figure out on how to fix that issue.
HERE'S WHAT I DID TO FIX THIS ISSUE
UIImage *thumbnail = [photo thumbnailImage:thumbnailSize.width
transparentBorder:2
cornerRadius:0
interpolationQuality:kCGInterpolationHigh];
CGPoint randomPoint = [self getRandomOriginPoint];
UIImageView *thumbnailView = [[UIImageView alloc] initWithFrame:CGRectMake(10.0, 10.0, thumbnail.size.width, thumbnail.size.height)];
[thumbnailView.layer setBorderWidth:2];
[self setContentMode:UIViewContentModeCenter];
[thumbnailView.layer setBorderColor:[UIColor whiteColor].CGColor];
[thumbnailView.layer setShadowOffset:CGSizeMake(-3.0, 3.0)];
[thumbnailView.layer setShadowRadius:3.0];
[thumbnailView.layer setShadowOpacity:1.0];
thumbnailView.layer.shouldRasterize = YES;
I used UIImage+Resize.h from here to get the thumbnail image in my code, so with this even after you rotate image with any angle the border will look perfectly fine!

Without having to use categories, solution that made the fewest presumptions
#import <QuartzCore/QuartzCore.h>
We will use the following variable
UIView *myView;
This has been confirmed to work on UIImageViews / UIViews of all types
myView.layer.shouldRasterize = YES; //the single line that solved this problem from the above
You should not need to add a border of any type for this to work.
CATransform3D rotate = CATransform3DIdentity;
myView.layer.shouldRasterize = YES;
rotate = CATransform3DRotate(rotate,M_PI_4/8,0.0,0.0,1);
myView.layer.transform = rotate;
Note: This answer is provided to consolidate the information into a single answer that removes some of the other requirements. In this example I use M_PI_4/8 as it's a nice viewing angle for if you have a Stickie Note sort of view that you are implementing.

Related

IOS: UIImageView set border white radius and shadow

Here is my code for set the border, shadow and corner
// set border
[self.avatarImageView.layer setBorderColor: [[UIColor whiteColor] CGColor]];
[self.avatarImageView.layer setBorderWidth: 2.0];
// set shadow
[self.avatarImageView.layer setShadowOffset:CGSizeZero];
[self.avatarImageView.layer setShadowOpacity:1.0];
self.avatarImageView.clipsToBounds = NO;
// set corner
self.avatarImageView.layer.cornerRadius = 10.0;
self.avatarImageView.layer.masksToBounds = YES;
If I only use the code for set border and set corner, it work well like this
But if I add the code set corner, I will got the result like this (the border and corner radius work, but the shadow is gone)
However the code for set corner work perfect if it stay alone.
Please guide me what to do. Any help would be appreciate
Update
Follow #ozgur answer. Add 2 lines to my code, it will give a very beautiful view but the shadow is a little bit smaller
self.avatarImageView.layer.shouldRasterize = YES;
self.avatarImageView.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:self.avatarImageView.bounds cornerRadius:10].CGPath;
Rounded corners requires masksToBounds to be set to YES. Because of that nothing past the boundaries (like a shadow) can be shown, because it will be masked/clipped off. If you disable masksToBounds so that it will show then the rounded corners wont work because they are unable to mask/clip your image into the rounded shape because you disabled masksToBounds.
Therefor, you can't do both at the same time on one view—so you need TWO views.
You need to make a UIView the same dimensions as your UIImageView and make the UIImageView a subview of your UIView.
Then on your UIImageView set masksToBounds to YES, and on its' superview (the UIView with the same dimensions) set masksToBounds to NO and add the properties accordingly.
Change your code to this: (typed it all w/o using xCode so it's possible I have typos)
UIView *avatarImageViewHolder = [[UIView alloc] initWithFrame:self.avatarImageView.frame];
avatarImageViewHolder.backgroundColor = [UIColor clearColor];
[avatarImageView.superview addSubview:avatarImageViewHolder];
avatarImageViewHolder.center = avatarImageView.center;
[avatarImageViewHolder addSubview:avatarImageView];
avatarImageView.center = CGPointMake(avatarImageViewHolder.frame.size.width/2.0f, avatarImageViewHolder.frame.size.height/2.0f);
self.avatarImageView.layer.masksToBounds = YES;
avatarImageViewHolder.layer.masksToBounds = NO;
// set avatar image corner
self.avatarImageView.layer.cornerRadius = 10.0;
// set avatar image border
[self.avatarImageView.layer setBorderColor: [[UIColor whiteColor] CGColor]];
[self.avatarImageView.layer setBorderWidth: 2.0];
// set holder shadow
[avatarImageViewHolder.layer setShadowOffset:CGSizeZero];
[avatarImageViewHolder.layer setShadowOpacity:1.0];
avatarImageViewHolder.clipsToBounds = NO;
You need to add a container view and move your imageview inside that container view, Use this code after doing so:
CALayer *imageViewLayer= self.imageView.layer;
imageViewLayer.cornerRadius= 20.0f;
imageViewLayer.masksToBounds= YES;
CALayer *containerLayer = self.containerView.layer;
containerLayer.borderColor= [UIColor whiteColor].CGColor;
containerLayer.borderWidth= 3.0f;
containerLayer.cornerRadius= 20.0f;
containerLayer.shadowOffset = CGSizeMake(0, 0);
containerLayer.shadowColor = [UIColor blackColor].CGColor;
containerLayer.shadowRadius = 10.0f;
containerLayer.shadowOpacity = 0.80f;
containerLayer.masksToBounds= NO;
containerLayer.shadowPath = [[UIBezierPath bezierPathWithRect:containerLayer.bounds] CGPath];
Feel free to tweak the settings as per your need. Enjoy!
You should update layer.shadowPath if you don't want the shadow to get clipped:
self.avatarImageView.layer.shouldRasterize = YES;
self.avatarImageView.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:self.avatarImageView.bounds cornerRadius:10].CGPath;
For this trick to work, clipsToBounds and his brother masksToBounds should be set to NO.
Thus, if you have an image whose size is greater than the avatarView's own bounds, you should define avatarImageView as plain UIView, create another imageView to display the image set its masksToBounds to YES and add it as a subview to avatarImageView so that you'll have a good cornered and shadowed view capable of displaying the clipped image.
You should set self.avatarImageView.clipsToBounds = YES; in set corner

Creating image which sweeps across the screen

I'm trying to create a banner that sweeps across the current scene. I want to create a banner which sweeps down the screen to show the current round. My attempt at it is creating a UIImageView and add it to the current view. However, i assume its calling the didMoveToView Function and resetting everything in that scene, which is something i dont want it to do. Here is my attempt:
-(void)createBanner{
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Banner"]];
[imageView setFrame:CGRectMake(0,0, imageView.frame.size.width, imageView.frame.size.height)];
[imageView setClipsToBounds:YES];
[self.view addSubview:imageView];
CABasicAnimation *sweep = [CABasicAnimation animationWithKeyPath:#"position"];
sweep.fromValue = [NSValue valueWithCGPoint:CGPointZero];
sweep.toValue = [NSValue valueWithCGPoint:CGPointMake(0.0, self.frame.size.height)];
sweep.duration = 10;
sweep.additive = YES;
[imageView.layer addAnimation:sweep forKey:#"sweep"];
}
EDIT: i am using sprite kit in order to create the game.
As hamobi said, it's best to use an 'SKSpriteNode' in Sprite Kit and not UIKit. Assuming your'e adding to an 'SKScene', your code above translated to Sprite Kit is:
-(void)createBanner{
SKSpriteNode* spriteNode = [SKSpriteNode spriteNodeWithImageNamed:#"Banner"]
//It's good practice not to resize the sprite in code as it should already be the right size but...
spriteNode.size = CGSizeMake(self.size.width, self.size.height)
//Set its center off to the left of the screen for horizontal sweep, or you can do vertical and set it off the top of the screen...
spriteNode.postion = CGPointMake(-spriteNode.size.width/2, self.size.height/2)
self.addChild(spriteNode)
//Then to sweep from left to right...
SKAction* sweep = [SKAction moveTo:CGPointMake(spriteNode.size.width/2, self.size.height/2) duration:10]
spriteNode.runAction(sweep)
}
I think that covers most of it.

UI help for Horizontal Color Picker

I am trying to make color picker by reading pixel of imageview. Right now I have one imageview in the background and retrieving pixel color from it.
But now i am looking to shift my UI like this, so need help in how should I make this UI
Here's what I thought this will loo like
UIView->n no of. Imageview and arrow will be on top of it.
But then there are few things that I am not sure how will I do it
I have to keep some color unlock as level is crossed colors will get unlock
knob should not go outside the bound
I have to retrieve the color from the edge of the arrow so that I get the proper pixel value
Here is my suggestion to how to deal with this,
Create a list of images and the color values for each of those in a
NSDictionary like,
NSDictionary *colors = #{[UIColor redColor]: [UIImage imageNamed:#"red"], [UIColor greenColor]: [UIImage imageNamed:#"green"], [UIColor yellowColor: [UIImage imageNamed:#"green"]]}
Then create the imageview and add the images to the view and add tap gesture recognizer to each of the imageViews;
UIView *parentView = self.parentView;
CGFloat knobHeight = 20;
CGFloat colorViewHeight = 20;
CGFloat colorViewWidht = 20;
int index = 0;
for(UIColor *color in colors){
CGRect frame = CGRectMake(index * colorViewWidth, knobHeight, colorViewWidth, colorViewHeight);
UIImageView *imageView = [[UIImageView alloc] initWithFrame: frame];
imageView.image = colors[color];
[parentView addSubView:imageView];
UITapGestureRecognizer *tapGestureRecognizer = [UITapGestureRecognizer alloc] initWithTarget: self selector:#selector(tappedImage:)];
imageView.userInteractionEnabled = YES;
[imageView addGestureRecognizer: tapGestureRecognizer];
}
Initially add knob to the first imageView;
UIImageView *firstImageView = [[parentView subView] firstObject];
CGRect knobFrame = CGRectMake (CGRectGetMidX(firstImageView.frame) - knobWidth / 2, 0, knobWidth, knobHeight);
UIImageView *knobImageView = [[UIImageView alloc] initWithFrame:knobFrame];
[parentView addSubview: knobImageView];
Then, in the tap selector you could animate the knob change as;
- (void)tappedImage:(UITapGestureRecognizer*)tap{
UIImageView *tappedImageView = tap.view;
CGRect newKnobFrame = CGRectMake((CGRectGetMidX(tappedImageView.frame) - knobWidth / 2, 0 , knobWidth, knobHeight);
[UIView animateWithDuration:2.0 animations:^{
knobImageView.frame = newKnobFrame;
}];
}
You could also keep track of the color for which you want to lock selection. Then, do not enable the userInteraction for those images and not add the gesture recognizer. You could set alpha value to show that it is not possible to select it.
This code is directly typed, but it should give you the rough idea of how it could work.

iOS slide up an image over an image, revealing the underneath one. (A-la jQuery.slide())

I'm trying to do the following thing -
I have two version of the same image, one colored and one black and white. I want the B&W to "Slide up" revealing the one under it.
I tried setting the height and the frame, but couldn't get it to work like I want it to.
For example , this would be a combination of both UIImages where one is revealing the other, while not moving:
Any ideas? :)
OK, here is a different method using a mask on the grey view. I actually tested this with a blueView and a greyView, where the grey covers up the blue. Put a mask layer on the grey layer so that the grey layer is visible. Now animate the mask away from the grey layer, making the grey layer disappear without moving it, the blue shows thru where the grey disappears.
If you want to experiment with this, create an empty project in XCode with a single view, and put this code inside viewDidLoad. That's how I tested this.
self.view.backgroundColor = [UIColor whiteColor];
UIView* blueView = [[[UIView alloc] init] autorelease];
blueView.backgroundColor = [UIColor blueColor];
blueView.frame = CGRectMake(50,50,100,100);
UIView* grayView = [[[UIView alloc] init] autorelease];
grayView.backgroundColor = [UIColor grayColor];
grayView.frame = CGRectMake(50,50,100,100);
[self.view addSubview:blueView];
[self.view addSubview:grayView]; // gray covers the blue
// Create a mask to allow the grey view to be visible and cover up the blue view
CALayer* mask = [CALayer layer];
mask.contentsScale = grayView.layer.contentsScale; // handle retina scaling
mask.frame = grayView.layer.bounds;
mask.backgroundColor = [UIColor blackColor].CGColor;
grayView.layer.mask = mask;
// Animate the position of the mask off the grey view, as the grey becomes unmasked,
// that part of the grey "dissappears" and is no longer covering the blue underneath
CABasicAnimation* a = [CABasicAnimation animationWithKeyPath:#"position"];
a.duration = 4;
a.fromValue = [NSValue valueWithCGPoint:mask.position];
CGPoint newPosition = mask.position;
newPosition.y += mask.bounds.size.height;
a.toValue = [NSValue valueWithCGPoint:newPosition];
a.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[mask addAnimation:a forKey:#"colorize"]; // animate presentation
mask.position = newPosition; // update actual position
Don't forget this line at the top with the #imports:
#import <QuartzCore/QuartzCore.h>
One way is to stack the UIView's so that the B&W image on top of the color image, where the color image is completely covered (hidden) by the B&W image.
[self.view insertSubview:bwImage aboveSubview:colorImage];
Only the B&W view is visible. Now set clipsToBounds on the B&W view:
bwImage.clipsToBounds = YES;
Finally animate the height in the bounds of the bwImage so that its height shrinks down to 0, clipping the B&W image and revealing the color image behind it. Something like this:
[UIView animateWithDuration:1.0 animations:^{
CGRect b = bwImage.bounds;
b.size.height = 0;
bwImage.bounds = b;
}];
I haven't tried this but hopefully you get the idea.

Creating a custom progress bar with images

Pretty much what I am trying to do, is create the following image:
So this is a progress bar, which will show how many votes the client get in that option. How can I do that? I was wondering if I can use a mask (as I would in flex), but all the masking implementations that I found on the web are doing masks for UIImages, and not for UIImageView. I cannot do the mask in the UIImage, because the two images have the same dimensions. I have to use the X and Y of the images to crop them?! So, any suggestions?
Here are the two images that I have to create that progress bar:
Cheers.
you want to get rid of all of the same bit in the middle of each image.
then do something like:
- (void)viewDidLoad
{
[super viewDidLoad];
[self addProgressBarInFrame:CGRectMake(20.f, 20.f, 280.f, 50.f) withProgress:.9f];
[self addProgressBarInFrame:CGRectMake(20.f, 100.f, 200.f, 25.f) withProgress:.1f];
}
-(void)addProgressBarInFrame:(CGRect)frame withProgress:(CGFloat)progress
{
float widthOfJaggedBit = 4.0f;
UIImage * imageA= [[UIImage imageNamed:#"imageA"] stretchableImageWithLeftCapWidth:widthOfJaggedBit topCapHeight:0.0f];
UIImage * imageB= [[UIImage imageNamed:#"imageB"] stretchableImageWithLeftCapWidth:widthOfJaggedBit topCapHeight:0.0f];
UIView * progressBar = [[UIView alloc] initWithFrame:frame];
progressBar.backgroundColor = [UIColor whiteColor];
UIImageView * imageViewA = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, frame.size.width*progress, frame.size.height)];
UIImageView * imageViewB = [[UIImageView alloc] initWithFrame:CGRectMake(frame.size.width*progress, 0.f, frame.size.width - (frame.size.width*progress), frame.size.height)];
imageViewA.image = imageA;
imageViewB.image = imageB;
// imageViewA.contentStretch = CGRectMake(widthOfJaggedBit, 0, imageA.size.width - 2*widthOfJaggedBit, imageA.size.height) ;
// imageViewB.contentStretch = CGRectMake(widthOfJaggedBit, 0, imageB.size.width - 2*widthOfJaggedBit, imageB.size.height) ;
[self.view addSubview:progressBar];
[progressBar addSubview:imageViewA];
[progressBar addSubview:imageViewB];
}
Grady Player's answer is great. Just a small note, for anyone that uses this. I tried to update the frames of the two UIImageView's to update the "progress". I tried to force an update with "setNeedsDisplay", but to no avail. (sizeToFit caused problems too).
Anyway, the only way I figured out how to update the progress of an existing progress bar was to call "setFrame:CGRectMake" with my new dimensions.

Resources