I'm using this method to change color to few chars of my UITextView
+(void)changeColorToTextObject : (UITextView *)textView ToColor : (UIColor *)color FromLocation : (int)location WithLength : (int)length
{
NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithAttributedString: textView.attributedText];
[text addAttribute:NSForegroundColorAttributeName value:color range:NSMakeRange(location, length)];
[textView setAttributedText: text];
}
It works great but after I change the specific range I want to be able to continue with the previous color and not with the new color that I just changed to.
How can I do it?
Actually, when working with attributed text, I found it best practice to set content first and style second (which actually originates from old typesetting rules).
In your case, I would first set the whole text on the UITextView, and then call the method changeColorToTextObject:ToColor:FromLocation:WithLength specifying only the portion of the text you want in a different color.
However, if you are not talking about static content, but rather something like a word processor app, where the user can change text color and continues to write afterwards, you need to think about how you want this app to work. Consider the following method:
- (void)highlightSelectedText {
UIColor *highlightColor = [UIColor redColor];
[self changeColorToTextObject:self.textView ToColor:highlightColor FromLocation:self.textView.selectedRange.location WithLength:self.textView.selectedRange.length];
}
This method allows you to highlight the specific part of the text, the user has currently selected:
If the user then continues typing, the new text will appear in red:
If instead, they put the focus at a place where font color is black, they will continue writing in black:
When you think about a word processor, that seems actually pretty familiar. And if that is good enough for you, you are good to go.
The only border case you need to worry about then is what you do, when there is no text selected.
However, if you need more control over the dynamic behavior of your UITextView (and if you are >iOS 7), you should consider using Text Kit, and in particular subclassing NSTextStorage. There is a introduction video to Text Kit from WWDC 2013, and a great tutorial by Ray Wenderlich.
Save your previous color into a 'UIColor' property before apply the attributedText to the UITextView, and assign it to the UITextView after using the 'changeColorToTextObject' method.
Related
Is there a way to attach a UITextField inside inside the range of an NSAttributedString that is inside a UITextView?
I want to recreate something like this:
Where the user would be able to press the empty lines to type text.
You could calculate a frame for the UITextField like so:
- (CGRect)textFieldFrameForCharacterRange:(NSRange)charRange inTextView:(UITextView *)textView {
NSRange glyphRange = [textView.layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL];
CGRect boundingRect = [textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textView.textContainer];
return boundingRect;
}
and then add it as a subview of the UITextView. Determining a character range to pass to the above method can be easy too - just search for a run of underlines, or maybe a custom attribute using - enumerateAttribute:inRange:options:usingBlock:.
The catch is this won't work properly if the underlines are split into more than one line, but UITextField wouldn't fit that case anyway. So you need a way to make sure the underline remains unbroken. The U+2060 unicode character may help, see Prevent line break in a NSAttributedString for more. An NSTextAttachment with the underline represented as an image would also be guaranteed not to get word wrapped.
Alternatively, you could implement a UITextViewDelegate, or possibly even a UITextView subclass, that disallows selection and editing outside of the underline ranges. That way you can avoid the weirdness of UITextFields within UITextViews. But the drawback with this method is even if you deleted an underline character every time the user types a character, the user would still see the overall length of the underline fluctuating slightly.
This problem sometimes bothers me a lot. For example, if I just want to change the header text font size, I have to create the whole UIView. There is no direct instance method for UITableView like [tableView setHeaderTextFont], I think apple should really add this method.
However, this post did a good job, but it's not exactly the same with the original one. If you check the original one very careful, it has effect like 'inner glow' in photoshop, but with NSAttributedString, this can not be done (correct me if I'm wrong). It must be rendered by some private methods.
Also, the footer text style is the same.
So my question is: how to recreate the grouped tableview header/footer text style, exactly.
Thanks.
The effect you're talking about is just a 1px white shadow. It's there in the post you linked to:
label.shadowColor = [UIColor colorWithWhite:1.0 alpha:1];
label.shadowOffset = CGSizeMake(0, 1);
Are you implementing this and not seeing the effect?
Is there any way to give different font size to each line of a label with "n" no of lines.
I dont want to take multiple labels for this purpose.
plz help!!!
Starting with iOS 6, you can use NSAttributedString in UILabel.
An NSAttributedString object manages character strings and associated sets of attributes (for example, font and kerning) that apply to individual characters or ranges of characters in the string.
NSMutableAttributedString *attrStr = ...
myLabel.attributedText = attrStr;
Yes, but You have to use UIWebView, pass your text in html format.
UILabel with NSAttributedString(iOS 6 and later).
Or UIWebView with html-strings, but my favorit is a UITextView with html-strings. You can use it like this:
UITextView *tView=[UITextView new];
[tView setValue:#"<b>foo</b>bar" forKey:#"contentToHTMLString"];
UITextViews are easier to handle then UIWebView, especially if you don't need all the functionality of the WebView.
If you are targeting iOS < 6.0 then by using UILabel its not at all possible. Else you can refer dasblinkenlight answer (for iOS > 6)
There are couple of methods by which you can implement same :
Use UIWebView
Create UILabel for different fonts you want and set their frame as you want to display.
I want to implement custom text fields. Here is a screenshot of what I want to implement:
So I have a UITableView with cells, and in each cell I have several text fields: one for date, one for name, one for theme, and one for text. But the UI requires theme and text to be next to each other (as can you see in the picture). I wanted to implement this as a single UITextField, but as far as I know, UITextField supports only one type of font. So maybe someone will give me a piece of advice how to implement the design shown in the screenshot — do I have to draw custom text fields, or are there simpler solutions? Any code samples or propositions would be helpful.
For iOS 6:
You can set a UITextField's attributed string:
#property(nonatomic,copy) NSAttributedString *attributedText;
I suggest to use a NSMutableAttributedString and to exploit polymorphism.
A mutable attributed string can hold vary attributes for each piece of the string. So you can add an attribute only for a certain range of the string with this method:
- (void)setAttributes:(NSDictionary *)attributes range:(NSRange)aRange;
For iOS 5:
Use directly this UITextField's property:
- (void)setAttributes:(NSDictionary *)attributes range:(NSRange)aRange;
I have implemented a UITextView where i can underline the text, sometime the text is not underlined at the right place it crosses the words and sentences, is there a default method which underlines the sentences at the right places avoiding the user to maintain the accuracy while touching the screen. help greatly appreciate please share your views.
I would use a UITextView with attributed text and modify the attributes as needed.
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:#"All work an no play makes Jack a dull boy."];
[attributedString addAttribute:NSUnderlineStyleAttributeName value:#(NSUnderlineStyleSingle) range:NSMakeRange(15, 4)];
self.textView.attributedText = attributedString;
where self.textView is an IBOutlet to your UITextView and the values are the NSRange are determined by your user interaction.
The problem is that internally a UITextView is a UIWebDocumentView (aka WebKit) and HTML has different rules for underlining words than normal text editors. See my analysis here: http://www.cocoanetics.com/2012/12/uitextview-caught-with-trousers-down/
The approach to set the attributedString the the problem that you would only be able to set the entire text.
Since we do not have direct access at the text container inside UIWebDocumentView and because of the intricacies of HTML you cannot hope to achieve a perfect result.
You can only solve this problem by rendering the text yourself with CoreText. My open source component DTCoreText could help with the display. I am also selling a commercial component for rich text editing that is based on that: http://www.cocoanetics.com/2012/12/dtrichtexteditor-1-1/
PS: he standard method for toggling the currently selected range of text underlined is toggleUnderline: defined as such:
#interface NSObject(UIResponderStandardEditActions) // these methods are not implemented in NSObject
- (void)cut:(id)sender NS_AVAILABLE_IOS(3_0);
- (void)copy:(id)sender NS_AVAILABLE_IOS(3_0);
- (void)paste:(id)sender NS_AVAILABLE_IOS(3_0);
- (void)select:(id)sender NS_AVAILABLE_IOS(3_0);
- (void)selectAll:(id)sender NS_AVAILABLE_IOS(3_0);
- (void)delete:(id)sender NS_AVAILABLE_IOS(3_2);
- (void)makeTextWritingDirectionLeftToRight:(id)sender NS_AVAILABLE_IOS(5_0);
- (void)makeTextWritingDirectionRightToLeft:(id)sender NS_AVAILABLE_IOS(5_0);
- (void)toggleBoldface:(id)sender NS_AVAILABLE_IOS(6_0);
- (void)toggleItalics:(id)sender NS_AVAILABLE_IOS(6_0);
- (void)toggleUnderline:(id)sender NS_AVAILABLE_IOS(6_0);
#end
This is exactly what the context menu adds if you enable attribute editing. If you are not happy with the result here, then my above statements are the way to go.