drawRect in Custom UIView off by height of navigation bar - iphone

I'm drawing a custom UIView that is sitting inside a xib whose ViewController is pushed onto a NavigationController.
Essentially the problem is that in the call to drawRect:(CGRect)rect, rect has origin at (0,0) when it should have origin at (0,nav_bar_height). Therefore, the following code, which draws an image
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
UIGraphicsPushContext(context);
// set up the image
UIImage * img = [UIImage imageWithData:someData];
// flip the image to the correct orientation
CGContextTranslateCTM(context, 0, rect.size.height + rect.origin.y );
CGContextScaleCTM(context, 1, -1);
// draw the image
CGContextDrawImage(context, rect, [img CGImage]);
UIGraphicsPopContext();
}
This will cut off the top 30 pixels or so of the image and leave empty the bottom 30.
How can I account for the navigation bar height?

Without knowing more about your view-structure, I risk being unhelpful, but let me know if the following helps at all: instead of using the rect parameter, which can be sort of unpredictable, try using the bounds of your custom view. In reality, I can't imagine why you'd be having this problem, unless your view is being overlapped with the navigation bar; I'd suggest checking to be sure this isn't so, in any case. Best of luck!
Update
Looks like that didn't help. Just offset your y parameter by self.navigationController.navigationBar.bounds.size.height. So your code should look like:
//...
CGFloat dy = self.navigationController.navigationBar.bounds.size.height;
CGRect r = CGRectMake(rect.origin.x,rect.origin.y + dy, rect.size.width, rect.size.height - dy);
//...
CGContextDrawImage(context,r,img.CGImage);
//...
I hope that was more helpful.

Related

Mask a View's Path in It's Superview To Reveal It's SuperSupervView

I am trying to figure how to mask the path of a view based on the path of it's subview to reveal the underlying view. I'm not sure that I can articulate what I'm trying to do 100% clearly, so here's a pic I drew:
To further clarify and drive the point home - View 3 is a subview of view 2. View 2 is a subview of view 1. View 3 has a clear background that reveals View 1.
I want to be able to dynamically mask this area based on the subviews of View 2. So - in view 2's drawRect - I want to be able to survey its subviews, attain their frames, and mask view 2 to those frames.
I'm not too shabby with the rest of iOS/Objective-c, but I'm still leaning Core Graphics and can't quite figure this one out. Any help would be MUCH appreciated.
One easy way might be for View 2 to draw all of itself as if there were no subviews, then iterate the subviews calling NSEraseRect with each subviews frame. Do note that view 3 is going to have to be completely transparent (i.e. not draw anything) for this to work.
I've got some working code here that does what you are attempting to do. Just make sure you set the backgroundColor of your UIView to clearColor.
Here's the drawRect: method:
- (void)drawRect:(CGRect)rect {
[[UIColor purpleColor] setFill];
CGContextRef context = UIGraphicsGetCurrentContext();
for (UIView *subview in self.subviews) {
CGContextAddPathWithRect(context, subview.frame, NO);
}
CGContextAddPathWithRect(context, rect, YES);
CGContextFillPath(context);
}
Essentially, what's happening here is that we create a bunch of paths for the subviews that go counter-clockwise, and then we make a single large path going clockwise for the current view's frame. Then, because of the even-odd rule, it will only color the part of the view that is not covered by subviews.
I've create this function CGContextAddPathWithRect to make the code cleaner and easier to read. Here's its implementation:
void CGContextAddPathWithRect(CGContextRef context, CGRect rect, BOOL clockwise) {
CGFloat x, y, width, height;
x = rect.origin.x; y = rect.origin.y;
width = rect.size.width; height = rect.size.height;
if (clockwise) {
CGContextMoveToPoint(context, x, y);
CGContextAddLineToPoint(context, x + width, y);
CGContextAddLineToPoint(context, x + width, y + height);
CGContextAddLineToPoint(context, x, y + height);
} else {
CGContextMoveToPoint(context, x + width, y + height);
CGContextAddLineToPoint(context, x + width, y);
CGContextAddLineToPoint(context, x, y);
CGContextAddLineToPoint(context, x, y + height);
}
CGContextClosePath(context);
}
Here's a zip link to the test project I've created: SOHoleSubviews.zip

divide image into two parts using divider

I'm working on one app where I need to divide a image into two part using a red line.
left part for labels
right part for prices
Question 1.
How can I draw a red line on image?
Question 2.
How can I divide image to two parts using red line ?( red line position is not fixed. user can move the position wherever it want)
Question 3.
How can I get line current position and how can I use that position two divide image
Thanks in advance
I would approach this in somewhat the same manner as koray was suggesting:
1) I am assuming that your above image/view is going to be managed by a view controller, which I will call ImageSeperatorViewController from here on.
Inside of ImageSeperatorViewController, insert koray's code in the -(void) viewDidLoad{} method. Make sure you change the imageToSplit variable to be an UIImageView instead of a plain UIView.
2) Next, I assume that you know how to detect user gestures. You will detect these gestures, and determine if the user has selected the view (i.e. bar in koray's code). Once you have determined if the user has selected bar, just update its origin's X position with the touch position.
CGRect barFrame = bar.frame;
barFrame.origin.x = *X location of the users touch*
bar.frame = barFrame;
3) For cropping, I would not use github.com/bilalmughal/NLImageCropper, it will not do what you need to do.
Try this on for size:
Header:
#interface UIImage (ImageDivider)
- (UIImage*)imageWithDividerAt:(CGFloat)position width:(CGFloat)width color:(UIColor*)color;
- (UIImage*)imageWithDividerAt:(CGFloat)position patternImage:(UIImage*)patternImage;
- (NSArray*)imagesBySlicingAt:(CGFloat)position;
#end
Implementation:
#implementation UIImage (ImageDivider)
- (UIImage*)imageWithDividerAt:(CGFloat)position patternImage:(UIImage*)patternImage
{
//pattern image
UIColor *patternColor = [UIColor colorWithPatternImage:patternImage];
CGFloat width = patternImage.size.width;
//set up context
UIGraphicsBeginImageContext(self.size);
CGContextRef context = UIGraphicsGetCurrentContext();
//draw the existing image into the context
[self drawAtPoint:CGPointZero];
//set the fill color from the pattern image color
CGContextSetFillColorWithColor(context, patternColor.CGColor);
//this is your divider's area
CGRect dividerRect = CGRectMake(position - (width / 2.0f), 0, width, self.size.height);
//the joy of image color patterns being based on 0,0 origin! must set phase
CGContextSetPatternPhase(context, CGSizeMake(dividerRect.origin.x, 0));
//fill the divider rect with the repeating pattern from the image
CGContextFillRect(context, dividerRect);
//get your new image and viola!
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
- (UIImage*)imageWithDividerAt:(CGFloat)position width:(CGFloat)width color:(UIColor *)color
{
//set up context
UIGraphicsBeginImageContext(self.size);
CGContextRef context = UIGraphicsGetCurrentContext();
//draw the existing image into the context
[self drawAtPoint:CGPointZero];
//set the fill color for your divider
CGContextSetFillColorWithColor(context, color.CGColor);
//this is your divider's area
CGRect dividerRect = CGRectMake(position - (width / 2.0f), 0, width, self.size.height);
//fill the divider's rect with the provided color
CGContextFillRect(context, dividerRect);
//get your new image and viola!
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
- (NSArray*)imagesBySlicingAt:(CGFloat)position
{
NSMutableArray *slices = [NSMutableArray array];
//first image
{
//context!
UIGraphicsBeginImageContext(CGSizeMake(position, self.size.height));
//draw the existing image into the context
[self drawAtPoint:CGPointZero];
//get your new image and viola!
[slices addObject:UIGraphicsGetImageFromCurrentImageContext()];
UIGraphicsEndImageContext();
}
//second
{
//context!
UIGraphicsBeginImageContext(CGSizeMake(self.size.width - position, self.size.height));
//draw the existing image into the context
[self drawAtPoint:CGPointMake(-position, 0)];
//get your new image and viola!
[slices addObject:UIGraphicsGetImageFromCurrentImageContext()];
UIGraphicsEndImageContext();
}
return slices;
}
The concept is simple - you want an image with the divider drawn over it. You could just overlay a view, or override drawRect:, or any number of any solutions. I'd rather give you this category. It just uses some quick Core Graphics calls to generate an image with your desired divider, be it pattern image or color, at the specified position. If you want support for horizontal dividers as well, it is rather trivial to modify this as such. Bonus: You can use a tiled image as your divider!
Now to answer your primary question. Using the category is rather self explanatory - just call one of the two methods on your source background to generate one with the divider, and then apply that image rather than the original source image.
Now, the second question is simple - when the divider has been moved, regenerate the image based on the new divider position. This is actually a relatively inefficient way of doing it, but this ought to be lightweight enough for your purposes as well as only being an issue when moving the divider. Premature optimization is just as much a sin.
Third question is also simple - call imagesBySlicingAt: - it will return an array of two images, as generated by slicing through the image at the provided position. Use them as you wish.
This code has been tested to be functional. I strongly suggest that you fiddle around with it, not for any purpose of utility, but to better understand the mechanisms used so that next time, you can be on the answering side of things
For Crop you can try this,
UIImage *image = [UIImage imageNamed:#"yourImage.png"];
CGImageRef tmpImgRef = image.CGImage;
CGImageRef topImgRef = CGImageCreateWithImageInRect(tmpImgRef, CGRectMake(0, 0, image.size.width, image.size.height / 2.0));
UIImage *topImage = [UIImage imageWithCGImage:topImgRef];
CGImageRelease(topImgRef);
CGImageRef bottomImgRef = CGImageCreateWithImageInRect(tmpImgRef, CGRectMake(0, image.size.height / 2.0, image.size.width, image.size.height / 2.0));
UIImage *bottomImage = [UIImage imageWithCGImage:bottomImgRef];
CGImageRelease(bottomImgRef);
hope this can help you, :)
if you want to draw a line you could just use a UIView with red background and make the height the size of your image and the width around 5 pixels.
UIView *imageToSplit; //the image im trying to split using a red bar
CGRect i = imageToSplit.frame;
int x = i.origin.x + i.size.width/2;
int y = i.origin.y;
int width = 5;
int height = i.size.height;
UIView *bar = [[[UIView alloc] initWithFrame:CGRectMake(x, y, width, height)] autorelease];
bar.backgroundColor = [UIColor redColor];
[self.view addSubview:bar];

gradient cut from top to bottom

This is a follow up question to - gradient direction from left to right
In this apple refection sample code,
Apple Reflection Example
when the size slider is moved, the image is cut from bottom to top. How can I cut it from top to bottom when the slider is moved? I am trying to understand this tutorial better
//I know the code is in this section here but I can't figure out what to change
- (UIImage *)reflectedImage:(UIImageView *)fromImage withHeight:(NSUInteger)height
{
...
}
//it probably has something to do with this code.
//I think this tells it how much to cut.
//Though I can't figure out how does it know where the 0,0 of the image is and why
// keep 0,0 of the image on the top? I am assuming this is where it hinges its
//point and cuts the image from bottom to top
CGContextRef MyCreateBitmapContext(int pixelsWide, int pixelsHigh)
{
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// create the bitmap context
CGContextRef bitmapContext = CGBitmapContextCreate (NULL, pixelsWide, pixelsHigh, 8, 0, colorSpace,(kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst));
CGColorSpaceRelease(colorSpace);
return bitmapContext;
}
What if the image to reflect was on the top. So in order to show it properly, I need to reveal it from top down, not bottom up. That;s the effect I am trying to achieve. In this case I just moved the UIImageViews around in their storyboard example. You now see my dilemma
it's very similar to #Putz1103 answer. You should create a new method starting from the previous - (UIImage *)reflectedImage:(UIImageView *)fromImage withWidth:(NSUInteger)width.
- (UIImage *)reflectedImage:(UIImageView *)fromImage withWidth:(NSUInteger)width andHeight:(NSUInteger)height
{
....
CGContextClipToMask(mainViewContentContext, CGRectMake(0.0, 0.0, width, height), gradientMaskImage);
....
}
Then in slideAction method, use something like:
self.reflectionView.image = [self reflectedImage:self.imageView withWidth:self.imageView.bounds.size.width andHeight:reflectionHeight];
Good luck!
My guess would be this line:
CGContextClipToMask(mainViewContentContext, CGRectMake(0.0, 0.0, fromImage.bounds.size.width, height), gradientMaskImage);
If you change it to this it should do the opposite:
CGContextClipToMask(mainViewContentContext, CGRectMake(0.0, fromImage.bounds.size.height - height, fromImage.bounds.size.width, height), gradientMaskImage);
Basically you need to set the clipping rectangle to the bottom of the image instead of the top of the image. This will invert what your slider does, but that is easy to resolve and I'll leave that for your exercise.

Problem in drawing square in sample app

Hi i am making a sample app in which i wanto create a square for which i used the following code
- (void)viewDidLoad {
[super viewDidLoad];
[self drawRect:CGRectMake(0, 0, 300, 200)];
[[self view] setNeedsDisplay];
}
- (void) drawRect:(CGRect)rect
{
NSLog(#"drawRect");
CGFloat centerx = rect.size.width/2;
CGFloat centery = rect.size.height/2;
CGFloat half = 100/2;
CGRect theRect = CGRectMake(-half, -half, 100, 100);
// Grab the drawing context
CGContextRef context = UIGraphicsGetCurrentContext();
// like Processing pushMatrix
CGContextSaveGState(context);
CGContextTranslateCTM(context, centerx, centery);
// Uncomment to see the rotated square
//CGContextRotateCTM(context, rotation);
// Set red stroke
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
{
CGContextSetRGBFillColor(context, 0.0, 1.0, 0.0, 1.0);
}
// Draw a rect with a red stroke
CGContextFillRect(context, theRect);
CGContextStrokeRect(context, theRect);
// like Processing popMatrix
CGContextRestoreGState(context);
[[self view] setNeedsDisplay];
}
But nothing is drawn on screen , dont know wheres the issue is .When i debug it the CGContextRef context was always 0x0 , i dont know why its 0x0 always am i missing something in my code.
It looks like you're trying to draw in a subclass of UIViewController. You need to subclass UIView to override the drawRect: method, which is then called automatically with a valid graphics context in place. You almost never call this method yourself.
To quote the Apple docs:
"To draw to the screen in an iOS application, you set up a UIView object and implement its drawRect: method to perform drawing. The view’s drawRect: method is called when the view is visible onscreen and its contents need updating. Before calling your custom drawRect: method, the view object automatically configures its drawing environment so that your code can start drawing immediately. As part of this configuration, the UIView object creates a graphics context (a CGContextRef opaque type) for the current drawing environment. You obtain this graphics context in your drawRect: method by calling the UIKit function UIGraphicsGetCurrentContext."
So essentailly, your code is on track, you just need to get it in the right place. It needs to be in the View object.

Adding a vertical rule (border) between two UIScrollViews

I have two UIScrollView instances and I want to draw a vertical line between them (not 100% height, more like 80%). How can I achieve this?
Use Core Graphics. Nest the two UIScrollViews inside another view and override the drawRect method on the parent. See Apple's UIView documentation for drawRect for more detail and the Core Graphics Context Reference for more information about drawing.
- (void) drawRect: (CGRect) rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
// Draw divider
UIGraphicsPushContext(context);
// Fill the background, this must happen if the view is not opaque.
if (self.opaque)
{
[[UIColor whiteColor ] set];
CGContextFillRect(context, rect);
}
[[UIColor grayColor] set];
CGContextSetLineWidth(context, 1);
CGContextBeginPath(context);
CGContextMoveToPoint(context, rect.size.width * 0.5, 0.1 * rect.size.height);
CGContextAddLineToPoint(context, rect.size.width * 0.5, 0.9 * rect.size.height);
CGContextStrokePath(context);
UIGraphicsPopContext();
}
edit
To address my oversight, there is no simple way that I know of to draw on top of subviews.
This behavior can be simulated using the same basic view hierarchy as above. Subclass UIView to create a completely transparent view that does not accept any touch events. Place the drawing code (I added a conditional for opacity in the code above) in the custom view and then insert an instance of the custom view at the front of the view hierarchy.
A vertical line between your views could also just be a thin, tall UIView (on top of the scroll views) with, say, a solid background color. This would mean you don't need to worry about custom drawing code. This also lets you lay it out in Interface Builder, use autosizing, etc.