Arc's position on circle - iphone

I've got few pixels wide circle (circle is UIBezierPath based). I have to put an arc (which is basically UIView subclass with custom drawing) on the circle so the arc covers circle. I know how to calculate rotation of arc and position but something is not right. I mean I know the reason - it's beacause center property which is assigned to center of UIView, if it was center of the arc, everything would be great. But it's not.
I also know how to solve that. I have to calculate smaller radius where I will put arcs on. But how to do that. Basically it seems easy but because of the arc is in rectangular UIView it gets a bit harder. I'll show you some images so you can see the problem.

The easiest way to do this is to change the anchor point of each arc view's layer. You can read about the anchor point here if you don't already know about it.
You will need to add the QuartzCore framework to your build target and add #import <QuartzCore/QuartzCore.h>.
CGRect circleBounds = circleView.bounds;
topArcView.layer.anchorPoint = CGPointMake(.5, 0);
topArcView.layer.position = CGPointMake(CGRectGetMidX(circleBounds), 0);
bottomArcView.layer.anchorPoint = CGPointMake(.5, 1);
bottomArcView.layer.position = CGPointMake(CGRectGetMidX(circleBounds), CGRectGetMaxY(circleBounds));
leftArcView.layer.anchorPoint = CGPointMake(0, .5);
leftArcView.layer.position = CGPointMake(circleBounds.origin.x, CGRectGetMidY(circleBounds));
rightArcView.layer.anchorPoint = CGPointMake(1, .5);
rightArcView.layer.position = CGPointMake(CGRectGetMaxX(circleBounds), CGRectGetMidY(circleBounds));

Perhaps you could make the UIView subclass the same size and center point as the circle's rect. Then draw the arc in the correct location.

Related

How to create round shape UIview instead of rectangle shape

Hi in my application i need a view which is of round shape instead of a rectangle shape. How to create a uiview object of round shape please let me know. Thanks in advance.
Technically, all UIView's will always be "rectangles", meaning they will be placed on the screen using {x, y} coordinates and they will be dimensioned with a height and a width (Making them rectangles). However, within the bounds of a UIView you can do a lot to make it appear as a circle. Here are some methods:
Use UIImageView and set it's Image to be an image of a circle. This is easy, but not very flexible.
Learn Core Graphics (also known as Quartz2D) and draw a circle in the UIView's -drawRect: method. Quartz 2D Programming Guide
Use a CAShapeLayer for the UIView's layers. CAShapeLayer Class Reference
There are certainly other methods but this should be a good start. If you need to detect touches within the circle, you can use either option 2. or 3. and keep a reference to the CGPathRef (or UIBezierPath) and use CGPathContainsPoint to determine if the touch is within the bounds of the circle and act accordingly.
You can set the cornerRadius of the layer of your view.
#import <QuartzCore/QuartzCore.h>
yourView.layer.cornerRadius = 20;

Move CALayer via UIBezierPath

I have a layer that will move from point A to point B on a UIBezierPath.
I have found a lot of samples those are refers to CAAnimation and UIBezierPath.
But I need to move my layer only from specified point to another on bezier path.
Any suggestions would be appreciated.
Thanks
Hope this will be helpful.
UIBezierPath *trackPath = [UIBezierPath bezierPath];
.
.
.
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
anim.path = trackPath.CGPath;
anim.repeatCount = 1;
anim.duration = 2.0;
[layerToAnimate addAnimation:anim forKey:#"pathGuide"];
If your bezier path is a circle or even a half circle, you could just skip the path and instead add the point to a larger square layer. Set the point to be a fixed distance from the center of that larger square layer. Then you can just rotate the larger layer on the z axis around the center whatever number of degrees you need it to move. Seems it would simplify things a bit, though I'm not sure exactly what else you need it to do.
i'd been looking to do something like this and fount this tutorial, it shows how to follow a specific path. It works with a car (a CALayer) and a UIBezierpath as the race track and is solved in this order:
Defining the path the car should follow (in this case your BezierPath)
Drawing the black line that defines the track; (N/A)
Drawing the white dashed center-line of the track; (N/A)
Creating the layer defining the car; (your Layer)
Animating the car along the path. (What your asked!)
You can check the reference Post here:
http://nachbaur.com/2011/01/07/core-animation-part-4/
Also you can download the source code here:
http://cdn5.nachbaur.com/wp-content/uploads/2011/01/CALayerAnimTest.zip?25881d
hope this helps!
regards,
Jorge.

MKOverlayView and OpenGL

I currently have a UIView that draws radar data on top of a MKMapView using OpenGL. Because of the level of detail in the radar image, OpenGL is required (CoreGraphics is not fast enough).
All of the images that I am drawing are saved in MKMapPoints. I choose them over the standard CLLocationCoordinate2D because their lengths do not depend on the latitude. The basic method for drawing is this:
Add the GLView as a subview of the MKMapView and set GLView.frame = MKMapView.frame.
Using GLOrthof, set the projection of the GLView to equal the current visible MKMapRect of the map. Here is the code that does this.
CLLocationCoordinate2D coordinateTopLeft =
[mapView convertPoint:CGPointMake(0, 0)
toCoordinateFromView:mapView];
MKMapPoint pointTopLeft = MKMapPointForCoordinate(coordinateTopLeft);
CLLocationCoordinate2D coordinateBottomRight =
[mapView convertPoint:CGPointMake(mapView.frame.size.width,
mapView.frame.size.height)
toCoordinateFromView:mapView];
MKMapPoint pointBottomRight = MKMapPointForCoordinate(coordinateBottomRight);
glLoadIdentity();
glOrthof(pointTopLeft.x, pointBottomRight.x,
pointBottomRight.y, pointTopLeft.y, -1, 1);
Set the viewport to be the correct size using glViewport(0, 0, backingWidth, backingHeight) where backingWidth and backingHeight is the size of the mapView in points.
Draw using glDrawArrays. Not sure if this matters, but GL_VERTEX_ARRAY and GL_TEXTURE_COORD_ARRAY are both enabled during the draw.
Using this method, everything works fine. The drawing is performed like it is supposed to. The only problem is since it is a subview of the mapView (and not an overlay), the radar image is drawn on top of any other MKAnnotations and MKOverlays. I need this layer to be drawn under the other annotations and overlays.
What I tried to do to get this working was to make the glView a subview of a custom MKOverlayView instead of the mapView. What I did was give the MKOverlay a boundingMapRect of MKMapRectWorld and set the frame of the glView the same way that I set the projection (since frame of a MKOverlayView is determined by MKMapPoints and not CGPoints). Again, here is the code.
CLLocationCoordinate2D coordinateTopLeft =
[mapView convertPoint:CGPointMake(0, 0)
toCoordinateFromView:mapView];
MKMapPoint pointTopLeft = MKMapPointForCoordinate(coordinateTopLeft);
CLLocationCoordinate2D coordinateBottomRight =
[mapView convertPoint:CGPointMake(mapView.frame.size.width,
mapView.frame.size.height)
toCoordinateFromView:mapView];
MKMapPoint pointBottomRight = MKMapPointForCoordinate(coordinateBottomRight);
glRadarView.frame = CGRectMake(pointTopLeft.x, pointTopLeft.y,
pointBottomRight.x - pointTopLeft.x,
pointBottomRight.y - pointTopLeft.y);
When I do this, the glView is positioned correctly on the screen (in the same place that is was while it was a subview of the mapView), but the drawing no longer works correctly. When the image does come up, it is not the right size and not in the correct location. I did a check and backingWidth and backingHeight are still the size of the view in points (as they should be).
Any idea why this is not working?
I've been away from iphone for too long to really fully grok your code, but I seem to recall from when I messed with Open GL on the iPhone some time ago, I found that it was necessary to maintain my own z-Index and simply draw items in that order... Each draw operation happened rotated in 3d properly, but something drawn later was always on top of something drawn earlier. One of my early test programs drew a grid on a surface, and then caused the whole thing to flip. My expectation was that the grid would disappear when the back of the object was facing me, but it remained visible because it was drawn later in a separate operation (IIRC)
It's possible that I was doing something wrong that caused that problem, but my solution was to order my draws by a z index.
Can you draw the image first using your first method?
I think that you should just set the viewport before setting the projection mode.

iPhone landscape-only app view axis confusion

Using the information here: iPhone Landscape-Only Utility-Template Application I am able to launch, use and maintain my view as landscape only. However, I am confused about the axis.
The absolute axis behaves as expected, meaning (0,0) is the top left, (240,0) is the top center, (0,320) is the bottom left corner, etc. However, when I attempt to do calculations related to the view I am drawing on I find x,y to be oriented as if the portrait top left were the origin. So to draw something at the center-point in my view controller I need to do:
CGPoint center = CGPointMake(self.view.center.y, self.view.center.x);
I assume this due to the fact that the UIView referenced by my controllers self.view is giving it's values relative to it's enclosing frame, meaning the window which has it's axis origin on the top left in portrait mode.
Is there some simple way to account for this that I am missing?
Documentation suggests that the transform property is exactly what I am looking for, however, I experiencing further confusion here. There are 3 essential properties involved:
frame
bounds
center
If I do this in viewDidLoad:
// calculate new center point
CGFloat x = self.view.bounds.size.width / 2.0;
CGFloat y = self.view.bounds.size.height / 2.0;
CGPoint center = CGPointMake(y, x);
// set the new center point
self.view.center = center;
// Rotate the view 90 degrees counter clockwise around the new center point.
CGAffineTransform transform = self.view.transform;
transform = CGAffineTransformRotate(transform, -(M_PI / 2.0));
self.view.transform = transform;
Now according to the reference docs, if transform is not set to the identity transform self.view.frame is undefined. So I should work with bounds and center.
self.view.center is correct now, because I set it to what I wanted it to be.
self.view.bounds appears unchanged.
self.view.frame appears to be exactly what I want it to be, but as noted above, the reference claims it is invalid.
So while I can get what I believe to be the right numbers, I fear I am overlooking something critical that will become troublesome later.
Thanks.
With Quartz 2D, the coordinate system has its origin (0,0) at the lower-left corner of the graphic, not at the upper-left corner. Maybe that's what you are missing.
See: http://developer.apple.com/documentation/graphicsimaging/Conceptual/drawingwithquartz2d/dq_overview/dq_overview.html#//apple_ref/doc/uid/TP30001066-CH202-CJBBAEEC

Convert a CGPoint from a UIView coordinate system to a CALayer coordinate system

There are methods to transfer a CGPoint from one UIView to another and from one CALayer to another. I cannot find a way to change a CGPoint from a UIView's coordinate system to a CALayer's coordinate system.
Does a layer and it's host view have the same coordinate system? Do I need to transform points between them?
Thanks,
Corey
[EDIT]
Thanks for the answer! It is hard to find information on the differences/similarities between CA on the iPhone and Mac. I am surprised I couldn't find this issue addressed directly in any Apple Documentation.
I was pursuing this answer to help with a bug I am troubleshooting, and this was so far my best guess, but I suppose I am barking up the wrong tree. If the coordinate systems are the same, then I have another issue...
The actual issue I am having can be found here on Stack Overflow:
layer hit test only returning layer when bottom half of layer is touched
The documentation for hitTest says:
/* Returns the farthest descendant of the layer containing point 'p'.
* Siblings are searched in top-to-bottom order. 'p' is in the
* coordinate system of the receiver's superlayer. */
So you need to do (something like this):
CGPoint thePoint = [touch locationInView:self];
thePoint = [self.layer convertPoint:thePoint toLayer:self.layer.superlayer];
CALayer *theLayer = [self.layer hitTest:thePoint];
In terms of Apple documentation, I found an "iPhone OS Note" in the Layer Coordinate System section of the Core Animation Programming Guide (2008-11-13).
The default root layer of a UIView instance uses a flipped coordinate system that matches the default coordinate system of a UIView instance–the origin is in the top-left and values increase down and to the right. Layers created by instantiating CALayer directly use the standard Core Animation coordinate system.
You can set the transform property of CALayers to the appropriate transform if you want to flip their coordinate system, but note that this will probably flip their drawing of the contents as well (I have not tested that, but it makes sense that this would be true). My assertion that the CALayer associated with a UIView shares the same coordinate system could in fact be entirely erroneous. It could also be that CALayers use the same coordinate system as UIViews (i.e. they're never flipped vertically), but I thought they were since CoreGraphics uses a flipped coordinate system relative to UIKit.
A simple way to test would be to add a screen-sized CALayer as the sublayer of a view's layer, then add another small CALayer as a sublayer of that. You could set it to show up at (0, 0, 320, 100) and see if it shows up on the top or the bottom of the iPhone's screen. This will tell you in which direction the Y axis goes for CoreAnimation.
- (void)viewDidLoad {
[super viewDidLoad];
CALayer *rootLayer = [CALayer layer];
rootLayer.frame = self.view.layer.bounds;
CALayer *smallLayer = [CALayer layer];
smallLayer.frame = CGRectMake(0, 0, rootLayer.bounds.size.width, 50);
smallLayer.backgroundColor = [UIColor blueColor].CGColor;
[rootLayer addSublayer:smallLayer];
[self.view.layer addSublayer:rootLayer];
}
I just performed this test myself, and it appears CALayers actually use the same coordinate system as UIViews, so my assertion that CALayer's flip the Y axis is definitely wrong. However, if you do drawing with CoreGraphics directly, be aware that CoreGraphics does use a flipped Y axis (though when drawing in a UIView subclass or, I assume, a CALayer delegate, the CoreGraphics context has already been flipped to match the UIView's (or CALayer's) coordinate system).
So the short answer, if you made it this far, is the coordinate system for CALayer should match the coordinate system for its corresponding UIView.
From my tests I've found out that sublayers share the same coordinate system as it's parent layer and therefore if you are adding sublayer to a UIView layer then they will share the same coordinate system.
In case you still want to flip the coordinate system so that it's origin is in the upper left corner you should use this transformation that flips the y axis.
(x,y,1) = (x', y', 1) * [1 0 0],[0 -1 0],[0 heigth 1]
which translated to code is:
[your_layer setAffineTransform:CGAffineTransformMake(1,0,0,-1,0,heigth)];
I believe the coordinate system of a UIView and its associated layer should be the same. However, if you create layers yourself, the coordinate system is flipped vertically.
Methods that fixing frames and points
- (CGRect) fixRect:(CGRect)rect inRect:(CGRect)container
{
CGRect frame = rect;
frame.origin.y = container.size.height - frame.origin.y - frame.size.height;
return frame;
}
- (CGPoint) fixPoint:(CGPoint)point fromPoint:(CGSize)containerSize
{
CGPoint frame = point;
frame.y = size.height - frame.y;
return frame;
}