Is there a way to implicitly make an button be multi-line? - ios

I want a button that is similar to this:
ONE
TWO
Where one word is over the other. However, it may be:
THREE
FOUR
Or any number, really. Is there a way with NSAttributedStrings that I can say to always have a line-break after the first word?

It's really not about NSAttributedString or NSString, but rather about the button itself. You may think of subclassing UIButton and overriding setTitle:forState: to automatically replace the first space with a \n.
Specifically the setTitle:forState: would look something like this
- (void)setTitle:(NSString *)title forState:(UIControlState)state {
NSRange firstSpaceRanger = [title rangeOfString:#" "];
if (firstSpaceRanger.location != NSNotFound) {
title = [title stringByReplacingCharactersInRange:firstSpaceRanger withString:#"\n"];
}
[super setTitle:title forState:state];
}
For instance, given one two three this will produce
one
two three
In order to make the button multiline you can do, in the UIButton initializer:
self.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
Yes, firstSpaceRanger is intentional. I couldn't resist.

Related

Set text(with emojis, special chars, links) in UILabel, and link should be clickable

In one of my application, there is API called and get data from server. In that there is text - which contains emojis, special characters, URLs, etc.
Not In my UITableViewCell, I have Simple UILabel and set text to label.
Till now everything is working fine.
But now I want that URLs in UILabel should be tappable, so when we click on URL and it should be open in Safari.
Note : As text is coming from server, so I don't know position of URL and there will be multiple URL as well.
What to do for that ? I have searched but didn't find solution.
I needed this kind of linked text for another project last month. I created a custom UITableViewCell with a UITextView object in the cell, subclassed to a custom UITextView subclass. I just made a quick demo project and put it up on git for you now. Feel free to use it.
github.com/fareast555/MPC_LinkedTextView
The basic secret sauce for using text links is the NSLinkAttributeName attribute in the mutable text, and telling the system to handle links.
- (void)updateTextViewWithFullText:(NSString *)fullText linkTriggerText:(NSString *)triggerText linkURLString:(NSString *)urlString
{
//Create a mutable string based on the full text
NSMutableAttributedString *mutableString = [[NSMutableAttributedString alloc]initWithString:fullText attributes:nil];
//Add the link attribute across the range of the target text
[mutableString addAttribute:NSLinkAttributeName value:urlString range:[fullText rangeOfString:triggerText]];
//Add any other font or color bling as needed
[mutableString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:18 weight:UIFontWeightMedium] range:NSMakeRange(0, [fullText length])];
//Set the mutable text to the textfield
[self setAttributedText: mutableString];
}
#pragma mark - UITextViewDelegate
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction
{
return YES;
}
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
return NO;
}
And for textviews, you'll find it easier to use auto layout if you turn off scrolling.
#pragma mark - Configure
- (void)_configureTextView
{
self.delegate = self;
[self setEditable:NO];
[self setSelectable:YES];
[self setScrollEnabled:NO];
[self setUserInteractionEnabled:YES];
[self setDataDetectorTypes:UIDataDetectorTypeLink];
self.scrollIndicatorInsets = UIEdgeInsetsZero;
}

Changing NSAttributedString text value for a UIControl

Currently all my buttons and textfields have attributedText values defined to be attributed strings.
Consider the case with a simple UILabel. Whenever I have to change the text for this UILabel (based on some user action), I have to redefine the attributes on the NSAttributedString. One way is to simply create a subroutine that generates these attributes whenever I require them but that's a concern given there could be a number of different labels (or attributed strings) that would require such convenience methods.
Another could be simply changing the text field and having observers add those attributes but that's the same amount of work and now probably more complicated.
Is there a simple way of achieving the above without redefining attributes?
Exploring #Harry's ideas, here are a few ideas :
Category on NSAttributedString, category on UILabel, or category on NSDictionary, and maybe a mix of them, according to which one best suits you and your project.
Using a category on NSAttributedString in priority before UILabel could be more interesting in case you wanted to use the custom NSAttributedString for others kind of object (like a UITextView).
A good start:
typedef enum : NSUInteger {
AttributeStyle1,
AttributeStyle2,
} AttributeStyles;
A possible category method on NSDictionary:
-(NSDictionary *)attributesForStyle:(AttributeStyles)style
{
NSDictionary *attributes;
switch(style)
{
case AttributeStyle1:
attributes = #{}//Set it
break;
case AttributeStyle2:
attributes = #{}//Set it
break;
default:
attributes = #{}//Set it
break;
}
return attributes;
}
Category possible on UILabel:
-(void)setString:(NSString *)string withAttributes:(NSDictionary *)attributes
{
[self setAttributedText:[[NSAttributedString alloc] initWithString:string attributes:attributes];
}
Category possible on NSAttributedString:
-(NSAttributedString *)initWithString:(NSString *)string withStyle:(AttributedStyles)style
{
//Here, a mix is possible using the first method, or doing here the switch case
//Ex: return [[NSAttributedString alloc] initWithString:string attributes:[NSDictionary attributesForStyle:style];
//And to use like this: [yourLabel setAttributedText:[[NSAttributedString alloc] initWithString:string withStyle:AttributeStyle1];
}

UILabel - Alternative for Deprecated Method "adjustsLetterSpacingToFitWidth"

So in my code, I was checking if my characters fit in my Label or not and had the following line :
return self.adjustsLetterSpacingToFitWidth;
This was placed in an implementation of UILabel. Can someone tell me just what is the exact alternative for this? The documentation says - Use NSKernAttributeName, but I wasn't quite able to understand that. Can someone help me on this?
In the larger sense - The method is called as:
xLab.adjustLetterSpacingToFitWidth = YES;
In my code, I have:
#implementation UILabel ()
- (BOOL) getter {
return self.adjustsLetterSpacingToFitWidth;
}
- (void) setter:(BOOL) setVal {
self.adjustsLetterSpacingToFitWidth = setVal;
}
#end
First of all, your getter and setter are entirely superfluous as shown; wherever you call getter and/or setter, you could simply get/set adjustsLetterSpacingToFitWidth directly.
As for the question of how to do auto-kerning with NSKernAttributeName, Apple's documentation says: “To turn on auto-kerning in the label, set NSKernAttributeName of the string to [NSNull null]”, i.e., you would do something like:
NSMutableAttributedString *s;
s = [[NSMutableAttributedString alloc] initWithString:my_uilabel.text];
[s addAttribute:NSKernAttributeName
value:[NSNull null]
range:NSMakeRange(0, s.length)];
my_uilabel.attributedText = s;
But if you did not want to do automatic adjustment of letter spacing but rather find out whether the text fits in the label or not, you might want to check the various methods in NSString UIKit additions. (This guess of intent is based on the wording in the original question.)

Add a permanent Character to UITextField

Is there a way to permanently add a letter to a UITextField where the user cannot delete it? I want to add a single character and the user not be able to delete it but they can still add letters after.
Cheers,
p.s. This is for iOS
A UITextField has a delegate method called should change characters in range, this method basically ask, should i add or remove the next character? and from that you can dictate what you would like. Here is some example code.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
BOOL shouldChangeCharacters = ( (textField.text.length + string.length) > 1 );
return shouldChangeCharacters;
}
This code means if the new character being added plus the current text is greater than 1 then it is okay to make the change, if the text is not greater than one, then we will not make the change...
Now, under the assumption that someone may try to paste over your character, this delegate method is still called but you have to do a few things.
if (range.location == 0) {
NSString* firstCharacter = [string substringToIndex:1];
BOOL firstCharacterIsDesiredCharacter = [firstCharacter isEqualToString:#"#"];
if ( !firstCharacterIsDesiredCharacter ) {
NSString* symbolWithText = [NSString stringWithFormat:#"#%#",text];
// ******************** \\
[textView setText:symbolWithText];
return NO;
// or we could do this
string = symbolWithText;
// ******************** \\
}
}
Both scenarios, are modifying the values of the parameters... some people don't like doing that. It could be a bad programming practice.. or if you are going to modify them there's some process you should do first.
With that, we only need to run this code if they are trying to replace the first character, i substituted the hash tag symbol, the first character is from a range of location 0 and length of 1. So if the range is equal to 0, then we run our code to fix it. This code also takes into consideration that they might be pasting the special symbol with it. so if the UITextField read #work, and they tried to copy "work" or "#work" it takes both scenarios into consideration and completely skips the code if the hash mark is the first character.
UITextField Reference
try this
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
label.text = #"left";
label.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.0];
textField.leftViewMode = UITextFieldViewModeAlways;
textField.leftView = label;
Try using this:
in your .h:
IBOutlet UITextFeild *textFeild;
in your .m:
- (BOOL)textFieldShouldReturn:(UITextField *)textFeild {
NSString *textFeildText = textFeild.text;
textFeild.text = [NSString stringWithFormat:#"string you want to always add %#",textFeildText];
return NO;
}
This won't always show up when the user is editing the text box, but once they hit return it will automatically be added to the text box. If you use this code I recommend that you do not have the string that you want in the text field by default, or else you may end up with something like this:
String you want to add String you want to add Hello World!
If you just use the code I give you and don't put the string that you want to always be in the field by default, you're good to go! If you want to make the keyboard automatically disappear after they tap return just add this in the textFeildShouldReturn method above the 'return NO' statement:
[textFeild resignFirstResponder];

Is there an option to implicitly localise labels in Interfacebuilder

Somewhere in a blog post I stumbled upon a strings file which looked like this:
// de.lproj/Localizable.strings
"This is the title" = "Das ist der Titel"
To me this looked like the actual labels in Interface builder were processed by the compiler so that no explicit translations using NSLocalizedString(#"SOME_IDENTIFIER", #""); would be necessary any more.
My question now, is whether there is some kind of shortcut or do I need to localise all my individual labels on my view e.g. in the awakeFromNib method.
I have figured out a way to semi-automate the process so that I don't have to do this:
label1.text = NSLocalizedString(#"label1_key", #"");
label2.text = NSLocalizedString(#"label2_key", #"");
....
labeln.text = NSLocalizedString(#"labeln_key", #"");
So for all labels which should be localised I set their text to __KeyForLabelX in IB. Then in the viewWillAppear method of the viewcontroller I loop through the items on the view and set the text to the localized value:
for (UIView *view in self.view){
if([view isMemberOfClass:[UILabel class]]){
UILabel *l = (UILabel *)view;
BOOL shouldTranslate = [l.text rangeOfString:#"__"].location != NSNotFound;
NSString *key = [l.text stringByReplacingOccurrencesOfString:#"__" withString:#"TranslationPrefix"];
if (shouldTranslate){
l.text = NSLocalizedString(key, #"");
}
}
}
My .strings file then look like this:
"TranslationPrefixKeyForLabelX" = "Translation of Label X";
Update: To further adapt the mechanism you could also check for other UIViews like UIButtons, UITextFields (including prompt text) etc.

Resources