I have an UILabel subclass that draws some text in drawinRect method using a pattern image. This pattern image is a gradient image I am creating.
-(void)drawRect:(CGRect)rect
{
UIColor *color = [UIColor colorWithPatternImage:[self gradientImage]];
[color set];
[someText drawInRect:rec withFont:font lineBreakMode:UILineBreakModeWordWrap alignment:someAlignment];
}
The gradient image method is
-(UIImage *)gradientImage
{
NSData *colorData = [[NSUserDefaults standardUserDefaults] objectForKey:#"myColor"];
UIColor *co = [NSKeyedUnarchiver unarchiveObjectWithData:colorData];
colors = [NSArray arrayWithObjects:co,co,[UIColor whiteColor],co,co,co,co,nil];
NSArray *gradientColors=colors;
CGFloat width;
CGFloat height;
CGSize textSize = [someText sizeWithFont:font];
width=textSize.width; height=textSize.height;
UIGraphicsBeginImageContext(CGSizeMake(width, height));
// get context
CGContextRef context = UIGraphicsGetCurrentContext();
// push context to make it current (need to do this manually because we are not drawing in a UIView)
UIGraphicsPushContext(context);
//draw gradient
CGGradientRef gradient;
CGColorSpaceRef rgbColorspace;
//set uniform distribution of color locations
size_t num_locations = [gradientColors count];
CGFloat locations[num_locations];
for (int k=0; k<num_locations; k++)
{
locations[k] = k / (CGFloat)(num_locations - 1); //we need the locations to start at 0.0 and end at 1.0, equaly filling the domain
}
//create c array from color array
CGFloat components[num_locations * 4];
for (int i=0; i<num_locations; i++)
{
UIColor *color = [gradientColors objectAtIndex:i];
NSAssert(color.canProvideRGBComponents, #"Color components could not be extracted from StyleLabel gradient colors.");
components[4*i+0] = color.red;
components[4*i+1] = color.green;
components[4*i+2] = color.blue;
components[4*i+3] = color.alpha;
}
rgbColorspace = CGColorSpaceCreateDeviceRGB();
gradient = 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(context, gradient, topCenter, midCenter, 0);
CGGradientRelease(gradient);
CGColorSpaceRelease(rgbColorspace);
// pop context
UIGraphicsPopContext();
// get a UIImage from the image context
UIImage *gradientImage = UIGraphicsGetImageFromCurrentImageContext();
// clean up drawing environment
UIGraphicsEndImageContext();
return gradientImage;
}
Everything is working fine. The text is drawn with nice glossy effect. Now my problem is if I change the x or y position of the text inside the UILabel and then call the
[someText drawinRect:rec]... to redraw, the gloss effect is generated differently. It seems that the pattern image is changing with change of text position..
The following is the effect for the frame rec = CGRectMake(0, 10, self.frame.size.width, self.frame.size.height);
The following is the effect for the frame rec = CGRectMake(0, 40, self.frame.size.width, self.frame.size.height);
I hope I have explained my question well. Could not find any exact answer elsewhere. Thanks in advance.
You might want to look at using an existing 3rd party solution like FXLabel.
Related
I am trying to place UIColor on particular (X,Y) position of UIImage,
But not able to get it,
Here, My code looks like below,
Below method return me a UIColor from particular (X,Y) position of UIImage
- (UIColor *)isWallPixel:(UIImage *)image xCoordinate:(int)x yCoordinate:(int)y {
CFDataRef pixelData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));
const UInt8* data = CFDataGetBytePtr(pixelData);
int pixelInfo = ((image.size.width * y) + x ) * 4; // The image is png
UInt8 red = data[pixelInfo]; // If you need this info, enable it
UInt8 green = data[(pixelInfo + 1)]; // If you need this info, enable it
UInt8 blue = data[pixelInfo + 2]; // If you need this info, enable it
UInt8 alpha = data[pixelInfo + 3]; // I need only this info for my maze game
CFRelease(pixelData);
UIColor* color = [UIColor colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha/255.0f]; // The
return color;
}
//Original Image, I get image pixels from this image.
UIImage* image = [UIImage imageNamed:#"plus.png"];
//Need convert Image
UIImage *imagedata = [[UIImage alloc]init];
//size of the original image
int Width = image.size.width;
int height = image.size.height;
//need to get this size of another blank image
CGRect rect = CGRectMake(0.0f, 0.0f, Width, height);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
for (int i = 0; i <Width; i++) {
for (int j = 0; j < height; j++) {
//Here I got the Image pixel color from below highlighted method
UIColor *colordatra = [self isWallPixel:image xCoordinate:i yCoordinate:j];
CGContextSetFillColorWithColor(context, [colordatra CGColor]);
rect.origin.x = i;
rect.origin.y = j;
CGContextDrawImage(context, rect, imagedata.CGImage);
imagedata = UIGraphicsGetImageFromCurrentImageContext();
}
}
UIGraphicsEndImageContext();
Please note that I want to functionality like get the UIColor from particular position and placed that UIColor on another blank image at same position.
With above code I am not able to get this, Please let me know where I have done the mistake.
Hope, for favourable reply,
Thanks in Advance.
By using uiView you can do this one like that.Actually you are getting x,y,width and height
uiview *view = [uiview alloc]initwithframe:CGRectMake(x,y,width,height);
view.backgroundcolor =[UIColor colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha/255.0f];
[self.YourImageView addsubview:view];
The task is to create an image that consists of gradient of a lot of points that represent different colors. For example it is not a problem to draw a gradient between to points, but near the line of these two points can be another point that have an effect changing gradient between these two points. Appreciate any help.
You'll probably want to look into Core Graphics... I have posted two examples below of Core Graphics gradient rendering.
1D Linear gradient with multiple colors
This method will create an image with a simple 1D linear gradient (from top to bottom) with a given array of colors, all evenly spaced. You can customise your own spacings by setting your own values in gradLocs[].
-(UIImage*) gradientImageWithSize:(CGSize)size withColors:(NSArray*)colors {
// Start context
UIGraphicsBeginImageContext(size);
CGContextRef c = UIGraphicsGetCurrentContext();
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
NSUInteger colorCount = [colors count];
CGFloat gradLocs[colorCount];
for (int i = 0; i < colorCount; i++) gradLocs[i] = i/colorCount; // Even spacing of colors.
// Create a simple linear gradient with the colors provided.
CGGradientRef grad = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, gradLocs);
CGColorSpaceRelease(colorSpace);
// Draw gradient with multiply blend mode over the source image
CGContextDrawLinearGradient(c, grad, CGPointZero, (CGPoint){0, size.height}, 0);
CGGradientRelease(grad);
// Grab resulting image from context
UIImage* resultImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultImg;
}
Usage:
NSArray* colors = #[(id)[UIColor redColor].CGColor, (id)[UIColor greenColor].CGColor, (id)[UIColor purpleColor].CGColor, (id)[UIColor blueColor].CGColor];
[self gradientImageWithSize:(CGSize){500, 500} withColors:colors];
2D gradient with multiple colors
This method, while not a very accurate way of generating a 2D gradient, is definitely the easiest way through Core Graphics. The radius parameter defines how far away from the point that the color has influence. To do this I created a custom object to store the gradient information at a given point:
/// Defines a simple point for use in a gradient
#interface gradPoint : NSObject
/// The color at the given point
#property (nonatomic) UIColor* color;
/// The position of the point
#property (nonatomic) CGPoint point;
/// The radius of the point (how far the color will have influence)
#property (nonatomic) CGFloat radius;
#end
#implementation gradPoint
+(instancetype) pointWithColor:(UIColor*)color point:(CGPoint)point radius:(CGFloat)radius {
gradPoint* p = [[self alloc] init];
p.color = color;
p.point = point;
p.radius = radius;
return p;
}
#end
The gradient generation method then takes a size and an array of these gradPoint objects.
-(UIImage*) gradient2DImageWithSize:(CGSize)size gradPointArray:(NSArray*)gradPoints {
UIGraphicsBeginImageContextWithOptions(size, YES, 0);
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(c, [UIColor whiteColor].CGColor);
CGContextFillRect(c, (CGRect){CGPointZero, size});
CGContextSetBlendMode(c, kCGBlendModeMultiply);
CGFloat gradLocs[] = {0, 1};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
for (gradPoint* point in gradPoints) {
NSArray* colors = #[(id)point.color.CGColor, (id)[UIColor whiteColor].CGColor];
CGGradientRef grad = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, gradLocs);
CGContextDrawRadialGradient(c, grad, point.point, 0, point.point, point.radius, 0);
CGGradientRelease(grad);
}
CGColorSpaceRelease(colorSpace);
UIImage* i = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return i;
}
Usage:
CGFloat gradRadius = frame.size.height;
NSArray* gradPoints = #[
[gradPoint pointWithColor:[UIColor redColor] point:CGPointZero radius:gradRadius],
[gradPoint pointWithColor:[UIColor cyanColor] point:(CGPoint){0, frame.size.height} radius:gradRadius],
[gradPoint pointWithColor:[UIColor yellowColor] point:(CGPoint){frame.size.height, 0} radius:gradRadius],
[gradPoint pointWithColor:[UIColor greenColor] point:(CGPoint){frame.size.height, frame.size.height} radius:gradRadius]
];
UIImage* gradImage = [self gradient2DImageWithSize:(CGSize){gradRadius, gradRadius} gradPointArray:gradPoints];
This method works best with a square image, with the radius set to the height/width.
Example Output:
Our app contains several MKPolyline boundaries that all create a closed in polygon. These are primarily to display as an MKOverlay on a MKMapView but I'm looking for a solution to display these polygons as small thumbnails to be visible not on the MKMapView but instead as a standard UIImage or UIImageView.
Just to be clear, I'm wanting these small thumbnails would just be displayed as small shapes that have a stroke color and a fill color but without any map background.
Could anyone help me with this?
Here you go.
+ (UIImage *)imageNamed:(NSString *)name withColor:(UIColor *)color{
// load the image
UIImage *img = [UIImage imageNamed:name];
// begin a new image context, to draw our colored image onto
UIGraphicsBeginImageContext(img.size);
// get a reference to that context we created
CGContextRef context = UIGraphicsGetCurrentContext();
// set the fill color
[color setFill];
// translate/flip the graphics context (for transforming from CG* coords to UI* coords
CGContextTranslateCTM(context, 0, img.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// set the blend mode to color burn, and the original image
CGContextSetBlendMode(context, kCGBlendModeColorBurn);
CGRect rect = CGRectMake(0, 0, img.size.width, img.size.height);
CGContextDrawImage(context, rect, img.CGImage);
// set a mask that matches the shape of the image, then draw (color burn) a colored rectangle
CGContextClipToMask(context, rect, img.CGImage);
CGContextAddRect(context, rect);
CGContextDrawPath(context,kCGPathFill);
// generate a new UIImage from the graphics context we drew onto
UIImage *coloredImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//return the color-burned image
return coloredImg;
}
Please check this original post for detail description.
I had to do exactly the same in my own app. Here is my solution : I generate a UIView which represents path's shape. In your case the path is a MKPolyline.
Here is my code :
+ (UIView *)createShapeForGPX:(GPX *)gpx
withFrameSize:(CGSize)frameSize
lineColor:(UIColor *)lineColor {
// Array of coordinates (Adapt this code with your coordinates)
// Note : in my case I have a double loops because points are in paths
// and I can have many paths for one route. So I concact all points
// into one array to simplify the code for your case. If you also have
// many paths, you have to change a little bit next code.
NSMutableArray<NSValue *> *dataPoints = [NSMutableArray new];
for (NSArray *path in gpx.paths) {
for (NSDictionary *point in path) {
double latitude = [point[#"latitude"] doubleValue];
double longitude = [point[#"longitude"] doubleValue];
[dataPoints addObject:[NSValue valueWithCGPoint:CGPointMake(longitude, latitude)]];
}
}
// Graph bounds (You need to calculate topRightCoordinate and bottomleftCoordinate. You can do it in previous for loop)
double lngBorder = gpx.topRightCoordinate.longitude - gpx.bottomLeftCoordinate.longitude;
double latBorder = gpx.topRightCoordinate.latitude - gpx.bottomLeftCoordinate.latitude;
double middleLng = gpx.bottomLeftCoordinate.longitude + (lngBorder / 2.f);
double middleLat = gpx.bottomLeftCoordinate.latitude + (latBorder / 2.f);
double boundLength = MAX(lngBorder, latBorder);
// *** Drawing ***
CGFloat margin = 4.f;
UIView *graph = [UIView new];
graph.frame = CGRectMake(0, 0, frameSize.width - margin, frameSize.height - margin);
CAShapeLayer *line = [CAShapeLayer layer];
UIBezierPath *linePath = [UIBezierPath bezierPath];
float xAxisMin = middleLng - (boundLength / 2.f);
float xAxisMax = middleLng + (boundLength / 2.f);
float yAxisMin = middleLat - (boundLength / 2.f);
float yAxisMax = middleLat + (boundLength / 2.f);
int i = 0;
while (i < dataPoints.count) {
CGPoint point = [dataPoints[i] CGPointValue];
float xRatio = 1.0-((xAxisMax-point.x)/(xAxisMax-xAxisMin));
float yRatio = 1.0-((yAxisMax-point.y)/(yAxisMax-yAxisMin));
float x = xRatio*(frameSize.width - margin / 2);
float y = (1.0-yRatio)*(frameSize.height - margin);
if (i == 0) {
[linePath moveToPoint:CGPointMake(x, y)];
} else {
[linePath addLineToPoint:CGPointMake(x, y)];
}
i++;
}
// Line
line.lineWidth = 0.8;
line.path = linePath.CGPath;
line.fillColor = [[UIColor clearColor] CGColor];
line.strokeColor = [lineColor CGColor];
[graph.layer addSublayer:line];
graph.backgroundColor = [UIColor clearColor];
// Final view (add margins)
UIView *finalView = [UIView new];
finalView.backgroundColor = [UIColor clearColor];
finalView.frame = CGRectMake(0, 0, frameSize.width, frameSize.height);
graph.center = CGPointMake(CGRectGetMidX(finalView.bounds), CGRectGetMidY(finalView.bounds));
[finalView addSubview:graph];
return finalView;
}
In my case GPX class contains few values :
- NSArray<NSArray<NSDictionary *> *> *paths; : contains all points of all paths. In your case I think it is your MKPolyline.
- topRightCoordinate and bottomLeftCoordinate : Two CLLocationCoordinate2D that represent top right and bottom left virtual coordinates of my path (you have to calcul them also).
You call this method like that :
UIView *shape = [YOURCLASS createShapeForGPX:gpx withFrameSize:CGSizeMake(32, 32) lineColor:[UIColor blackColor]];
This solution is based on this question how to draw a line graph in ios? Any control which will help me show graph data in ios which gives a solution to draw a graph from points.
Maybe all this code is not usefull for you (like margins) but it should help you to find your own solution.
Here is how it displays in my app (in a UITableView) :
I have placed a view on a viewController and I want to draw a rectangle in this view, but with a custom height. I determine the height based on a parameter in my viewController.
So for example. If my parameter is 50, I want the rectangle to have the height of 50% of the UIView.
2 questions:
how can I pass the height to the custom drawRect?
how do I make the rectangle be placed on the bottom of the UIView?
I have placed the view using Interface Builder and I have implemented the drawRect in a subclass of UIView and used this as Custom Class for my UIView.
So in the custom class I have:
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
UIColor * lightGrayColor = [UIColor colorWithRed:230.0/255.0 green:230.0/255.0 blue:230.0/255.0 alpha:1.0];
CGRect paperRect = self.bounds;
drawLinearGradient(context, paperRect, lightGrayColor.CGColor, [UIColor clearColor].CGColor);
}
void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor, CGColorRef endColor)
{
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = { 0.0, 1.0 };
NSArray *colors = #[(__bridge id) startColor, (__bridge id) endColor];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations);
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGContextSaveGState(context);
CGContextAddRect(context, rect);
CGContextClip(context);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGContextRestoreGState(context);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
}
This draws a nice gradient rectangle in my view but it fills the complete UIView.
This is because of the self.bounds.
I have added a property *height to my custom class, but I don't know how to fill this from my viewController. So I want it to start at the bottom off the UIView, and make it as high as I have determined (a % of the real height).
Anybody knows how I can achieve this?
have you set your custom view class in identity inspector in interface builder?
you can set the height property from your viewController:
((MyViewClass *) self.myView).height = myCalculatedValue;
then implement drawRect:
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
UIColor * lightGrayColor = [UIColor colorWithRed:230.0/255.0 green:230.0/255.0 blue:230.0/255.0 alpha:1.0];
CGRect paperRect = self.bounds;
paperRect.origin.y = paperRect.size.height - self.height;
paperRect.size.height = self.height;
//assuming your height property is of CGFloat type
drawLinearGradient(context, paperRect, lightGrayColor.CGColor, [UIColor clearColor].CGColor);
}
this will draw your gradient from (height) points above the bottom to the bottom
I think you can get calculate 50% of screen height and then substract new view height from the full screen Height
youParameter = 50; // Lets assume this is your parametere
int heightOfView = ([UIScreen mainScreen].bounds.size.height * yourParameter) / 100;
// For placing the view to the bottom;
CGRect newFrame;
frame.origin.y = [UIScreen mainScreen].bounds.size.height - heightOfView;
frame.origin.x = 0;
frame.size.width = [UIScreen mainScreen].bounds.size.width; // assuming it will take full width
frame.size.height = heightOfView;
youViewToChange.frame = newFrame; // normally do this or
To pass the value to drawRect you can do:
[self drawRect:newFrame];
I am facing a problem with NSString method drawAtPoint and kCGTextFillClip. I needed to create a UILabel with a gradient instead of a simple color, so I've subclassed it and overrode the drawRect method. I managed to get what I wanted by using the function CGContextShowTextAtPoint, but it doesn't handle UTF-8 properly, which is essential to me.
I am aware that this is a common problem so after searching for a bit, I found out that I could use drawAtPoint to solve this encoding problem. Indeed, the text is now showing properly. The problem is I don't know how to make kCGTextFillClip work anymore.
Look at the result.
As you can see, it seems to clip to the first letter just fine, but not after that. Do you have any idea on how to solve this issue?
Here's the code I'm using :
- (void)drawRect:(CGRect)rect {
CGContextRef theContext = UIGraphicsGetCurrentContext();
CGRect viewBounds = self.bounds;
CGContextSaveGState(theContext);
CGContextTranslateCTM(theContext, 0.0, viewBounds.size.height/2 + 1);
CGContextSetTextDrawingMode(theContext, kCGTextFillClip);
[self.text drawAtPoint:CGPointMake(0, 0) withFont:[UIFont fontWithName:#"Helvetica-Bold" size:11.5]];
// Creation of the gradient
CGFloat locations[2] = { 0.0f, 1.0f };
CGFloat components[8] = {
190.0/255, 190.0/255, 190.0/255, 1.0, // Start color
113.0/255, 113.0/255, 113.0/255, 1.0 // End color
};
CGColorSpaceRef rgbColorspace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, 2);
CGPoint topCenter = CGPointMake(CGRectGetMidX(viewBounds), 0.0f);
CGPoint midCenter = CGPointMake(CGRectGetMidX(viewBounds), CGRectGetMidY(viewBounds));
CGContextDrawLinearGradient(theContext, gradient, topCenter, midCenter, 0);
Thank you!
Try and draw letter by letter?