UPDATE
I need to draw a circle onto a MKMapView, something where I can get the radius of the circle and it's center coordinate. However, I would also like the circle to be a subview of the MKMapView, so that the map view can scroll underneath the circle, updating its center coordinate as the map moves and updating its radius as the map is zoomed in and out.
Does anyone know how I might be able to accomplish this?
This is the original wording of the question
I've drawn a circle onto a MKMapView using the code below:
- (void)viewDidLoad
{
[super viewDidLoad];
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.region = [MKCircle circleWithCenterCoordinate:self.locationManager.location.coordinate radius:kViewRegionDefaultDistance];
[self.mapView addOverlay:self.region];
}
- (MKOverlayPathRenderer *)mapView:(MKMapView *)map viewForOverlay:(id <MKOverlay>)overlay
{
MKCircleRenderer *region = [[MKCircleRenderer alloc] initWithOverlay:overlay];
region.strokeColor = [UIColor blueColor];
region.fillColor = [[UIColor blueColor] colorWithAlphaComponent:0.4];
return region;
}
This works and produces a circle on the map view. However, when I scroll the map view, the circle moves with it. I would like the circle to remain stationary and have the map view scroll underneath the circle.
Is is important to note that I will need to get the center coordinate and radius of the circle in order to create a region. For that reason, I cannot simply draw a UIView on top of the MKMapView, as I would have no way to get the radius in meters of the UIView.
I solved it!
Step 1:
I created a UIView and added it as a subview to the map view. It is important to note that I made sure to center the UIView on the map view. This is important because you will use the centerCoordinate property of the MKMapView to calculate the radius.
self.region = [[UIView alloc] initWithFrame:centerOfMapViewFrame];
self.region.contentMode = UIViewContentModeRedraw;
self.region.userInteractionEnabled = NO;
self.region.alpha = 0.5;
self.region.layer.cornerRadius = widthOfView/2;
self.region.backgroundColor = [UIColor blueColor];
[self.mapView addSubview:self.region];
The image below shows the circular UIView added as a subview to the mapView.
Step 2:
Calculate the radius based off of the center coordinate of map view and the edge coordinate of the UIView.
CLLocationCoordinate2D edgeCoordinate = [self.mapView convertPoint:CGPointMake((CGRectGetWidth(self.region.bounds)/2), 0) toCoordinateFromView:self.region]; //self.region is the circular UIView
CLLocation *edgeLocation = [[CLLocation alloc] initWithLatitude:edgeCoordinate.latitude longitude:edgeCoordinate.longitude];
CLLocation *centerLocation = [[CLLocation alloc] initWithLatitude:self.mapView.centerCoordinate.latitude longitude:self.mapView.centerCoordinate.longitude];
CGFloat radius = [edgeLocation distanceFromLocation:centerLocation]; //is in meters
The image below shows an annotation on the edgeLocation and on the centerLocation.
Swift 4.2/5 adaptation
let edgeCoordinate = self.mapView.convert(mapView.center, toCoordinateFrom: overlayView)
let edgeLocation: CLLocation = .init(latitude: edgeCoordinate.latitude, longitude: edgeCoordinate.longitude)
let centerLocation: CLLocation = .init(latitude: mapView.centerCoordinate.latitude, longitude: mapView.centerCoordinate.longitude)
let radius = edgeLocation.distance(from: centerLocation)
// do something with the radius
Where overlayView is the custom circle you created to represent radius
Related
I'm trying to add direction arrows to a MKPolyline, and I've almost got it, but for some reason some of the arrows are chopped off (see screen shot below). I'm not great at drawing with Core Graphics, so my guess is it's something in there. Anyone have any pointers on how to deal with the clipped arrows?
The following code does the drawing from in the drawMapRect:zoomScale:inContext: method of a subclassed MKPolylineRenderer:
MKMapPoint prevMapPoint = mapPoints[0];
MKMapPoint mapPoint = mapPoints[1];
for (NSUInteger i = 1; i < pointCount; i++) {
mapPoint = _mapPoints[i];
CGPoint prevCGPt = [self pointForMapPoint:prevMapPoint];
CGPoint cgPoint = [self pointForMapPoint:mapPoint];
CGFloat bearing = atan2(cgPoint.y - prevCGPt.y, cgPoint.x - prevCGPt.x) - M_PI;
//Get other two corners of triangle
CGFloat arrowAngle = degreesToRadians(40.0);
CGPoint pt2 = PointAtBearingFromPoint(cgPoint, bearing+arrowAngle/2, arrowLength);
CGPoint pt3 = PointAtBearingFromPoint(cgPoint, bearing-arrowAngle/2, arrowLength);
//Draw triangle and fill
CGContextBeginPath(context);
CGContextMoveToPoint(context, cgPoint.x, cgPoint.y); //go to tip of triangle
CGContextAddLineToPoint(context, pt2.x, pt2.y);
CGContextAddLineToPoint(context, pt3.x, pt3.y);
CGContextClosePath(context);
CGContextFillPath(context);
}
prevMapPoint = mapPoint;
(also in the drawMapRect:zoomScale:inContext: method is a For loop to decide where to put each arrow and populate the mapPoints array, but I'm assuming that isn't the problem).
Why is my SKPhysicsBody positioned way off from the SKSpriteNode that it is attached to? This is how I create the physics body:
self.pin = [SKSpriteNode spriteNodeWithImageNamed:[IPGameManager sharedGameData].world.player.ship.type];
self.pin.position = CGPointMake([IPGameManager sharedGameData].world.player.location.xCoordinate, [IPGameManager sharedGameData].world.player.location.yCoordinate);
self.pin.userInteractionEnabled = NO;
NSLog(#"Pin width: %f", self.pin.size.width);
NSLog(#"Pin position: %f %f", self.pin.position.x, self.pin.position.y);
self.pin.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:self.pin.size.width * 0.5 center:self.pin.position];
self.pin.physicsBody.dynamic = YES;
self.pin.physicsBody.allowsRotation = NO;
self.pin.physicsBody.friction = 0.0;
self.pin.physicsBody.linearDamping = 0.0;
By setting skView.showsPhysics = YES;, it shows the physics bodies and the circle is WAY off the SKSpriteNode. Any idea why?
You are setting the center position of the physicsBody to the be the CGPoint value of self.pin.position, which is a point in self.pin's parent node's coordinate space. If you are looking to have the physics body simply be centered in self.pin, then you would either use a 0,0 position for center (or not use the method with center at all), or an offset point if you have moved the anchor position of self.pin:
[SKPhysicsBody bodyWithCircleOfRadius:self.pin.size.width * 0.5 center:CGPointZero];
//OR
[SKPhysicsBody bodyWithCircleOfRadius:self.pin.size.width * 0.5 ]
Hello dear developers,
Im using xamarin (monotouch) i want to draw circle image view like google plus profile image or like otherones...
I was search on net but didnt find useful thing.
Somebody help me?
Thank you..
For your purposes you can use UIView or UIButton. With UIButton it is easier to handle touch events.
The basic idea is to create a UIButton with specific coordinates and size and set the CornerRadius property to be one half of the size of the UIButton (assuming you want to draw a circle, width and height will be the same).
Your code could look something like this (in ViewDidLoad of your UIViewController):
// define coordinates and size of the circular view
float x = 50;
float y = 50;
float width = 200;
float height = width;
// corner radius needs to be one half of the size of the view
float cornerRadius = width / 2;
RectangleF frame = new RectangleF(x, y, width, height);
// initialize button
UIButton circularView = new UIButton(frame);
// set corner radius
circularView.Layer.CornerRadius = cornerRadius;
// set background color, border color and width to see the circular view
circularView.BackgroundColor = UIColor.White;
circularView.Layer.CornerRadius = cornerRadius;
circularView.Layer.BorderColor = UIColor.Red.CGColor;
circularView.Layer.BorderWidth = 5;
// handle touch up inside event of the button
circularView.TouchUpInside += HandleCircularViewTouchUpInside;
// add button to view controller
this.View.Add(circularView);
At last implement the event handler (define this method somewhere in your UIViewController:
private void HandleCircularViewTouchUpInside(object sender, EventArgs e)
{
// initialize random
Random rand = new Random(DateTime.Now.Millisecond);
// when the user 'clicks' on the circular view, randomly change the border color of the view
(sender as UIButton).Layer.BorderColor = UIColor.FromRGB(rand.Next(255), rand.Next(255), rand.Next(255)).CGColor;
}
Xamarin.iOS is a wrapper over Cocoa Touch on UI side,
https://developer.apple.com/technologies/ios/cocoa-touch.html
So to draw circle you need to use the Cocoa Touch API, aka CoreGraphics,
http://docs.xamarin.com/videos/ios/getting-started-coregraphics/
I need a radial gradient in the shape of an oval or ellipse and it seems like it CGContextDrawRadialGradient can only draw a perfect circle. I've been drawing to a square context then copying/drawing into a rectangular context.
Any better way to do this?
Thanks!
The only way I've found to do this is as Mark F suggested, but I think the answer needs an example to be easier to understand.
Draw an elliptical gradient in a view in iOS (and using ARC):
- (void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
// Create gradient
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = {0.0, 1.0};
UIColor *centerColor = [UIColor orangeColor];
UIColor *edgeColor = [UIColor purpleColor];
NSArray *colors = [NSArray arrayWithObjects:(__bridge id)centerColor.CGColor, (__bridge id)edgeColor.CGColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations);
// Scaling transformation and keeping track of the inverse
CGAffineTransform scaleT = CGAffineTransformMakeScale(2, 1.0);
CGAffineTransform invScaleT = CGAffineTransformInvert(scaleT);
// Extract the Sx and Sy elements from the inverse matrix
// (See the Quartz documentation for the math behind the matrices)
CGPoint invS = CGPointMake(invScaleT.a, invScaleT.d);
// Transform center and radius of gradient with the inverse
CGPoint center = CGPointMake((self.bounds.size.width / 2) * invS.x, (self.bounds.size.height / 2) * invS.y);
CGFloat radius = (self.bounds.size.width / 2) * invS.x;
// Draw the gradient with the scale transform on the context
CGContextScaleCTM(ctx, scaleT.a, scaleT.d);
CGContextDrawRadialGradient(ctx, gradient, center, 0, center, radius, kCGGradientDrawsBeforeStartLocation);
// Reset the context
CGContextScaleCTM(ctx, invS.x, invS.y);
// Continue to draw whatever else ...
// Clean up the memory used by Quartz
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
}
Put in a view with a black background you get:
You can change the transform of the context to draw an ellipse (for example, apply CGContextScaleCTM(context, 2.0, 1.0) just before calling CGContextDrawRadialGradient () to draw an elliptical gradient that's twice as wide as it is high). Just remember to apply the inverse transform to your start and end points, though.
This question already has answers here:
How do I make UILabel display outlined text?
(15 answers)
Closed 8 years ago.
I was wondering how can I create text stroke for UILabel ? is there any possible way ?
thank you ,
#import <Foundation/Foundation.h>
#interface CustomLabel : UILabel {
}
#end
#import "CustomLabel.h"
#implementation CustomLabel
- (void)drawTextInRect:(CGRect)rect {
CGSize shadowOffset = self.shadowOffset;
UIColor *textColor = self.textColor;
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(c, 22);
CGContextSetTextDrawingMode(c, kCGTextStroke);
self.textColor = [UIColor whiteColor];
[super drawTextInRect:rect];
CGContextSetTextDrawingMode(c, kCGTextFill);
self.textColor = textColor;
self.shadowOffset = CGSizeMake(0, 0);
[super drawTextInRect:rect];
self.shadowOffset = shadowOffset;
//works fine with no warning
}
now the question is how can i use this subclass with a IBOutlet label on different viewcontrollers . is it right :
label = [[CustomLabel alloc] initWithFrame:CGRectMake(0, 0, 190, 190)];
It may be helpful to some to add that depending on font and characters, adding:
CGContextSetLineJoin(c,kCGLineJoinRound);
can prevent artifacts from too large a stroke applied to too sharp a character.
There is one issue with this implementation. Drawing a text with stroke has a slightly different character glyph width than drawing a text without stroke, which can produce "uncentered" results. You can fix that by adding an invisible stroke around the fill text.
You should replace:
CGContextSetTextDrawingMode(c, kCGTextFill);
self.textColor = textColor;
self.shadowOffset = CGSizeMake(0, 0);
[super drawTextInRect:rect];
with:
CGContextSetTextDrawingMode(context, kCGTextFillStroke);
self.textColor = textColor;
[[UIColor clearColor] setStroke]; // invisible stroke
self.shadowOffset = CGSizeMake(0, 0);
[super drawTextInRect:rect];
I'm not 100% sure, if that's the real deal, because I don't know if self.textColor = textColor; has the same effect as [textColor setFill], but you get the idea.
Disclosure: I'm the developer of THLabel.
I've released a UILabel subclass a while ago, which allows an outline in text and other effects. You can find it here: https://github.com/tobihagemann/THLabel