I'm already pretty sure that it can't be done with any public API, but I still want to ask:
Is there any way to change the line height in a UITextView?
Would be enough to do it statically, no need to change it at runtime. The problem is that the default line height is just WAY too small. Text will look extremely compressed and is a nightmare when trying to write longer texts.
thanks,
Max
EDIT: I know that there is UIWebView and that it's nice and can do styling etc. But it's not editable. I need a editable text component with acceptable line height. That thing from the Omni Frameworks doesn't help either, as it's too slow and doesn't feel right...
After iOS 7, the styleString approach no longer works.
Two new alternatives are available.
Firstly, TextKit; a powerful new layout engine. To change line spacing, set the UITextView's layout manager's delegate:
textView.layoutManager.delegate = self; // you'll need to declare you implement the NSLayoutManagerDelegate protocol
Then override this delegate method:
- (CGFloat)layoutManager:(NSLayoutManager *)layoutManager lineSpacingAfterGlyphAtIndex:(NSUInteger)glyphIndex withProposedLineFragmentRect:(CGRect)rect
{
return 20; // For really wide spacing; pick your own value
}
Secondly, iOS 7 now supports NSParagraphStyle's lineSpacing. This gives even more control, e.g. first line indentation, and calculation of a bounding rect. So alternatively...
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.headIndent = 15; // <--- indention if you need it
paragraphStyle.firstLineHeadIndent = 15;
paragraphStyle.lineSpacing = 7; // <--- magic line spacing here!
NSDictionary *attrsDictionary =
#{ NSParagraphStyleAttributeName: paragraphStyle }; // <-- there are many more attrs, e.g NSFontAttributeName
self.textView.attributedText = [[NSAttributedString alloc] initWithString:#"Hello World over many lines!" attributes:attrsDictionary];
FWIW, the old contentInset method to align the text along the left edge of UITextView is also no use under iOS7. Instead, to remove the margin:
textView.textContainer.lineFragmentPadding = 0;
Note: this is not available in iOS7:
I have discovered that you can create a subclass that re-implements [UITextView styleString]:
#interface UITextView ()
- (id)styleString; // make compiler happy
#end
#interface MBTextView : UITextView
#end
#implementation MBTextView
- (id)styleString {
return [[super styleString] stringByAppendingString:#"; line-height: 1.2em"];
}
#end
This is not private API usage: it is just subclassing. It's possible Apple may disagree of course (though considering how we all used to swizzle everything in order to customize UIKit appearance, I feel that this kind of “private” usage is not what Apple object to), but it's such an easy way to achieve the goals of this question that you may as well try it. Should the app be rejected you can spend the (probably significant) time on a more difficult solution.
In the Attribute Inspector for the UITextView instead change the property Text to Attributed (from Plain), and click the "more" button, there you can set the line height and spacing.
The only solution we've found and the one we've chosen: create a custom font. Sound silly but seems to be the only realistic way.
The UITextView subclass override of styleString only works if you define a category on UITextView that defines styleString, otherwise you get a compile error. For example, in your UITextView subclass:
#import "SomeDangTextView.h"
#interface UITextView ()
- (id)styleString;
#end
#implementation SomeDangTextView
- (id)styleString {
return [[super styleString] stringByAppendingString:#"; line-height: 1.5em"];
}
#end
This question is almost 10 years old but this is how it's done:
Just implement the following method of UITextViewDelegate and set your attributes:
let textViewAttributes: [NSAttributedString.Key:Any] = [
.font: UIFont.systemFont(ofSize: 15, weight: .medium),
.foregroundColor: UIColor.black,
.paragraphStyle: {
let paragraph = NSMutableParagraphStyle()
paragraph.lineSpacing = 4
return paragraph
}()
]
func textViewDidBeginEditing(_ textView: UITextView) {
textView.typingAttributes = textViewAttributes
}
It's important to add these attributes on textViewDidBeginEditing as the dictionary gets reset every time the text selection changes. More info can be found on the official documentation.
According to Apple's Documentation you can use a UIWebView.
This class does not support multiple styles for text. The font, color, and text alignment attributes you specify always apply to the entire contents of the text view. To display more complex styling in your application, you need to use a UIWebView object and render your content using HTML.
This class does not support multiple styles for text. The font, color, and text alignment attributes you specify always apply to the entire contents of the text view. To display more complex styling in your application, you need to use a UIWebView object and render your content using HTML.
Use OHAttributedLabel Lib. This will solve all the problem you mentioned.
Related
If I have multi-line non-scrollable UITextView whose text is longer than can fit in the visible area, then the text just cuts off like so:
Congress shall make no law respecting
an establishment of religion, or
How would I get the text to show with an ellipsis where the text cut-off is, like so
Congress shall make no law respecting
an establishment of religion, or …
Other controls like labels and buttons have this ability.
Why not use a UILabel setting numberOfLines to something appropriate and getting that functionality for free?
The UITextView is designed to scroll when the string is larger than what the view can show. Make sure that you have set the anchoring and autoresize attributes correctly in code or your xib.
Here is an example from a blog post about how to implement your own ellipsis.
#interface NSString (TruncateToWidth)
- (NSString*)stringByTruncatingToWidth:(CGFloat)width withFont:(UIFont *)font;
#end
#import "NSString+TruncateToWidth.h"
#define ellipsis #"…"
#implementation NSString (TruncateToWidth)
- (NSString*)stringByTruncatingToWidth:(CGFloat)width withFont:(UIFont *)font
{
// Create copy that will be the returned result
NSMutableString *truncatedString = [[self mutableCopy] autorelease];
// Make sure string is longer than requested width
if ([self sizeWithFont:font].width > width)
{
// Accommodate for ellipsis we'll tack on the end
width -= [ellipsis sizeWithFont:font].width;
// Get range for last character in string
NSRange range = {truncatedString.length - 1, 1};
// Loop, deleting characters until string fits within width
while ([truncatedString sizeWithFont:font].width > width)
{
// Delete character at end
[truncatedString deleteCharactersInRange:range];
// Move back another character
range.location--;
}
// Append ellipsis
[truncatedString replaceCharactersInRange:range withString:ellipsis];
}
return truncatedString;
}
#end
Someone just showed me that it's actually really easy to do this with UITextView on iOS 7 and up:
UITextView *textView = [UITextView new];
textView.textContainer.lineBreakMode = NSLineBreakByTruncatingTail;
Do you know a simple (or not simple) way to hide a view (or anything like a view) and let the other views of the screen use the place left blank ? And make the opposite when showing back that view. Something like Android Visibility = GONE for layers.
Thank you
Many UIKit classes have a property hidden that does what you want. It is defined in UIView so you will find it in most visual elements you use.
There is no such thing as Visibility.GONE, as far as my research has shown, not even AutoLayout can help you. You have to manually replace the views affected by the optionally shown component (in my case, all the views below the optionalView on bottomView):
- (IBAction)toggleOptionalView:(id)sender {
if (!_expanded) {
self.optionalView.frame = CGRectMake(self.optionalView.frame.origin.x, self.optionalView.frame.origin.y, self.optionalView.frame.size.width, _optionalHeight);
self.bottomView.frame = CGRectMake(self.bottomView.frame.origin.x, self.bottomView.frame.origin.y+_optionalHeight, self.bottomView.frame.size.width, self.bottomView.frame.size.height);
_expanded = YES;
} else {
self.optionalView.frame = CGRectMake(self.optionalView.frame.origin.x, self.optionalView.frame.origin.y, self.optionalView.frame.size.width, 0);
self.bottomView.frame = CGRectMake(self.bottomView.frame.origin.x, self.bottomView.frame.origin.y-_optionalHeight, self.bottomView.frame.size.width, self.bottomView.frame.size.height);
_expanded = NO;
}
}
It is advisable not to hard-code the height of the optional component, otherwise your code breaks every time you edit the XIB/Storyboard. I have a field float _optionalHeight which I set in viewDidLoad, so it is always up to date.
I've been looking all over the 'net for information/examples...
I'm trying to change the line spacing of text inside a UITextView object to double spaced. I thought you could do it via Core Text, but haven't found a solution!
Any example code or information would be greatly appreciated! Thanks!
You don't have to look all over the net. A look at the documentation for UITextView is sufficient to determine that changing the line spacing is not supported by that control.
With Core Text you have complete control over the layout of the text you draw, of course. But it would be a lot of work to rewrite a UITextView-like control from scratch.
This question was asked before iOS 6. I'd like to post an updated answer for those who would like a way to do this.
This can be done now with iOS 6 and later by using an NSAttributedString. UITextView now accepts an attributed string as one of its properties. You can do all sorts of text manipulation with attributed strings, including line spacing. You can set the min and max line heights for the paragraph style attribute of the string.
Check out the NSAttributedString Class Reference for more information.
Here's a sample of what you could do:
NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
paragraph.minimumLineHeight = 21.0f;
paragraph.maximumLineHeight = 21.0f;
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:#"Test text line 1\nTest text line 2\nTest text line 3" attributes:#{NSParagraphStyleAttributeName: paragraph}];
textView.attributedText = attributedString;
You can use NSLayoutManagerDelegate. Add that delegate to your ViewController or UIView class (etc.) and then when you create your UITextView...
yourTextView.layoutManager.delegate = self
Add this delegate method:
func layoutManager(layoutManager: NSLayoutManager, lineSpacingAfterGlyphAtIndex glyphIndex: Int, withProposedLineFragmentRect rect: CGRect) -> CGFloat {
return 5 //Whatever you'd like...
}
I'm trying to get the contents from a dictionary into a UITextView. The dictionary contains molecular masses paired with percentages, for example:
24 -> 98
25 -> 1.9
26 -> 0.1
I have an NSArray containing the keys from the dictionary, sorted in ascending order. So, here is my code to generate the string to set as the textField.text property:
-(void)detailIsotopes:(NSMutableDictionary *)isotopes withOrder:(NSMutableArray *)order{
NSMutableString *detailString = [[NSMutableString alloc] init];
for (NSNumber *mass in order){
[detailString appendString:[NSString stringWithFormat:#"%d: %f\n", [mass integerValue], [[isotopes valueForKey:[NSString stringWithFormat:#"%#", mass]] floatValue]]];
}
NSLog(#"%#", detailString);
textField.text = detailString;
[detailString release];
}
This should create a string looking like this:
24: 89
25: 1.9
26: 0.1
For some reason, this method never does anything the first time it runs. I see the NSLog output, which outputs the correct string. However, the contents of the UITextView don't change: they stay as 'Lorem ipsum dolor sit amet...' from Interface Builder. If I run the method again, it works, sort of.
The UITextView displays some of the text, and then just cuts off half way through a line, leaving only the tops of the characters. If I delete the contents above the half line, the other lines pull up from under the divide: the contents are there, they just stop being shown, if you understand what I mean. This appears to go away if I enable paging in the view. If I do that, then the line isn't truncated, but the UITextView just stops showing any content after some point, although the scroll bar indicates that there is more to go (which there is).
The view containing the UITextView is not visible when the contents is set, if that makes a difference. A separate view controller generates the NSMutableDictionary and NSMutableArray and sends them to its delegate, which then sends them to the view which should display the UITextField and has the detailIsotopes: withOrder: method. The two can be swapped between with an info button.
Does anyone understand why these things are happening?
Thanks for any advice you can give!
First of all, I don't think you need to allocate and release your NSMutableString here. Simply initialize one using [NSMutableString string] which creates an empty string you can modify and don't need to explicitly release.
Second thing, you seem to store masses in NSStrings in your NSDictionnary, why do you use their integerValue method for stringWithFormat (with %d modifier), instead of using them as is? (the stringWithFormat modifier for NSStrings is %#)
Also, you talk about a UITextView at start, then about a UITextField, are you sure you did not make a mistake somewhere? I guess the receiving object for your formatted NSString should rather be the UITextView (if you have both a textField and textView).
If it's not about this, maybe you are calling detailIsotopes too early and the textView it's created yet. Try to NSLog its address and see if it's nil the first time. It could be the case if you use Interface Builder and your UITextField is an ib outlet. If you do, then you could store your dictionary and array in the viewController, and set the textField in the viewController's viewDidLoad method. Or call detailIsotopes after you've displayed the view, I guess that's up to you.
About the truncated text, I think that's because UITextView doesn't resize itself automatically, so it keeps the height you originally set. What I usually do is this:
CGRect frame = textView.frame;
frame.size.height = textView.contentSize; // you can adjust this to leave some space at the end
textView.frame = frame;
This will set the textView height to the content (the text) height.
Also note that if your textView is supposed to display the whole text, you can set its scrollingEnabled property to FALSE so it never allows scrolling.
Hope that helps.
i have a uiTextField that i am validating input on, when i have invalid input what are some appropriate ways to show that the input is invalid? is there any built in mechanism for showing invalid input states?
It's pretty easy to add an 'warning' image to the left-hand side of a UITextField to indicate that the field needs a value.
Try this:
UITextField* field = .... your text field ...
if ( fails_validation ) {
field.leftViewMode = UITextFieldViewModeAlways;
UIImageView* imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 32, 32)];
imageView.image = [UIImage imageNamed:#"warning.png"];
imageView.contentMode = UIViewContentModeScaleAspectFit;
field.leftView = imageView;
} else {
field.leftViewMode = UITextFieldViewModeNever;
field.leftView = nil;
}
The best two options I've found so far are TextFieldValidator and US2FormValidator. With the caveat that I have only used the former, here's my take on each.
TextFieldValidator
↑ Simple (just one class!)
↑ Offers default UI with additional customization possible
↑ Handles multiple validations on a single field
↓ Doesn't handle text views
↓ Slightly awkward name…ValidatedTextField, for example, would be more accurate :)
US2FormValidator
↑ Handles text fields and text views
↑ Importable as a framework, including CocoaPods support
↑ Handles multiple validations on a single field
↓ No default UI
If you just need something implemented quickly, TextFieldValidator may be all you need. If you must have validated UITextViews, US2FormValidator is the way to go.
Have a look at Managing Overlay Views section in UITextField docs
Put a check mark to the right of the UITextField when correct, Else put an X to the right of it. to make it look smooth: fade it using an animation