when entering the editing mode in a UITableViewCell through setEditing:animated:, there is an (implicit) animation where the cells move right and the delete "buttons" appear at the left.
How do I manage, probably with Core Animation features, to run a second animation simultaneously to the first animation when entering editing mode, i.e. with the the same starting time, duration, timing curve, and so on? (I'm actually trying to change the width of a cell's sublayer synchronously.)
I studied the Core Animation Programming Guide and tried some of the techniques in a custom UITableViewCell class, for example by overriding willTransitionToState:. In principle, everything works well, however, I can only manage to have the animations one after the other, but not simultaneously.
Any advice is greatly appreciated.
Alright, it's much simpler than I initially thought. The behavior I sought can rather easily be achieved by correctly initializing the layers (or views) in the custom UITableViewCell. However, I realized that the order and, e.g., whether you manipulate bounds or frame properties is quite important.
The following code snippet does the job for a cell in a grouped UITableView. In this example, a CAGradientLayer (as an example for a CALayer) is added as a sublayer to a UIView, which itself is added as a subview to self.contentView of the cell. The subview's frame is tweaked to fit into the contentView. The code goes, e.g., into initWithStyle:reuseIdentifier: of a custom UITableViewCell:
CGRect realFrame = self.contentView.frame;
realFrame.size.width += 20;
UIView *gradientView = [[UIView alloc] initWithFrame:realFrame];
CAGradientLayer *gradientLayer = [[CAGradientLayer alloc] init];
gradientLayer.anchorPoint = CGPointMake(0.0, 0.0);
gradientLayer.frame = gradientView.frame;
gradientLayer.position = CGPointMake(0.0, 0.0);
[gradientLayer setColors:[NSArray arrayWithObjects:(id)[[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:1.0] CGColor], (id)[[UIColor clearColor] CGColor], nil]];
gradientLayer.startPoint = CGPointMake(0.5, 0.0);
gradientLayer.endPoint = CGPointMake(0.5, 1.0);
gradientView.layer.masksToBounds = YES;
gradientView.layer.cornerRadius = 8.0;
[gradientView.layer insertSublayer:gradientLayer atIndex:0];
[self.contentView addSubview:gradientView];
gradientView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[gradientView release];
[gradientLayer release];
Related
I've created a subview of UIView to make some drawings - the drawings work fine in the drawRect method of my subclass, however, I cannot change the background color of the view. A little googling tells me I haven't set the frame for the view, but I'm not entirely sure how to do this. I tried two things:
I create the view in Storyboard and add it to my view controller, then declare it as a property in the header file and link them up. I synthesize the property at the top of the implementation file and in the viewDidLoad method, I add:
[myView setBackgroundColor: [UIColor whiteColor]];
The view's background is still black.
I also tried:
ViewSubclass *v = [[ViewSubclass alloc] initWithFrame: self.view.frame];
v.backgroundColor = [UIColor whiteColor];
myView = v;
To no avail.
What am I doing wrong?
UPDATE: This is the code I use to draw in the view, in case there's something going on there!
- (void)drawRect:(CGRect)rect {
CGFloat height = self.bounds.size.height;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
CGContextSetFillColorWithColor(context, [UIColor grayColor].CGColor);
CGFloat barWidth = 30;
int count = 0;
for (NSNumber *num in samples) {
CGFloat x = count * (barWidth + 10);
CGRect barRect = CGRectMake(x, height - ([num floatValue] * height), barWidth, [num floatValue] * height);
CGContextAddRect(context, barRect);
count++;
}
CGContextFillPath(context);
}
It just creates a set of bars in the screen, of different heights.
CGContextClearRect From the docs:
If the provided context is a window or bitmap context, Quartz
effectively clears the rectangle. For other context types, Quartz
fills the rectangle in a device-dependent manner. However, you should
not use this function in contexts other than window or bitmap
contexts.
It might be that this clearing clears your entire view with no regard to the background color you set
I can t add comments due my low lvl, but the question springs to mind, have you added to your UIView to viewcontroller?
like
[self.view addSubview:v];
EDIT:
I'm sorry I had understood that the second option was added to the view that pragmatically.
Are you adding the view in the header file? If so you might need to be sure the frame's coordinates are visible and that the view is on top, also make the new view slightly smaller to see if it is actually being created:
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0,0,100,100)];
[self.view addSubview: myView];
[self.view bringSubviewToFront:myView];
You can also change the main view background color to see if the new view is actually being created. For example:
[self.view setBackgroundColor:[UIColor grayColor]];
If the color is changing for your main view then the problem is that your new view is not being or brought to the front. Let us know how it goes!
At the point where do
[myView setBackgroundColor: [UIColor whiteColor]];
check to see if myView is not nil in the debugger
I'm trying to slide down an image in an UIImageView, but I don't know which combination of UIContentMode and animation property is the right one to make that happen.
The image should always have the same size and should not be streched... all I want is, that nothing is visible first and then the frame extends and reveals the image.
Maybe it's easier if you see what I mean:
So, it sounds rather easy, but what UIContentMode should I use for the UIImageView and what property should I animate? Thank you!
I took your lead and made a screencast as well. Was this what you had in mind?
I put the animation repeating indefinitely so it would be easier to capture with a video, but it can be started at the press of a button, as well as frozen in place, showing the popover and its contents, until reversed to be hidden again.
I used Core Animation for that, instead of animating a UIView, since I wanted to use the mask property of CALayer to hide the popover and reveal it with a sliding animation.
Here is the code I used (same as in the video):
- (void)viewDidLoad
{
// Declaring the popover layer
CALayer *popover = [CALayer layer];
CGFloat popoverHeight = 64.0f;
CGFloat popoverWidth = 200.0f;
popover.frame = CGRectMake(50.0f, 100.0f, popoverWidth, popoverHeight);
popover.contents = (id) [UIImage imageNamed:#"popover.png"].CGImage;
// Declaring the mask layer
CALayer *maskLayer = [CALayer layer];
maskLayer.frame = CGRectMake(0, - popoverHeight, popoverWidth, popoverHeight);
maskLayer.backgroundColor = [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:1.0f].CGColor;
// Setting the animation (animates the mask layer)
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"position.y"];
animation.byValue = [NSNumber numberWithFloat:popoverHeight];
animation.repeatCount = HUGE_VALF;
animation.duration = 2.0f;
[maskLayer addAnimation:animation forKey:#"position.y"];
//Assigning the animated maskLayer to the mask property of your popover
popover.mask = maskLayer;
[self.view.layer addSublayer:popover];
[super viewDidLoad];
}
NOTE: You have to import the QuartzCore framework into your project and write this line in your header file: #import <QuartzCore/QuartzCore.h>.
Tells if this works for you or if you need any more help setting this up.
Try this code.
Consider the UIImageView as imageView.
imageView.contentMode = UIViewContentModeScaleAspectFill;
CGRect imageRect = imageView.frame;
CGRect origImgRect = imageRect;
imageRect.size.height = 5;
imageView.frame = imageRect;
[UIView animateWithDuration:2.0
animations:^{imageView.rect = origImgRect;}
completion:^(BOOL finished){ }];
The Instruments tool reports memory leak for CALayer. I have a custom class, a subclass of UIViewController; in the viewDidLoad() I instantiate a view with CAGradientLayer and insert this view as a subview of current view, e.g.:
UIControl *view = [[[UIControl alloc] initWithFrame:CGRectMake(10, 10, 460, 220)] autorelease];
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = view.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor whiteColor] CGColor], (id)[[UIColor colorWithRed:0.85 green:0.85 blue:0.85 alpha:1.0] CGColor],nil];
gradient.startPoint = CGPointMake(0, 0);
gradient.endPoint = CGPointMake(1, 1);
[view.layer insertSublayer:gradient atIndex:0];
view.layer.masksToBounds = YES;
[view.layer setCornerRadius:5];
[self.view insertSubview:view atIndex:1];
When I run the code in simulator, all is fine. However when I run it on device (iOS 4.3.3), the Instruments tool reports leaks for CALayer. When I coment out this code, there are no leaks.
What is the issue here?
Edit: I found out it only leaks if I insert the subview into a view which has a scrollview (so my subview with the gradient calayer is inserted between the view and scrollview).
release the
uiview and
layer
because simulator runs in system configuration so no problem will occur
but the device has less memory compared to system
how would you go about overlaying a mask image of sorts on top of a UIScrollView?
For example, I have an image with black on the left and right fading to white in the center. I'd like to use this so that the items in my scroll view gradually fade out to the sides and the center item is completely opaque.
Also, the background of the view the scrollview is placed on is an image (not a solid color) which is dynamic and can change.
Any ideas?
Your question wound up being the best source I could find on the basic problem, so I decided to post my solution here.
If you add the mask directly to the scroll view, you wind up applying the mask to the content itself for some reason -- i. e. the transparency doesn't shift as you scroll. As per the suggestions of other posts, I put the gradient in a view over (or rather, containing) the scroll view. As a result, scrollContainer is a UIViewObject whose sole subview is the scroll view in question.
This mask is configured for left-to-right scrolling. You could change it to top to bottom by manipulating the start and end point properties of the gradient.
The one down side is that this also clips the scroll position bar indicator of the scroll view. Since my scenario doesn't need one, that's acceptable, but your mileage may vary.
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = scrollView.bounds;
gradient.colors = [NSArray arrayWithObjects:
(id)[[UIColor colorWithWhite:0 alpha:0] CGColor],
(id)[[UIColor colorWithWhite:0 alpha:1] CGColor],
(id)[[UIColor colorWithWhite:0 alpha:1] CGColor],
(id)[[UIColor colorWithWhite:0 alpha:0] CGColor], nil];
gradient.locations=[NSArray arrayWithObjects:
[NSNumber numberWithFloat:0],
[NSNumber numberWithFloat:.1],
[NSNumber numberWithFloat:.9],
[NSNumber numberWithFloat:1], nil];
gradient.startPoint=CGPointMake(0, .5);
gradient.endPoint=CGPointMake(1, .5);
[scrollContainer.layer setMask:gradient];
If you have a solid color background behind the scrollview, you'll get better performance by just overlaying an image on top of the scroll view rather than trying to perform a mask within it.
Try something along these lines:
// I assume you are in a UIViewController and self.view is set to some kind of view
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 420)];
scrollView.backgroundColor = [UIColor redColor];
[self.view addSubview: scrollView];
[scrollView release];
// add img with gradient
UIImageView *imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"my-gradient.png"];
imgView.frame = CGRectMake(0, 0, 160, 420);
[self.view addSubview: imgView];
[imgView release];
This would give you a gradient that begins on the left and goes all the way to the center for the entire height, assuming that "my-gradient.png" is an image that actually contains a gradient.
These kind of manipulations are big ressources consumers. The best approach would be (if your case allows it) to put a mask image OVER your UIScrollView. This image's background shoud be transparent (ex. png32) and the image should be an alpha-gradient.
Use insertView:yourView atIndex:0
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Manually drawing a gradient in iPhone apps?
My application needs to display text in either a UIView or UILabel but the back ground must be a gradient as opposed to a true UIColor. Using a graphics program to create desired look is no good as the text may vary depending on data returned from a server.
Does anyone know the quickest way to tackle this?
Your thoughts are greatly appreciated.
I realize this is an older thread, but for future reference:
As of iPhone SDK 3.0, custom gradients can be implemented very easily, without subclassing or images, by using the new CAGradientLayer:
UIView *view = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 100)] autorelease];
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = view.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor blackColor] CGColor], (id)[[UIColor whiteColor] CGColor], nil];
[view.layer insertSublayer:gradient atIndex:0];
Take a look at the CAGradientLayer docs. You can optionally specify start and end points (in case you don't want a linear gradient that goes straight from the top to the bottom), or even specific locations that map to each of the colors.
You can use Core Graphics to draw the gradient, as pointed to in Mike's response. As a more detailed example, you could create a UIView subclass to use as a background for your UILabel. In that UIView subclass, override the drawRect: method and insert code similar to the following:
- (void)drawRect:(CGRect)rect
{
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGGradientRef glossGradient;
CGColorSpaceRef rgbColorspace;
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
CGFloat components[8] = { 1.0, 1.0, 1.0, 0.35, // Start color
1.0, 1.0, 1.0, 0.06 }; // End color
rgbColorspace = CGColorSpaceCreateDeviceRGB();
glossGradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);
CGRect currentBounds = self.bounds;
CGPoint topCenter = CGPointMake(CGRectGetMidX(currentBounds), 0.0f);
CGPoint midCenter = CGPointMake(CGRectGetMidX(currentBounds), CGRectGetMidY(currentBounds));
CGContextDrawLinearGradient(currentContext, glossGradient, topCenter, midCenter, 0);
CGGradientRelease(glossGradient);
CGColorSpaceRelease(rgbColorspace);
}
This particular example creates a white, glossy-style gradient that is drawn from the top of the UIView to its vertical center. You can set the UIView's backgroundColor to whatever you like and this gloss will be drawn on top of that color. You can also draw a radial gradient using the CGContextDrawRadialGradient function.
You just need to size this UIView appropriately and add your UILabel as a subview of it to get the effect you desire.
EDIT (4/23/2009): Per St3fan's suggestion, I have replaced the view's frame with its bounds in the code. This corrects for the case when the view's origin is not (0,0).
Note: The results below apply to older versions of iOS, but when testing on iOS 13 the stepping doesn't occur. I don't know for which version of iOS the stepping was removed.
When using CAGradientLayer, as opposed to CGGradient, the gradient is not smooth, but has noticeable stepping to it. See :
To get more attractive results it is better to use CGGradient.
You could also use a graphic image one pixel wide as the gradient, and set the view property to expand the graphic to fill the view (assuming you are thinking of a simple linear gradient and not some kind of radial graphic).
Mirko Froehlich's answer worked for me, except when i wanted to use custom colors. The trick is to specify UI color with Hue, saturation and brightness instead of RGB.
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = myView.bounds;
UIColor *startColour = [UIColor colorWithHue:.580555 saturation:0.31 brightness:0.90 alpha:1.0];
UIColor *endColour = [UIColor colorWithHue:.58333 saturation:0.50 brightness:0.62 alpha:1.0];
gradient.colors = [NSArray arrayWithObjects:(id)[startColour CGColor], (id)[endColour CGColor], nil];
[myView.layer insertSublayer:gradient atIndex:0];
To get the Hue, Saturation and Brightness of a color, use the in built xcode color picker and go to the HSB tab. Hue is measured in degrees in this view, so divide the value by 360 to get the value you will want to enter in code.
This is what I got working- set UIButton in xCode's IB to transparent/clear, and no bg image.
UIColor *pinkDarkOp = [UIColor colorWithRed:0.9f green:0.53f blue:0.69f alpha:1.0];
UIColor *pinkLightOp = [UIColor colorWithRed:0.79f green:0.45f blue:0.57f alpha:1.0];
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = [[shareWordButton layer] bounds];
gradient.cornerRadius = 7;
gradient.colors = [NSArray arrayWithObjects:
(id)pinkDarkOp.CGColor,
(id)pinkLightOp.CGColor,
nil];
gradient.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0f],
[NSNumber numberWithFloat:0.7],
nil];
[[recordButton layer] insertSublayer:gradient atIndex:0];
I achieve this in a view with a subview that is an UIImageView. The image the ImageView is pointing to is a gradient. Then I set a background color in the UIView, and I have a colored gradient view. Next I use the view as I need to and everything I draw will be under this gradient view. By adding a second view on top of the ImageView, you can have some options whether your drawing will be below or above the gradient...