respond to selector method for unsupported implementations - iphone

In my app I need to display under line text in a label so I used following code to display underlined text
NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] initWithString:normalString];
[attributeString addAttribute:NSUnderlineStyleAttributeName
value:[NSNumber numberWithInt:1]
range:(NSRange){0,[attributeString length]}];
wesiteAddressLabel.attributedText = attributeString;
This method and some other implementations which works fine in iOS 6.1
But when I executed in iOS 5.1 and below, app gets crashed due to the reason,
[attributeString addAttribute:NSUnderlineStyleAttributeName
value:[NSNumber numberWithInt:1]
range:(NSRange){0,[attributeString length]}];
not supported in previous versions
So I want to use respondsToSelector: method to check if instance responds and implement another method for unsupported selector.
How I use this method?

As from the documentation:
attributedText The styled text displayed by the label.
#property(nonatomic,copy) NSAttributedString *attributedText
Discussion This property is nil by default. Assigning a new value to
this property also replaces the value of the text property with the
same string data, albeit without any formatting information. In
addition, assigning a new a value updates the values in the font,
textColor, and other style-related properties so that they reflect the
style information starting at location 0 in the attributed string.
Availability Available in iOS 6.0 and later. Declared In UILabel.h
You should check if the a specific UIView element is able to respond to the attributedText. In this case:
[wesiteAddressLabel respondsToSelector:#selector(attributedText)];
Should be enough

For previoes versions you have to draw an UIImageView Just below the Text by getting the with and Height of text in each line.
Or you can create a category of label by using DrawRect method.
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(ctx, 0.0f/255.0f, 0.0f/255.0f, 255.0f/255.0f, 1.0f); // Your underline color
CGContextSetLineWidth(ctx, 1.0f);
UIFont *font = [UIFont systemFontOfSize:16.0f];
CGSize constraintSize = CGSizeMake(MAXFLOAT, MAXFLOAT);
CGSize labelSize;
labelSize = [self.text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
CGContextMoveToPoint(ctx, 0, self.bounds.size.height - 1);
CGContextAddLineToPoint(ctx, labelSize.width + 10, self.bounds.size.height - 1);
CGContextStrokePath(ctx);
[super drawRect:rect];
}

Related

UILabel auto size error on iOS 7

I'm making my app transition to iOS 7 and have this method (already modified for iOS 7, using boundingRectWithSize...):
+ (CGSize)messageSize:(NSString*)message {
NSDictionary *attributes = #{NSFontAttributeName : [UIFont fontWithName:#"Futura-Medium" size:13]};
CGRect frame = [message boundingRectWithSize:CGSizeMake([PTSMessagingCell maxTextWidth], CGFLOAT_MAX) options:NSStringDrawingUsesFontLeading attributes:attributes context:nil];
return frame.size;
}
I am getting this appearance:
The message UILabel is being cut. It feels like line spacing is too big. It tried many other answers I found but none of them work.
If someone knows how to help me, I appreciate! ;)
Thanks!
Try changing NSStringDrawingUsesFontLeading as your option to NSStringDrawingUsesLineFragmentOrigin.
If you were only supporting iOS 6 and iOS 7, then I would definitely change all of your NSString's sizeWithFont:... to the NSAttributeString's boundingRectWithSize. Starting in iOS 6, the NSAttributedString's NSStringDrawing functions were introduced and they're threadsafe unlike the old NSString+UIKit methods we're used to (eg. sizeWithFont:..., etc), which were UIStringDrawing functions (and act unpredictably when you use them from a non-main thread. It'll save you a lot of headache if you happen to have a weird multi-threading corner case! Here's how I converted NSString's sizeWithFont:constrainedToSize::
What used to be:
NSString *text = ...;
CGFloat width = ...;
UIFont *font = ...;
CGSize size = [text sizeWithFont:font
constrainedToSize:(CGSize){width, CGFLOAT_MAX}];
Can be replaced with:
NSString *text = ...;
CGFloat width = ...;
UIFont *font = ...;
NSAttributedString *attributedText =
[[NSAttributedString alloc]
initWithString:text
attributes:#
{
NSFontAttributeName: font
}];
CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX}
options:NSStringDrawingUsesLineFragmentOrigin
context:nil];
CGSize size = rect.size;
Please note the documentation mentions:
In iOS 7 and later, this method returns fractional sizes (in the size
component of the returned CGRect); to use a returned size to size
views, you must use raise its value to the nearest higher integer
using the ceil function.
So to pull out the calculated height or width to be used for sizing views, I would use:
CGFloat height = ceilf(size.height);
CGFloat width = ceilf(size.width);
I think you are updating your label frame from either viewDidLoad or viewWillAppear, so it is not working.
if you will update frame of label from viewDidAppear method then you will get updated frame of label.
I am not sure why this is happened, I think it is iOS 7 bug.
Try this
+ (CGSize)messageSize:(NSString*)message {
CGSize nameSize = [message sizeWithFont:[UIFont fontWithName:#"Futura-Medium" size:13]
constrainedToSize:CGSizeMake(maxWidth, maxHeight) lineBreakMode:NSLineBreakByWordWrapping];
NSLog(#"width = %f, height = %f", nameSize.width, nameSize.height);
return nameSize;
}

iPhone : Underline Text

Is there appeared easy way to underline text in iOS 4 ?? In IB maybe ? Thanks in advance...
As far as I know you cannot underline text.
You can "fake" it though by (for example) placing an UImageView or a regular View just underneath with the same color as your text. You can use strike-through etc, but cannot underline.
Edit:
You could use this approach to underline your UILabel though. You could namely use a custom UILabel. So you could create some class like CUILabel that inherits UILabel and replace its drawRect method in the #implementation section with the following:
- (void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(ctx, 0.0f/255.0f, 0.0f/255.0f, 255.0f/255.0f, 1.0f); // Your underline color
CGContextSetLineWidth(ctx, 1.0f);
UIFont *font = [UIFont systemFontOfSize:16.0f];
CGSize constraintSize = CGSizeMake(MAXFLOAT, MAXFLOAT);
CGSize labelSize;
labelSize = [self.text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
CGContextMoveToPoint(ctx, 0, self.bounds.size.height - 1);
CGContextAddLineToPoint(ctx, labelSize.width + 10, self.bounds.size.height - 1);
CGContextStrokePath(ctx);
[super drawRect:rect];
}
Just subclass UILabel and in drawRect after you draw your text just draw an simple line under the text. Take a look at StrikeUILabel it have some bugs in it but you can start from that class.
NSMutableAttributedString/NSAttributedString allows you to create text with various rich attributes like font,color,type styles (including underline). For generic details follow this link..
Introduction to Attributed String Programming Guide
Specific to underline of text, this link may help..
Changing an Attributed String
Sometimes i prefer to use UILabel as Underline with background of Underline color, no text, height 1 or 2 pixel and width as equal to Text to be underline...

NSString sizeWithFont: returning inconsistent results? known bug?

I'm trying to create a simple custom UIView wich contain a string drawn with a single font, but where the first character is slightly larger.
I thought this would be easily implemented with two UILabel:s placed next to eachother.
I use NSString sizeWithFont to measure my string to be able to lay it out correctly.
But I noticed that the font baseline in the returned rectangle varies with +/- 1 pixel depending on the font size I set.
Here is my code:
NSString* ctxt = [text substringToIndex:1];
NSString* ttxt = [text substringFromIndex:1];
CGSize sz = [ctxt sizeWithFont: cfont ];
clbl = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, sz.width, sz.height)];
clbl.text = ctxt;
clbl.font = cfont;
clbl.backgroundColor = [UIColor clearColor];
[contentView addSubview:clbl];
CGSize sz2 = [ttxt sizeWithFont: tfont];
tlbl = [[UILabel alloc] initWithFrame:CGRectMake(sz.width, (sz.height - sz2.height), sz2.width, sz2.height)];
tlbl.text = ttxt;
tlbl.font = tfont;
tlbl.backgroundColor = [UIColor clearColor];
[contentView addSubview:tlbl];
If I use 12.0 and 14.0 as sizes, it works fine.
But if I instead use 13.0 and 15.0, then the first character is 1 pixel too high.
Is this a known problem?
Any suggestions how to work around it?
Creating a UIWebView with a CSS and HTML page seems way overkill for this. and more work to handle dynamic strings. Is that what I'm expected to do?
Found the answer...
Ofcourse, I also have to check the descender value on the font, and compensate for that in the layout.
New rect for the second label is:
CGRectMake(sz.width, (sz.height - sz2.height) + floor(cfont.descender - tfont.descender), sz2.width, sz2.height)
floor() is to make sure it snaps to pixel position, or the font will look blurry

striking through the text

I need to strike through the text of a multi-line label. Is there a way to do it?
Any suggestion would be greatly helpful. Thanks,
if you want do it with UILabel for iPhone you can't :(
so there are 3 ways:
(simplest) use UIWebView:
// set html header with styles, you can certainly use some other attributes
NSString * htmlWrap = #"<html><head><style>body{text-align:left; background-color:transparent; color:black; font-weight:bold; text-decoration:line-through; font-size:%dpt}`</style></head>`<body>%#</body`></html>";
NSStrring * myText = #"My sample strikethrough text";
webView.backgroundColor = [UIColor clearColor];
[webView setOpaque:NO];
NSString * htmlText = [NSString stringWithFormat:htmlWrap, 12, myText];
[webView loadHTMLString:htmlText baseURL:nil];
use unicode combining diacritic (this works with any objects labels, textfields etc.)
"long stroke overlay" (U+0336) or
"combining low line" (U+0332) before
each charecter in your string. Use
-(void)getCharacters:(unichar *)buffer range:(NSRange)aRange
to create unichar array from string (allocate double size of string length), then rearrange array and add U+0336 or U+0332 before each character, then convert unichar array back to NSString with
-(id)initWithCharacters:(const unichar *)characters length:(NSUInteger)length
but in most cases this looks bad
Draw it manualy on context.
This will work for a single line label.
#interface UILabelStrikeThrough : UILabel {
}
#end
#implementation UILabelStrikeThrough
- (void)drawRect:(CGRect)rect {
CGContextRef c = UIGraphicsGetCurrentContext();
CGFloat black[4] = {0.0f, 0.0f, 0.0f, 1.0f};
CGContextSetStrokeColor(c, black);
CGContextSetLineWidth(c, 2);
CGContextBeginPath(c);
CGFloat halfWayUp = (self.bounds.size.height - self.bounds.origin.y) / 2.0;
CGContextMoveToPoint(c, self.bounds.origin.x, halfWayUp );
CGContextAddLineToPoint(c, self.bounds.origin.x + self.bounds.size.width, halfWayUp);
CGContextStrokePath(c);
[super drawRect:rect];
}
#end
Improved #RefuX code to handle location and width of the strikethrough.
I will add multiline support soon.
Multiple lines now supported
Gist for UILabelStrikethrough

Drop Shadow on UITextField text

Is it possible to add a shadow to the text in a UITextField?
As of 3.2, you can use the CALayer shadow properties.
_textField.layer.shadowOpacity = 1.0;
_textField.layer.shadowRadius = 0.0;
_textField.layer.shadowColor = [UIColor blackColor].CGColor;
_textField.layer.shadowOffset = CGSizeMake(0.0, -1.0);
I have a slightly different problem - I want a blurred shadow on a UILabel. Luckily, the solution to this turned out to be number (2) from Tyler
Here's my code :
- (void) drawTextInRect:(CGRect)rect {
CGSize myShadowOffset = CGSizeMake(4, -4);
CGFloat myColorValues[] = {0, 0, 0, .8};
CGContextRef myContext = UIGraphicsGetCurrentContext();
CGContextSaveGState(myContext);
CGColorSpaceRef myColorSpace = CGColorSpaceCreateDeviceRGB();
CGColorRef myColor = CGColorCreate(myColorSpace, myColorValues);
CGContextSetShadowWithColor (myContext, myShadowOffset, 5, myColor);
[super drawTextInRect:rect];
CGColorRelease(myColor);
CGColorSpaceRelease(myColorSpace);
CGContextRestoreGState(myContext);
}
This is in a class that extends from UILabel and draws the text with a shadow down and to the right 4px, the shadow is grey at 80% opacity and is sightly blurred.
I think that Tyler's solution number 2 is a little better for performance than Tyler's number 1 - you're only dealing with one UILabel in the view and, assuming that you're not redrawing every frame, it's not a hit in rendering performance over a normal UILabel.
PS This code borrowed heavily from the Quartz 2D documentation
I don't think you get built-in support for text shadows here, the way you do with UILabel.
Two ideas:
(1) [Moderately tricky to code.] Add a second UITextField behind the original, at a very small offset (maybe by (0.2,0.8)? ). You can listen to every text change key-by-key by implementing the textField:shouldChangeCharactersInRange:replacementString: method in the UITextFieldDelegate protocol. Using that, you can update the lower text simultaneously. You could also make the lower text (the shadow text) gray, and even slightly blurry using the fact that fractionally-offset text rects appear blurry. Added: Oh yea, don't forget to set the top text field's background color to [UIColor clearColor] if you go with this idea.
(2) [Even more fun to code.] Subclass UITextField and override the drawRect: method. I haven't done this before, so I'll mention up front that this depends on this being the designated drawing method, and it may turn out that you have to override another drawing function, such as drawTextInRect:, which is specific to UITextField. Now set up the drawing context to draw shadows via the CGContextSetShadow functions, and call [super drawRect:rect];. Hopefully that works -- in case the original UITextField code clears the drawing context's shadow parameters, that idea is hosed, and you'll have to write the whole drawing code yourself, which I anti-recommend because of all the extras that come with UITextFields like copy-and-paste and kanji input in Japanese.
Although the method of applying the shadow directly to the UITextView will work, it's the wrong way to do this. By adding the shadow directly with a clear background color, all subviews will get the shadow, even the cursor.
The approach that should be used is with NSAttributedString.
NSMutableAttributedString* attString = [[NSMutableAttributedString alloc] initWithString:textView.text];
NSRange range = NSMakeRange(0, [attString length]);
[attString addAttribute:NSFontAttributeName value:textView.font range:range];
[attString addAttribute:NSForegroundColorAttributeName value:textView.textColor range:range];
NSShadow* shadow = [[NSShadow alloc] init];
shadow.shadowColor = [UIColor whiteColor];
shadow.shadowOffset = CGSizeMake(0.0f, 1.0f);
[attString addAttribute:NSShadowAttributeName value:shadow range:range];
textView.attributedText = attString;
However textView.attributedText is for iOS6. If you must support lower versions, you could use the following approach. (Dont forget to add #import <QuartzCore/QuartzCore.h>)
CALayer *textLayer = (CALayer *)[textView.layer.sublayers objectAtIndex:0];
textLayer.shadowColor = [UIColor whiteColor].CGColor;
textLayer.shadowOffset = CGSizeMake(0.0f, 1.0f);
textLayer.shadowOpacity = 1.0f;
textLayer.shadowRadius = 0.0f;