Draw text outside of CGRect. - ios

I am using Apple's Sample Code TheElements for this question. How would I go about drawing text outside of the elementSymbolRectangle. For example I would like to display the Elements Name, but I need it to be outside of the elementSymbolRectangle. I am new to programming and would appreciate any help.
Thanks
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setAutoresizesSubviews:YES];
[self setupUserInterface];
// set the background color of the view to clearn
self.backgroundColor=[UIColor clearColor];
}
return self;
}
- (void)jumpToWikipedia:(id)sender {
// create the string that points to the correct Wikipedia page for the element name
NSString *wikiPageString = [NSString stringWithFormat:#"http://en.wikipedia.org/wiki/%#", self.element.name];
if (![[UIApplication sharedApplication] openURL:[NSURL URLWithString:wikiPageString]])
{
// there was an error trying to open the URL. for the moment we'll simply ignore it.
}
}
- (void)drawRect:(CGRect)rect {
// get the background image for the state of the element
// position it appropriately and draw the image
//
UIImage *backgroundImage = [self.element stateImageForAtomicElementView];
CGRect elementSymbolRectangle = CGRectMake(0, 0, [backgroundImage size].width, [backgroundImage size].height);
[backgroundImage drawInRect:elementSymbolRectangle];
// all the text is drawn in white
[[UIColor whiteColor] set];
// draw the element number
UIFont *font = [UIFont boldSystemFontOfSize:32];
CGPoint point = CGPointMake(10,5);
[[NSString stringWithFormat:#"%#", self.element.atomicNumber] drawAtPoint:point withFont:font];
// draw the element symbol
CGSize stringSize = [self.element.symbol sizeWithFont:font];
point = CGPointMake((self.bounds.size.width-stringSize.width-10),5);
[self.element.symbol drawAtPoint:point withFont:font];

This solved my problem.
UILabel *scoreLabel = [ [UILabel alloc ] initWithFrame:CGRectMake((self.bounds.size.width / 2), -50.0, 100.0, 100.0) ];
scoreLabel.textAlignment = UITextAlignmentCenter;
scoreLabel.textColor = [UIColor whiteColor];
scoreLabel.backgroundColor = [UIColor blackColor];
scoreLabel.font = [UIFont fontWithName:#"Arial Rounded MT Bold" size:(36.0)];
[self addSubview:scoreLabel];
scoreLabel.text = [NSString stringWithFormat: #"Test"];

Related

NSTextAttachment not showing

I am unable to get the NSTextAttachment to show for the NSAttributedString returned in the function below.
- (NSAttributedString *) tokenString:(NSUInteger) tokens
{
CGFloat size = 20;
NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
attachment.bounds = CGRectMake(0,0,size,size)
attachment.image = [[UIImage imageNamed:#"ContestTokenSmall"] imageScaledToSize:CGSizeMake(size, size)];
NSAttributedString *attachmentString = [NSAttributedString attributedStringWithAttachment:attachment];
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:#" %ld", (long)tokens] attributes:[self attributedStringAttributes]];
[str insertAttributedString:attachmentString atIndex:0];
return str;
}
The label I'm using is as follows:
- (UILabel *) tokenLabel
{
if (_tokenLabel == nil)
{
_tokenLabel = [[UILabel alloc] init];
_tokenLabel.translatesAutoresizingMaskIntoConstraints = NO;
_tokenLabel.font = [UIFont zbd_boldFontWithSize:13.0];
_tokenLabel.textColor = UIColor.whiteColor;
[_tokenLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[_tokenLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[_tokenLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[_tokenLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
}
return _tokenLabel;
}
I've tried removing the NSTextAttachment bounds, changing/removing image resizing, and using a different image but nothing I do seems to make an image appear in the label.
Is there something wrong in the code above? Is there a property I need to set in the label to make the image show?
Thanks in advance!
EDIT
How I set the label:
self.tokenLabel.attributedText = [self tokenString:award.topBid];
Log of string:
2018-04-24 16:03:14.579429-0500 TestGame1[44243:1597097] {
NSAttachment = "<NSTextAttachment: 0x6040002c2e60>";
} 10{
NSColor = "UIExtendedGrayColorSpace 1 1";
NSFont = "<UICTFont: 0x7ff0df85ff10> font-family: \"Domus\"; font-weight: bold; font-style: normal; font-size: 13.00pt";
}
Also, I see an empty space where the image should be.
UPDATE 2
The image created with the code below will show in the attributed string, but still none of the pdf or png images loaded from Media.xcassets will show.
CGRect rect = CGRectMake(0, 0, 1, 1);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor.redColor CGColor]);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
I figured it out. The problem was with [UIImage imageNamed:#"ContestTokenSmall"]. Since the image was located in the framework the this code is in, I needed to use
+ (nullable UIImage *)imageNamed:(NSString *)name inBundle:(nullable NSBundle *)bundle compatibleWithTraitCollection:(nullable UITraitCollection *)traitCollection NS_AVAILABLE_IOS(8_0);
so that it would get the image from the correct bundle. I was getting the image from the framework's bundle while debugging and that is why the image would show in image views while not showing in the attributed string.

How to disable Customized UIButton

I subclassed UIButton and did some customized drawing in drawRect method such as drawing NSAttributedString and UIImage.
However, after I did this, the customized UIButton doesn't gray out when enabled is set to NO. I think my customized drawing happens on top of its state. How do I deal with this?
Sharing my drawing code here:
- (void)drawRect:(CGRect)rect
{
// Drawing code
if (self.faceUp) {
[self drawCardText:self.card.contents inRect:self.bounds];
} else {
[self drawCardImage:[UIImage imageNamed:CardBackImageName] inRect:self.bounds];
}
}
- (void)drawCardText:(NSString *)text inRect:(CGRect)rect
{
// set background color to white so text can be shown
[[UIColor whiteColor] setFill];
[[UIBezierPath bezierPathWithRect:rect] fill];
UIFont *preferedFont = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
UIFont *actualFont = [UIFont fontWithName:preferedFont.fontName
size:hypotf(rect.size.width, rect.size.height) / 6.0];
NSDictionary *attributes = #{NSFontAttributeName: actualFont};
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:attributes];
[self setAttributedTitle:attributedText forState:UIControlStateNormal];
}
- (void)drawCardImage:(UIImage *)image inRect:(CGRect)rect
{
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 1.0f);
[image drawInRect:rect];
UIImage *actualImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[[UIColor colorWithPatternImage:actualImage] setFill];
[[UIBezierPath bezierPathWithRect:rect] fill];
}
You might have to add an overlay view with grey color and some alpha yourself. Remove the overlay when button is enabled again.
You can add this method to CustomButton class.
-(void)setEnabled:(BOOL)enabled{
//disableLayer.hidden = !enabled;
if (enabled) {
//self.enabled = YES;
self.alpha = 1.0;
}else{
//self.enabled = NO;
self.alpha = 0.7;
}
[super setEnabled:enabled];
}
For enable or disable call-
[customButtob setEnabled:buttonStatus];
If you want to change color, add a background layer, and toggle its hidden property in setEnabled method.
disableLayer = [CALayer layer];
disableLayer.backgroundColor = [UIColor colorWithRed:20/255.0f green:20/255.0f blue:20/255.0f alpha:1.0f].CGColor;
disableLayer.frame = self.layer.bounds;
disableLayer.hidden = YES;
[self.layer insertSublayer:disableLayer below:otherLayer];
I think you share some more details of your implementation. Moreover I am sure you have called
Super methods in the customized methods at first call.
You have to override the setEnabled: method of your CustomButton
-(void)setEnabled:(BOOL)enabled
{
if (!enabled) {
[self setAlpha:0.2f];
}
}
OR
If you have to change the color of UIButton in its disabled state. Yo can change drawRect: method as
Make a assign property of BOOL type variable (say isEnabled)
-(void)drawRect:(CGRect)rect
{
[[UIColor redColor] setFill];
UIRectFill(rect);
if (!self.isEnabled) {
[[UIColor blackColor] setFill];
UIRectFill(rect);
}
}
-(void)setEnabled:(BOOL)enabled
{
self.isEnabled = enabled;
[self setNeedsDisplay];
}
You can use
btn.userInteractionEnabled = NO;
Or you can use
btn.enabled = NO;
Hope this helps... :)
Edit:
I think you can use this
[btn setTitleColor:[UIColor grayColor] forState:UIControlStateDisabled];
Add this:
-(void)setEnabled:(BOOL)enabled{
if (!enabled) {
self.alpha = 0.3;
}else{
self.alpha = 1.0;
}
[super setEnabled:enabled];
}
add a method which can help u for enable and disable of your button just add this method in custom button class
//in customButton.h file
#import <UIKit/UIKit.h>
#interface CustomButton : UIButton
#property (nonatomic,assign) BOOL faceUp;//your property
- (void)makeButtonDisable:(BOOL)disable;//method to handle disable and enable
#end
//in customButton.m file
#import "CustomButton.h"
#implementation CustomButton
{
CALayer *grayLayer;//layer to handle disable and enable
}
#synthesize faceUp;
// in initilization method
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self)
{
grayLayer = [CALayer layer];
grayLayer.frame = frame;
[self.layer addSublayer:grayLayer]; //add the grayLayer during initialisation
}
return self;
}
//your code that put above
- (void)drawRect:(CGRect)rect
{
// Drawing code
if (self.faceUp) {
[self drawCardText:self.card.contents inRect:self.bounds];
} else {
[self drawCardImage:[UIImage imageNamed:CardBackImageName] inRect:self.bounds];
}
}
- (void)drawCardText:(NSString *)text inRect:(CGRect)rect
{
// set background color to white so text can be shown
[[UIColor whiteColor] setFill];
[[UIBezierPath bezierPathWithRect:rect] fill];
UIFont *preferedFont = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
UIFont *actualFont = [UIFont fontWithName:preferedFont.fontName
size:hypotf(rect.size.width, rect.size.height) / 6.0];
NSDictionary *attributes = #{NSFontAttributeName: actualFont};
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:attributes];
[self setAttributedTitle:attributedText forState:UIControlStateNormal];
}
- (void)drawCardImage:(UIImage *)image inRect:(CGRect)rect
{
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 1.0f);
[image drawInRect:rect];
UIImage *actualImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[[UIColor colorWithPatternImage:actualImage] setFill];
[[UIBezierPath bezierPathWithRect:rect] fill];
}
//add this method
- (void)makeButtonDisable:(BOOL)disable
{
if(disable)
{
grayLayer.backgroundColor = [UIColor colorWithRed:201.0f/255.0f green:201.0f/255.0f blue:201.0f/255.0f alpha:5.0f].CGColor;
self.userInteractionEnabled = NO;
grayLayer.opacity = 0.5f;
self.alpha = 0.5f;
}
else
{
grayLayer.backgroundColor = [UIColor clearColor].CGColor;
self.userInteractionEnabled = YES;
grayLayer.opacity = 0.0f;
self.alpha = 1.0f;
}
}
in the controller where u are using this button
//some where in the controller u want to disable the button just do like this
//to disable
[button makeButtonDisable:YES]; //button is an instance of customCell
//to enable
[button makeButtonDisable:NO]; //button is an instance of customCell

Draw a line between two points - How to pass UIView

I am trying to draw a simple line connecting two points. I think i got the right code for that in my drawLine method. No lines appear at all and I am pretty sure it has to do with the View I am calling with and the one I want to draw on. The following code is where I build my next View in code (data coming from a web service). The View gets created just fine and all buttons and objects appear.
I am trying to add lines to the View I'm creating (Next) dynamically and not sure how to get that done. From the Errors I get (invalid context 0x0) it seems I am doing something out of order. Trying to display on a view that is not there. how than can I get this done? Any advice would be appreciated.
I realize that passing on the View might not be the right answer. I am in the midst of trial and error here.
To clarify: I am calling drawLine here:
[nextView.view addSubview:next];
Thanks,
Shai
-(void) drawView
{
CGRect rec;
CGFloat width = [UIScreen mainScreen].bounds.size.width;
CGFloat height = [UIScreen mainScreen].bounds.size.height;
UIView *next = [[UIView alloc]initWithFrame:CGRectMake(0, 0, height, width)];
next.backgroundColor = [UIColor whiteColor];
next.userInteractionEnabled = YES;
next.multipleTouchEnabled = YES;
nextView = [UIViewController alloc];
[nextView setTitle: as_objectDesc];
for (object in viewObjects)
{ // ratio based on Ipad 2 screen size conversion from the Datawindow size - alter to support other device
CGFloat objWidth = object.Width/3.83;
CGFloat objHeight = object.Height/2.78;
CGFloat objXPoint = object.positionX/3.83;
CGFloat objYPoint = object.positionY/2.78;
if ([object.Shape isEqualToString:#"text"])
{
rec = CGRectMake(objXPoint, objYPoint, objWidth, objHeight);
UILabel *myLabel = [[UILabel alloc] initWithFrame:rec];
myLabel.backgroundColor = [UIColor clearColor];
myLabel.text = object.objectTag;
myLabel.lineBreakMode = NSLineBreakByWordWrapping;
if ([object.description isEqualToString:#"Header"])
{
myLabel.font = [UIFont systemFontOfSize:20];
}
else
{
myLabel.font = [UIFont systemFontOfSize:10];
}
[next addSubview:myLabel];
}
else if ([object.Shape isEqualToString:#"line"])
{
**[self drawLine:nextView.view];**
}
else
{
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeCustom];
if ([object.Shape isEqualToString:#"ellipse"])
{
UIImage * buttonImage;
if ([object.Color isEqualToString:#"255"])
{
buttonImage = [UIImage imageNamed:#"redBackground.png"];
}
else if ([object.Color isEqualToString:#"65535"])
{
buttonImage = [UIImage imageNamed:#"yellowBackground.png"];
}
else if ([object.Color isEqualToString:#"GRAY"])
{
buttonImage = [UIImage imageNamed:#"graybground.PNG"];
}
else if ([object.Color isEqualToString:#"BLACK"])
{
buttonImage = [UIImage imageNamed:#"BlackBackground.png"];
}
[myButton setBackgroundImage:buttonImage forState: UIControlStateNormal];
objWidth = objHeight;
rec = CGRectMake(objXPoint, objYPoint, objWidth, objHeight);
myButton.frame = rec;
myButton.clipsToBounds = YES;
myButton.layer.cornerRadius = objWidth/2;
}
else
{
rec = CGRectMake(objXPoint, objYPoint, objWidth, objHeight);
if ([object.Color isEqualToString:#"255"])
myButton.backgroundColor = [UIColor redColor];
else if ([object.Color isEqualToString:#"65535"])
myButton.backgroundColor = [UIColor yellowColor];
else if ([object.Color isEqualToString:#"GRAY"])
myButton.backgroundColor = [UIColor grayColor];
else if ([object.Color isEqualToString:#"BLACK"])
myButton.backgroundColor = [UIColor blackColor];
}
[[myButton layer] setBorderWidth:1.0f];
[[myButton layer] setBorderColor:[UIColor blackColor].CGColor];
myButton.enabled = YES;
myButton.accessibilityHint = object.objectTag;
myButton.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
myButton.titleLabel.textAlignment = NSTextAlignmentCenter;
myButton.titleLabel.font = [UIFont systemFontOfSize:13];
[myButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[myButton addTarget:self action:#selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
myButton.frame = rec;
[myButton setTitle:object.description forState:UIControlStateNormal];
// Check if an Empty Rectangle
NSRange test =[myButton.accessibilityHint rangeOfString:#"r_"];
if (test.location == 0)
{
[next sendSubviewToBack:myButton];
[myButton setEnabled:NO];
}
[next addSubview:myButton];
}
UIPinchGestureRecognizer *twoFingerPinch = [[UIPinchGestureRecognizer alloc]
initWithTarget:self
action:#selector(twoFingerPinch:)]
;
[[nextView view] addGestureRecognizer:twoFingerPinch];
[next addSubview:activityIndicator];
[next bringSubviewToFront:activityIndicator];
}
[nextView.view addSubview:next];
[self dismissViewControllerAnimated:NO completion:nil];
[self.navigationController pushViewController:nextView animated:NO];
}
- (void)drawLine:(UIView *) view
{
CGFloat x1 = object.positionX;
CGFloat y1 = object.positionY;
CGFloat x2 = object.positionX2;
CGFloat y2 = object.positionY2;
CGContextRef context = UIGraphicsGetCurrentContext();
if ([object.Color isEqualToString:#"255"])
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
else if ([object.Color isEqualToString:#"65535"])
CGContextSetStrokeColorWithColor(context, [UIColor yellowColor].CGColor);
else if ([object.Color isEqualToString:#"GRAY"])
CGContextSetStrokeColorWithColor(context, [UIColor grayColor].CGColor);
else if ([object.Color isEqualToString:#"BLACK"])
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
CGContextSetLineWidth(context, 3.0);
CGContextBeginPath(context);
CGContextMoveToPoint(context, x1, y1);
CGContextAddLineToPoint(context, x2, y2);
CGContextStrokePath(context);
}
I end up figuring this up days a go and thought I would share here. I might have added to much code in my question which did not help to simplify it to others (no comments).
What this came down to is when trying to Draw a line you need to override the DrawRect Method of the UIView subclass and not the UIViewController as I did. There is no Error thrown if you call DrawRect from your UIViewController but it doesn't do anything. I missed that small detail.
What added to the complexity of finding this out ans solving this was that I was creating all of it Programmatically and initially didn't subclass UIView at all. I ended up adding all the Points in an NSMutablearray and redrawing the lines in the following class after the newly View got created:
#import "draw.h"
#implementation draw
#synthesize lines;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// Initialization code
}
return self;
}
-(void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
for (line *myLine in lines)
{
if ([myLine.color isEqualToString:#"255"])
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
else if ([myLine.color isEqualToString:#"65535"])
CGContextSetStrokeColorWithColor(context, [UIColor yellowColor].CGColor);
else if ([myLine.color isEqualToString:#"GRAY"])
CGContextSetStrokeColorWithColor(context, [UIColor grayColor].CGColor);
else if ([myLine.color isEqualToString:#"BLACK"])
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
int setNew = myLine.Width/4;
CGContextSetLineWidth(context, (CGFloat)setNew);
CGContextBeginPath(context);
CGContextMoveToPoint(context, myLine.x1, myLine.y1);
CGContextAddLineToPoint(context, myLine.x2, myLine.y2);
CGContextStrokePath(context);
}
}
#end

CATiledLayer subview causing memory warning and crash [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
Alright, here's what's happening... I have a UIView subclass, which includes two points (start and end), a CGPath between those two points and lot of UILabels. If I add that UIView subclass as a subview to my scrollview (over top of my CATiledLayer based UIView) then my app starts getting memory warnings and eventually crashes. However, if I remove the UIView subclass that contains the points and path and leave the CATiledLayer view as it is, everything functions perfectly and no crashing or memory warnings occur.
Does anyone have any ideas on why this is occurring? The content view shouldn't have any problems being so big should it since I'm only draw labels that are at max 40px wide and a CGPath, which is also pretty small?
This is a mapping application
Here's some code:
//Display the map image
- (void)displayTiledImageNamed:(NSString *)imageName size:(CGSize)imageSize {
self.zoomScale = 1.0;
container = [[UIView alloc] initWithFrame:(CGRect){ CGPointZero, imageSize }];
_zoomView = [[UIImageView alloc] initWithFrame:(CGRect){ CGPointZero, imageSize }];
NSString *path = [NSString stringWithFormat:#"%#/%#-Small.png",[[NSBundle mainBundle] bundlePath], [imageName stringByDeletingPathExtension]];
UIImage *zoomImage = [UIImage imageWithContentsOfFile:path];
[_zoomView setImage:zoomImage];
[container addSubview:_zoomView];
[_zoomView release];
_tilingView = [[UMTileView alloc] initWithImageName:imageName size:imageSize];
_tilingView.frame = _zoomView.bounds;
[_zoomView addSubview:_tilingView];
[_tilingView release];
[self configureForImageSize:imageSize];
//This is the view that's causing the crash and memory warnings
//If this view is commented out the app functions just fine
UMMapContentView *m = [[UMMapContentView alloc] initWithFrame:CGRectMake(0,0,imageSize.width,imageSize.height) andColors:self.colors mapView:self.mapView];
self.contentView = m;
[container addSubview:self.contentView];
[m release];
[self addSubview:container];
[container release];
}
This my path view class which is another subview inside the UMMapContentView view above:
+ (Class)layerClass {
return [CATiledLayer class];
}
+ (CFTimeInterval)fadeDuration {
return 0.0;
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.userInteractionEnabled = NO;
self.backgroundColor = [UIColor clearColor];
}
return self;
}
- (void)dealloc {
self.path = nil;
self.pathColor = nil;
[super dealloc];
}
- (void)resetPathView {
CATiledLayer *tiledLayer = (CATiledLayer*)self.layer;
tiledLayer.contents = nil;
[tiledLayer setNeedsDisplay];
}
- (void)drawPath:(UMPath*)p {
self.path = p;
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
if (self.path) {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, self.pathColor.CGColor);
CGContextSetShadowWithColor(context, CGSizeMake(2.0, 1.0), 1.0, [[UIColor colorWithWhite:0.2 alpha:0.8] CGColor]);
CGContextSetLineWidth(context, 8.0);
CGContextBeginPath (context);
CGContextMoveToPoint (context, ((UMMapPoint*)[self.path.points objectAtIndex: 0]).x, ((UMMapPoint*)[self.path.points objectAtIndex: 0]).y);
for (int i = 1; i < self.path.points.count; i++) {
CGContextAddQuadCurveToPoint (context, ((UMMapPoint*)[self.path.points objectAtIndex:i-1]).x, ((UMMapPoint*)[self.path.points objectAtIndex: i-1]).y, ((UMMapPoint*)[self.path.points objectAtIndex:i]).x, ((UMMapPoint*)[self.path.points objectAtIndex:i]).y);
}
CGContextStrokePath (context);
}
}
And this is my method that places all the UILabels in the UMMapContentView view:
- (void)mapAllLabelsInNavigationMap:(int)navigationMap {
//There are 1109 label dictionaries in the labels array
for (NSDictionary *dict in labels) {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake([[dict objectForKey:#"X"] floatValue],[[dict objectForKey:#"Y"] floatValue],[[dict objectForKey:#"Width"] floatValue],[[dict objectForKey:#"Height"] floatValue])];
[label setLineBreakMode:NSLineBreakByWordWrapping];
[label setNumberOfLines:0];
[label setBackgroundColor:[UIColor clearColor]];
[label setTextColor:[self colorFromHexString:[dict objectForKey:#"Foreground"]]];
[label setFont:[UIFont systemFontOfSize:[[dict objectForKey:#"FontSize"] floatValue]]];
[label setTextAlignment:NSTextAlignmentCenter];
[label setText:[[UMDataBase shared] stringForID:[[dict objectForKey:#"Texts_ID"] intValue] withLanguage:languageID]];
[self addSubview:label];
[localMapLabels addObject:label];
[label release];
}
}
I'm not really sure what direction I need to head... Any help would be much appreciated! :)
You did not give the source code of UMMapContentView, but if it uses CATiledLayer, then that may be the problem. It seems that you cannot subview a CATiledLayer-backed view.
Scroll to the bottom (in the comments section): http://red-glasses.com/index.php/tutorials/catiledlayer-how-to-use-it-how-it-works-what-it-does/
It may be easier if you write your own tiled layer anyhow. Documentation on CATiledLayer is sparse while the bugs are aplenty.

Underline text in UIlabel

How can I underline a text that could be multiple lines of string?
I find some people suggest UIWebView, but it is obviously too heavy a class for just text rendering.
My thoughts was to figure out the start point and length of each string in each line.
And draw a line under it accordingly.
I meet problems at how to figure out the length and start point for the string.
I tried to use -[UILabel textRectForBounds:limitedToNumberOfLines:], this should be the drawing bounding rect for the text right?
Then I have to work on the alignment?
How can I get the start point of each line when it is center-justified and right justified?
You may subclass from UILabel and override drawRect method:
- (void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(ctx, 207.0f/255.0f, 91.0f/255.0f, 44.0f/255.0f, 1.0f); // RGBA
CGContextSetLineWidth(ctx, 1.0f);
CGContextMoveToPoint(ctx, 0, self.bounds.size.height - 1);
CGContextAddLineToPoint(ctx, self.bounds.size.width, self.bounds.size.height - 1);
CGContextStrokePath(ctx);
[super drawRect:rect];
}
UPD:
As of iOS 6 Apple added NSAttributedString support for UILabel, so now it's much easier and works for multiple lines:
NSDictionary *underlineAttribute = #{NSUnderlineStyleAttributeName: #(NSUnderlineStyleSingle)};
myLabel.attributedText = [[NSAttributedString alloc] initWithString:#"Test string"
attributes:underlineAttribute];
If you still wish to support iOS 4 and iOS 5, I'd recommend to use TTTAttributedLabel rather than underline label manually. However if you need to underline one-line UILabel and don't want to use third-party components, code above would still do the trick.
In Swift:
let underlineAttriString = NSAttributedString(string: "attriString",
attributes: [NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue])
label.attributedText = underlineAttriString
This is what i did. It works like butter.
1) Add CoreText.framework to your Frameworks.
2) import <CoreText/CoreText.h> in the class where you need underlined label.
3) Write the following code.
NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:#"My Messages"];
[attString addAttribute:(NSString*)kCTUnderlineStyleAttributeName
value:[NSNumber numberWithInt:kCTUnderlineStyleSingle]
range:(NSRange){0,[attString length]}];
self.myMsgLBL.attributedText = attString;
self.myMsgLBL.textColor = [UIColor whiteColor];
Use an attribute string:
NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString:#"Your String"]
[attrString addAttribute:(NSString*)kCTUnderlineStyleAttributeName
value:[NSNumber numberWithInt:kCTUnderlineStyleSingle]
range:(NSRange){0,[attrString length]}];
And then override the label - (void)drawTextInRect:(CGRect)aRect and render the text in something like:
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrString);
drawingRect = self.bounds;
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, drawingRect);
textFrame = CTFramesetterCreateFrame(framesetter,CFRangeMake(0,0), path, NULL);
CGPathRelease(path);
CFRelease(framesetter);
CTFrameDraw(textFrame, ctx);
CGContextRestoreGState(ctx);
Or better yet instead of overriding just use the OHAttributedLabel created by Olivier Halligon
I've combined some of provided answers, to create better (at least for my requirements) UILabel subclass, which supports:
multiline text with various label bounds (text can be in the middle of label frame, or accurate size)
underline
strikeout
underline/strikeout line offset
text alignment
different font sizes
https://github.com/GuntisTreulands/UnderLineLabel
People, who do not want to subclass the view (UILabel/UIButton) etc...
'forgetButton' can be replace by any lable too.
-(void) drawUnderlinedLabel {
NSString *string = [forgetButton titleForState:UIControlStateNormal];
CGSize stringSize = [string sizeWithFont:forgetButton.titleLabel.font];
CGRect buttonFrame = forgetButton.frame;
CGRect labelFrame = CGRectMake(buttonFrame.origin.x + buttonFrame.size.width - stringSize.width,
buttonFrame.origin.y + stringSize.height + 1 ,
stringSize.width, 2);
UILabel *lineLabel = [[UILabel alloc] initWithFrame:labelFrame];
lineLabel.backgroundColor = [UIColor blackColor];
//[forgetButton addSubview:lineLabel];
[self.view addSubview:lineLabel];
}
NSString *tem =self.detailCustomerCRMCaseLabel.text;
if (tem != nil && ![tem isEqualToString:#""]) {
NSMutableAttributedString *temString=[[NSMutableAttributedString alloc]initWithString:tem];
[temString addAttribute:NSUnderlineStyleAttributeName
value:[NSNumber numberWithInt:1]
range:(NSRange){0,[temString length]}];
self.detailCustomerCRMCaseLabel.attributedText = temString;
}
Another solution could be (since iOS 7) given a negative value to NSBaselineOffsetAttributeName, for example your NSAttributedString could be:
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:#"my text goes here'
attributes:#{NSFontAttributeName: [UIFont fontWithName:#"Helvetica-Regular" size:12],
NSForegroundColorAttributeName: [UIColor blackColor],
NSUnderlineStyleAttributeName: #(NSUnderlineStyleSingle), NSBaselineOffsetAttributeName: #(-3)}];
Hope this will help ;-)
NSMutableAttributedString *text = [self.myUILabel.attributedText mutableCopy];
[text addAttribute:NSUnderlineStyleAttributeName value:#(NSUnderlineStyleSingle) range:NSMakeRange(0, text.length)];
self.myUILabel.attributedText = text;
You can create a custom label with name UnderlinedLabel and edit drawRect function.
#import "UnderlinedLabel.h"
#implementation UnderlinedLabel
- (void)drawRect:(CGRect)rect
{
NSString *normalTex = self.text;
NSDictionary *underlineAttribute = #{NSUnderlineStyleAttributeName: #(NSUnderlineStyleSingle)};
self.attributedText = [[NSAttributedString alloc] initWithString:normalTex
attributes:underlineAttribute];
[super drawRect:rect];
}
Here is the easiest solution which works for me without writing additional codes.
// To underline text in UILable
NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:#"Type your text here"];
[text addAttribute:NSUnderlineStyleAttributeName value:#(NSUnderlineStyleSingle) range:NSMakeRange(0, text.length)];
lblText.attributedText = text;
Sometimes we developer stuck in small designing part of any UI screen. One of the most irritating requirement is under line text. Don’t worry here is the solution.
Underlining a text in a UILabel using Objective C
UILabel *label=[[UILabel alloc]initWithFrame:CGRectMake(0, 0, 320, 480)];
label.backgroundColor=[UIColor lightGrayColor];
NSMutableAttributedString *attributedString;
attributedString = [[NSMutableAttributedString alloc] initWithString:#"Apply Underlining"];
[attributedString addAttribute:NSUnderlineStyleAttributeName value:#1 range:NSMakeRange(0,
[attributedString length])];
[label setAttributedText:attributedString];
Underlining a text in UILabel using Swift
label.backgroundColor = .lightGray
let attributedString = NSMutableAttributedString.init(string: "Apply UnderLining")
attributedString.addAttribute(NSUnderlineStyleAttributeName, value: 1, range:
NSRange.init(location: 0, length: attributedString.length))
label.attributedText = attributedString
An enhanced version of the code of Kovpas (color and line size)
#implementation UILabelUnderlined
- (void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
const CGFloat* colors = CGColorGetComponents(self.textColor.CGColor);
CGContextSetRGBStrokeColor(ctx, colors[0], colors[1], colors[2], 1.0); // RGBA
CGContextSetLineWidth(ctx, 1.0f);
CGSize tmpSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(200, 9999)];
CGContextMoveToPoint(ctx, 0, self.bounds.size.height - 1);
CGContextAddLineToPoint(ctx, tmpSize.width, self.bounds.size.height - 1);
CGContextStrokePath(ctx);
[super drawRect:rect];
}
#end
I have Created for multiline uilabel with underline :
For Font size 8 to 13 set int lineHeight = self.font.pointSize+3;
For font size 14 to 20 set int lineHeight = self.font.pointSize+4;
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
const CGFloat* colors = CGColorGetComponents(self.textColor.CGColor);
CGContextSetRGBStrokeColor(ctx, colors[0], colors[1], colors[2], 1.0); // RGBA
CGContextSetLineWidth(ctx, 1.0f);
CGSize tmpSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(self.frame.size.width, 9999)];
int height = tmpSize.height;
int lineHeight = self.font.pointSize+4;
int maxCount = height/lineHeight;
float totalWidth = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(1000, 9999)].width;
for(int i=1;i<=maxCount;i++)
{
float width=0.0;
if((i*self.frame.size.width-totalWidth)<=0)
width = self.frame.size.width;
else
width = self.frame.size.width - (i* self.frame.size.width - totalWidth);
CGContextMoveToPoint(ctx, 0, lineHeight*i-1);
CGContextAddLineToPoint(ctx, width, lineHeight*i-1);
}
CGContextStrokePath(ctx);
[super drawRect:rect];
}
Swift 4.1 ver:
let underlineAttriString = NSAttributedString(string:"attriString", attributes:
[NSAttributedStringKey.underlineStyle: NSUnderlineStyle.styleSingle.rawValue])
label.attributedText = underlineAttriString
As kovpas has shown you can use the bounding box in most cases, although it is not always guaranteed that the bounding box will fit neatly around the text. A box with a height of 50 and font size of 12 may not give the results you want depending on the UILabel configuration.
Query the UIString within the UILabel to determine its exact metrics and use these to better place your underline regardless of the enclosing bounding box or frame using the drawing code already provided by kovpas.
You should also look at UIFont's "leading" property that gives the distance between baselines based on a particular font. The baseline is where you would want your underline to be drawn.
Look up the UIKit additions to NSString:
(CGSize)sizeWithFont:(UIFont *)font
//Returns the size of the string if it were to be rendered with the specified font on a single line.
(CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size
// Returns the size of the string if it were rendered and constrained to the specified size.
(CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode
//Returns the size of the string if it were rendered with the specified constraints.
I use an open source line view and just added it to the button subviews:
UILabel *label = termsButton.titleLabel;
CGRect frame = label.frame;
frame.origin.y += frame.size.height - 1;
frame.size.height = 1;
SSLineView *line = [[SSLineView alloc] initWithFrame:frame];
line.lineColor = [UIColor lightGrayColor];
[termsButton addSubview:line];
This was inspired by Karim above.
Based on Kovpas & Damien Praca's Answers, here is an implementation of UILabelUnderligned which also support textAlignemnt.
#import <UIKit/UIKit.h>
#interface UILabelUnderlined : UILabel
#end
and the implementation:
#import "UILabelUnderlined.h"
#implementation DKUILabel
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
const CGFloat* colors = CGColorGetComponents(self.textColor.CGColor);
CGContextSetRGBStrokeColor(ctx, colors[0], colors[1], colors[2], 1.0); // RGBA
CGContextSetLineWidth(ctx, 1.0f);
CGSize textSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(200, 9999)];
// handle textAlignement
int alignementXOffset = 0;
switch (self.textAlignment) {
case UITextAlignmentLeft:
break;
case UITextAlignmentCenter:
alignementXOffset = (self.frame.size.width - textSize.width)/2;
break;
case UITextAlignmentRight:
alignementXOffset = self.frame.size.width - textSize.width;
break;
}
CGContextMoveToPoint(ctx, alignementXOffset, self.bounds.size.height - 1);
CGContextAddLineToPoint(ctx, alignementXOffset+textSize.width, self.bounds.size.height - 1);
CGContextStrokePath(ctx);
[super drawRect:rect];
}
#end
Here's another, simpler solution (underline's width is not most accurate but it was good enough for me)
I have a UIView (_view_underline) that has White background, height of 1 pixel and I update its width everytime I update the text
// It's a shame you have to do custom stuff to underline text
- (void) underline {
float width = [[_txt_title text] length] * 10.0f;
CGRect prev_frame = [_view_underline frame];
prev_frame.size.width = width;
[_view_underline setFrame:prev_frame];
}
NSUnderlineStyleAttributeName which takes an NSNumber (where 0 is no underline) can be added to an attribute dictionary.
I don't know if this is any easier. But, it was easier for my purposes.
NSDictionary *attributes;
attributes = #{NSFontAttributeName:font, NSParagraphStyleAttributeName: style, NSUnderlineStyleAttributeName:[NSNumber numberWithInteger:1]};
[text drawInRect:CGRectMake(self.contentRect.origin.x, currentY, maximumSize.width, textRect.size.height) withAttributes:attributes];
You can use this my custom label!
You can also use interface builder to set
import UIKit
class YHYAttributedLabel : UILabel{
#IBInspectable
var underlineText : String = ""{
didSet{
self.attributedText = NSAttributedString(string: underlineText,
attributes: [NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue])
}
}
}

Resources