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
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).
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
I'm trying to get the amount of lines in a TextView. I've searched here for a solution, basically every thread has the same answer : contentheight/fontlineheight.
I have a TextView with 8 lines, i run this code and i get contsize : 1.944413
NSLog(#"contsize : %f", descLabel.contentSize.height/descLabel.font.lineHeight);
What am i doing wrong?
For iOS7, a version that take care that you can have differents font (and font size) in your UITextView :
- (NSUInteger)numberOfLinesInTextView:(UITextView *)textView
{
NSLayoutManager *layoutManager = [textView layoutManager];
NSUInteger index, numberOfLines;
NSRange glyphRange = [layoutManager glyphRangeForTextContainer:[textView textContainer]];
NSRange lineRange;
for (numberOfLines = 0, index = glyphRange.location; index < glyphRange.length; numberOfLines++){
(void) [layoutManager lineFragmentRectForGlyphAtIndex:index
effectiveRange:&lineRange];
index = NSMaxRange(lineRange);
}
return numberOfLines;
}
so I have a [NSMutableArray *paths] that contains UIBezierPaths that correspond to each separate path drawn on the screen. I also have another NSMutable array colors that contains the corresponding color for the path. The problem that I am having is that although the two arrays are mutating and I can see the count of both going up, the following drawRect method only seems to draw the last path in the array.
I have scoured through a lot of questions and based on what I have read so far, this very problem seems to get us newbies and mostly because of type mismatch. I even tried to Typecast the return and it was futile.
Here is my code from the drawRect
int cnt = paths.count;
if (cnt != 0)
{
for(int i = 0; i < cnt; i++)
{
NSLog(#"Drawing path:%i", i);
UIColor *color;
color = [colors objectAtIndex:i];
[color setStroke];
UIBezierPath *path = [paths objectAtIndex:i];
path.lineWidth = 2;
[path stroke];
}
}
every time I call [self setNeedsDisplay] it wipes out my entire screen.
Am I correct in assuming that I am not handling my
UIBezierPath *path = [paths objectAtIndex:i];
well?
Thank you very much and in advance.
UPDATE:
I was rummaging through some more codes and now it almost looks like there is another possibility that ARC is releasing the contents of my NSMutable array. I tried the do the following and got a compiler error.
[paths retain];
Besure you add your path to your path array afer each draw
- (void)drawRect:(CGRect)rect {
[brushPattern setStroke];
UIBezierPath *drawPath = [UIBezierPath bezierPath];
drawPath.lineCapStyle = kCGLineCapRound;
drawPath.miterLimit = 0;
drawPath.lineWidth = thisLineWidth;
for (UIBezierPath *path in pathArray)
[drawPath appendPath:path];
UIBezierPath *path = [self pathForCurrentLine];
if (path)
[drawPath appendPath:path];
[drawPath stroke];
}
- (UIBezierPath*)pathForCurrentLine {
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:startPoint];
[path addLineToPoint:endPoint];
return path;
}
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 11 years ago.
I created CCSprite and add it to the main view.
Then I want to see the sprite image, so I saved it to PNG
But lots of them doesn't saved correctly.
Only show white background.
I can't know the reason, and also found some articles via Google, but they didn't help me at all.
How can I solve this?
CCSprite *sprite = [[CCSprite spriteWithFile:[NSString stringWithUTF8String:name] rect:CGRectMake(startx, starty, w, h)] retain];
float drawX = x, drawY = y;
CGSize size = [sprite contentSize];
int nWidth = size.width;
int nHeight = size.height;
nWidth *= scale;
nHeight *= scale;
drawX = drawX + nWidth/2;
drawY = drawY - nHeight/2;
ConvertCoordf(&drawX, &drawY);
drawY -= nHeight;
[sprite setScale:scale];
[sprite setPosition:ccp(drawX, drawY)];
[_mainLayer addChild:sprite];
CCRenderTexture* render = [CCRenderTexture renderTextureWithWidth:sprite.contentSize.width height:sprite.contentSize.height];
[render begin];
[sprite visit];
[render end];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pngPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"new%d.png",i]];
i++;
[render saveBuffer:pngPath format:kCCImageFormatPNG];
[sprite release];