I have a an array of UIBezierPaths which I drew and also a UIImageView all put on a UIView. When I draw a UIBezierPath and save it to the array, then add a UIImageView, then draw another path, the second path will appear behind the image. Is there something such as a bringSubViewToFront: sort of thing for UIBezierPath?
- (void)drawRect:(CGRect)rect
{
for(int a=0; a<_pathArray.count; a++) {
[(UIColor )[_colorArray objectAtIndex:a] setStroke];
[(UIBezierPath)[_pathArray objectAtIndex:a] strokeWithBlendMode:kCGBlendModeNormal alpha:1.0];
}
}
where _colorArray is the array of the colors for each of the paths, and _pathArray is the array of the UIBezierPaths. I'm inserting a UIImageView onto the view and then what I want to do is to be able to draw on the whole UIView (including on top of the UIImageView).
All subviews will always be drawn on "top" of the superview. Bezier paths aren't subviews, they are drawn as part of the view so they will always be behind the subviews.
You can either render the images that you are currently adding to the image views into the current context (during drawRect). Or you could add the image views behind the view that you draw the Bezier paths into (by adding them to the same superview, or by adding a new sibling view that the beziers are drawn into.
Related
Using this tutorial: http://www.techotopia.com/index.php/An_Example_iOS_4_iPhone_Camera_Application_%28Xcode_4%29
I have displayed on the UI an image from the photo library. I would like to simply overlay some basic text or a shape (like a square) over the UIImage. Is there an easy way to do this?
Thank you.
Do you have an UIImage only or do you display it using an UIImageView?
If you have an UIImageView then you can just add a UILabel as sub view to your image view.
Assuming your UIImageView is named myImageView ...
// Create and init the label with any rect.
UILabel *myLabel = [[UILabel alloc] initWithFrame:myImageView.frame]; // The lable has the same size as the Image has.
mylabel.text = #"the text";
[myImageView addSubview:myLabel];
//A subview's coordinates are within its superview's coordinates.
mylabel.center = CGPointMake(myImageView.frame.size.width/2,myImageView.frame.size.height/2) // aligning the centers of the views
//An alternative could be transforming the image's center into the coordinate system of its superviews. That code would look smarter but his example is imho easier to understand.
//Setting front, size, alpha, background colour ...
...
You can create a subclass of a UIView, make it transparent, and add an instance of this class, the same size as your image, as a overlay subview on top of the image. Then, when you want to display something on top of your image, call setNeedsDisplay on the transparent subview, and draw whatever you want (colored rectangles, text, etc.) in the drawRect callback of this view.
To make a UIView subclass transparent, add this to the view class initWithFrame method:
[ self setBackgroundColor:[ UIColor clearColor ] ];
To describe my project:
I have a rectangle UIImageView frame floating over a white layer. Inside the UIImageView, I'm successfully creating the illusion that it is showing a portion of a background image behind the white layer. You can drag the rectangle around, and it will "redraw" the image so that you can peer into what is behind the white. Its basically this code:
//whenever the frame is moved, updated the CGRect frameRect and run this:
self.newCroppedImage = CGImageCreateWithImageInRect([bgImage.image CGImage], frameRect);
frame.image = [UIImage imageWithCGImage:self.newCroppedImage];
Anyhow, I also have a rotation gesture recognizer that allows the user to rotate the frame (and consequentially rotates the image). This is because the CGRect sent to the CGImageCreateWithImageInRect is still oriented at its original rotation. This breaks the illusion that you're looking through a window because the image you see is rotated when only the frame should appear that way.
So ideally, I need to take the rotation of my frame and apply it to the image created from my bgImage. Does anyone have any clues or ideas on how I could apply this?
I suggest you take a different approach. Don't constantly create new images to put in your UIImageView. Instead, set up your view hierarchy like this:
White view
"Hole" view (just a regular UIView)
Image view
That is, the white view has the hole view as a subview. The hole view has the UIImageView as its subview.
The hole view must have its clipsToBounds property set to YES (you can set it with code or in your nib).
The image view should have its size set to the size of its image. This will of course be larger than the size of the hole view.
And this is very very important: the image view's center must be set to the hole view's center.
Here's the code I used in my test project to set things up. The white view is self.view. I start with the hole centered in the white view, and I set the image view's center to the hole view's center.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];
CGRect bounds = self.view.bounds;
self.holeView.center = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
self.holeView.clipsToBounds = YES;
bounds = self.holeView.bounds;
self.imageView.center = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
self.imageView.bounds = (CGRect){ CGPointZero, self.imageView.image.size };
}
I also set the image view's size to the size of its image. You might want to set it to the size of the white view.
To pan and rotate the hole, I'm going to set holeView.transform. I'm not going to change holeView.frame or holeView.center. I have two instance variables, _holeOffset and _holeRotation, that I use to compute the transform. The trick to making it seem like a hole through the white view, revealing the image view, is to apply the inverse transform to the image view, undoing the effects of the hole view's transform:
- (void)updateTransforms {
CGAffineTransform holeTransform = CGAffineTransformIdentity;
holeTransform = CGAffineTransformTranslate(holeTransform, _holeOffset.x, _holeOffset.y);
holeTransform = CGAffineTransformRotate(holeTransform, _holeRotation);
self.holeView.transform = holeTransform;
self.imageView.transform = CGAffineTransformInvert(holeTransform);
}
This trick of using the inverse transform on the subview only works if the center of the subview is at the center of its superview. (Technically the anchor points have to line up, but by default the anchor point of a view is its center.)
I put a UIPanGestureRecognizer on holeView. I configured it to send panGesture: to my view controller:
- (IBAction)panGesture:(UIPanGestureRecognizer *)sender {
CGPoint offset = [sender translationInView:self.view];
[sender setTranslation:CGPointZero inView:self.view];
_holeOffset.x += offset.x;
_holeOffset.y += offset.y;
[self updateTransforms];
}
I also put a UIRotationGestureRecognizer on holeView. I configured it to send rotationGesture: to my view controller:
- (IBAction)rotationGesture:(UIRotationGestureRecognizer *)sender {
_holeRotation += sender.rotation;
sender.rotation = 0;
[self updateTransforms];
}
I have a UIButton of the custom button type, where I just set a background and border color on the underlying CALayer of the button. I then applied a mask to this underlying layer composed of a CAShapeLayer (to get two rounded corners) as described in this post.
So far so good. I animate the UIButton to grow in size. So far so good. See below (everything in the image is the _fusedBar, save the x-axis line snippet you see below it and the dollar amount text).
When I go to animate the UIButton to shrink using the same animation routine but different parameter values (affine translation with simultaneous frame height change), none of the UIButton (save for a sliver) is visible as the shrinking is taking place.
I've tried to have the CAShapeLayer that is the mask remove itself from its super layer, I've tried to set the mask property on the UIButton's layer to nil, etc., but none of these remove the mask such that I can see the UIButton animate (shrinking with affine translation and frame height change).
Here's the relevant animation code:
// Stackoverflow readers: Assume 'duration', 'scaledHeight' and 'delay'
// already defined.
void (^fusedBarChange)(void) = ^{
_fusedBar.transform = CGAffineTransformMakeTranslation(0, -scaledHeight);
[_fusedBar nd_setNewSizeHeight:scaledHeight];
};
[UIView animateWithDuration:duration
delay:delay
options:UIViewAnimationCurveEaseOut
animations:^(void) {
fusedBarChange();
}
completion:^(BOOL finished) {
// Other stuff not relevant to _fusedBar animation
}];
Before this animation gets called, I have the mask setup per the post cited above. That code is below. Note that '_fusedBar' is the UIButton at the heart of this discussion.
- (void)configureRoundedTop {
// YES: Start by creating the path (with only the top-left corner rounded)
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:_fusedBar.bounds
byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight
cornerRadii:CGSizeMake(4.0, 4.0)];
// Create the shape layer and set its path
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = _fusedBar.bounds;
maskLayer.path = maskPath.CGPath;
// Set the newly created shape layer as the mask for the image view's layer
_fusedBar.layer.mask = maskLayer;
}
When growing the _fusedBar from a sliver to a few hundred points, the growth is animated, and you can see it.
Prior to using this CAShapeLayer mask, when shrinking the _fusedBar back to a sliver, I could see the animation (same code above, just a different value for 'scaledHeight'). Now, I cannot see the shrinking animation.
So, I know I need to somehow shake-off the effects of the masking layer before animating the shrinking operation, but the obvious things (to me) haven't worked. Here's an example of various things I've tried:
// None of these do the trick. I call them just before
// the shrinking animation is invoked:
[_fusedBar setNeedsLayout];
[_fusedBar setNeedsDisplay];
[_fusedBar.layer.mask removeFromSuperlayer];
_fusedBar.layer.mask = nil;
Clearly, I'm missing some nugget of CALayers, masking and perhaps even presentationLayer semantics.
Any guidance much appreciated!
Coming back to it after a couple of days, I saw my mistake (not visible from the extracts posted above).
Essentially, after the animation to grow or shrink the "fusedBar", I was reapplying a mask based on the view's frame. Since this would effectively execute in concert with the animation block, it was going to have a mask that the view was shrinking into.
I needed to put that re-apply mask code in my animation completion block or suppress it if the fusedBar was shrinking.
Thanks to all those who took a look to help out!
I'm trying to draw in a scrollview on top of an image using multi-touch. The circles are actually drawn, but under the image. In my code I currently create a custom ScrollView:
#interface AppScrollView : UIScrollView {
//objects to draw circles
NSMutableDictionary *circlesInProcess;
NSMutableArray *completeCircles;
}
#end
In my AppScrollView.m I override touches commands to store touches and add a
(void)drawRect:(CGRect) rect
method to draw the circles in the scrollview. I also include
[self setNeedsDisplay]
to display the circles. In my app controller, I declare the new custom scrollView object:
IBOutlet AppScrollView *scrollView;
Later I add an image to the scrollView using:
[scrollView addSubview:myImage];
The image is visible, but when I try to draw circles on top of it, they are drawn under the image. Let me know if you have any suggestions?
Suggestion: draw the image onto your scrollview (instead of adding the image view as a subview) and then draw the circles on top of that.
In your code check where did you add you custom scrollview. Is it added after imageView is added or before.
You scrollView should be on top of imageView.
I have UIViewController and a ScrollView. I add the scrollview as a subview to the UIViewController. The main aim of the scroll view is to add the tiled images. I have basically followed the example provided by developer.apple.com.
http://developer.apple.com/library/ios/#samplecode/ScrollViewSuite/Introduction/Intro.html
The question is how do I draw line on top of the scrollView. The sample code adds label on top of each tile (i.e. in scroll view class).
Can anyone, explain me on how to draw line on top of the view.
I did try adding lines using
- (void)drawRect:(CGRect)rect
{
CGFloat red[4] = {1.0f, 0.0f, 0.0f, 1.0f};
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextSetStrokeColor(c, red);
CGContextBeginPath(c);
CGContextMoveToPoint(c, 5.0f, 5.0f);
CGContextAddLineToPoint(c, 50.0f, 50.0f);
CGContextStrokePath(c);
}
But the problem with this approach is that though the line is being draw its been hidden under the tiled image.
Can anyone help me on this?
Add the line to a transparent subview that is layered on top of the scrollview?
Use a CAShapeLayer (the code is a lot like what you have now, only building up a path for the CAShapeLayer). Then you can add the line layer as a sublayer ahead of the other content in the scroll view.