I have a UITextView with random properties and random size. I need to append a watermark written into a UITextView. But the watermark needs to have different text properties and different alignment .
Example:
This is the UITextView with random properties.
This is the watermark.
You need to use attributed strings (NSAttributedString) instead of strings (NSString).
UITextView has a text property and an attributedText property. In your case, use the attributedText property once you have created the attributed string.
Try using attributed string:
NSString *textViewText = #"...";
NSString *watermarkText = #"\nThis is the watermark";
NSString *fullText = [textViewText stringByAppendingString:watermarkText];
NSMutableParagraphStyle *watermarkParagraphStyle = [NSMutableParagraphStyle new];
watermarkParagraphStyle.alignment = NSTextAlignmentCenter;
NSMutableAttributedString *fullTextAttributed = [[NSMutableAttributedString alloc] initWithString:fullText];
[fullTextAttributed addAttribute:NSParagraphStyleAttributeName
value:watermarkParagraphStyle
range:[fullText rangeOfString:watermarkText]];
textView.attributedText = fullTextAttributed;
Here's a translation of #skorolkov's Objective-C code:
let textViewText = "..."
let watermarkText = "\nThis is the watermark"
let fullText = textViewText + watermarkText
let watermarkParagraphStyle = NSMutableParagraphStyle()
watermarkParagraphStyle.alignment = NSCenterTextAlignment
let fullTextAttributed = NSMutableAttributedString(string: fullText)
fullTextAttributed.addAttribute(NSParagraphStyleAttributeName,
value: watermarkParagraphStyle,
range: fullText.rangeOfString(waterMarkText))
textView.attributedText = fullTextAttributed
Related
I want to have two lines of text appear really close together (small line spacing) for a button. I have the following code:
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:#"50 WPM"];
NSMutableParagraphStyle *paragrapStyle = [[NSMutableParagraphStyle alloc] init];
paragrapStyle.alignment = NSTextAlignmentCenter;
paragrapStyle.lineSpacing = -10;
[string addAttribute:NSParagraphStyleAttributeName value:paragrapStyle range:NSMakeRange(0, string.length)];
UIFont *font1 = [UIFont systemFontOfSize:22.0];
[string addAttribute:NSFontAttributeName value:font1 range:NSMakeRange(0, string.length - 4)];
UIFont *font = [UIFont systemFontOfSize:15.0];
[string addAttribute:NSFontAttributeName value:font range:NSMakeRange(string.length - 3, 3)];
[string addAttribute:NSForegroundColorAttributeName value:[UIColor greenColor] range:NSMakeRange(0, string.length)];
[self.button setAttributedTitle:string forState:UIControlStateNormal];
But as linespacing can't be negative, it doesn't get nearly as close as I'd like it to be. It looks like this:
Is there any way to get them closer?
Well if you have an attribute string then everything should be possible. :) You just have to look more.
- (void)setMinimumLineHeight:(CGFloat)aFloat
- (void)setMaximumLineHeight:(CGFloat)aFloat
Try
[paragraphStyle setLineSpacing:0.0f];
[paragraphStyle setMaximumLineHeight:7.0f];
You will realise that maximumLineHeight is not maximumLineSpacing. ^^
This for example is with setMaximumLineHeight:12];
Here a little extension in Swift3 which supports negative lineSpacing
extension UILabel {
func set(lineSpacing: CGFloat, textAlignment: NSTextAlignment = NSTextAlignment.center) {
if let text = self.text {
let paragraphStyle = NSMutableParagraphStyle()
if lineSpacing < 0 {
paragraphStyle.lineSpacing = 0
paragraphStyle.maximumLineHeight = self.font.pointSize + lineSpacing
} else {
paragraphStyle.lineSpacing = lineSpacing
}
let attrString = NSMutableAttributedString(string: text)
attrString.addAttribute(NSParagraphStyleAttributeName, value:paragraphStyle, range:NSMakeRange(0, attrString.length))
self.attributedText = attrString
self.textAlignment = textAlignment
}
}
}
I would suggest reading up on TextKit that was introduced in iOS7. I do not have much experience from it, but I do know that it gives you a lot of possibilities when it comes to attributing your texts.
In Swift 3, you can achieve this by :
let paragraph = NSMutableParagraphStyle()
paragraph.lineSpacing = 0
paragraph.maximumLineHeight = 20.
Keep the lineSpacing = 0. You can adjust the maximumLineHeight to make it closer or to increase the spacing.
How about subclassing UIButton, and add 2 UILabels to the buttons view that are close together. Create properties for the labels and set approrpietly:
CustomButton *btn = [CustomButton new];
btn.textLine1 = #"Top";
btn.textLine2 = #"Bottom";
The only problem doing it this way is you will need to handle the text color when the state changes yourself.
What would be the best approach for aligning a UIImage icon next to the centered placeholder text of a UITextField? Ex:
I have been attempting to replicate this for sometime now and can't seem to reach the desired effect.
I've tried using the LeftView of the UITextField however can't get this aligned next to the placeholder text (I thought about adding padding to the LeftView by changing it's frame but am unsure how I would obtain the values needed to align it next to the placeholder. There does not appear to be away to get the coordinates for the placeholder text.) . I've also tried to subclass UITextField but here am again unsure on how to obtain the proper coordinates.
You can attach images inside attributed strings using NSTextAttachment
Swift 3.0
txtField.textAlignment = .center
let attachment = NSTextAttachment()
attachment.image = UIImage(named: "searchIcon")
attachment.bounds = CGRect(x: 0, y: 0, width: 10, height: 10)
let attachmentStr = NSAttributedString(attachment: attachment)
let myString = NSMutableAttributedString(string: "")
myString.append(attachmentStr)
let myString1 = NSMutableAttributedString(string: "Find a Location (Zip Code or Name)")
myString.append(myString1)
txtField.attributedPlaceholder = myString
Objective-C
NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
attachment.image = [UIImage imageNamed:#"searchIcon"];
attachment.bounds = CGRectMake(0, 0, 10, 10);
NSAttributedString *attachmentStr = [NSAttributedString attributedStringWithAttachment:attachment];
NSMutableAttributedString *myString = [[NSMutableAttributedString alloc] initWithString:#""];
[myString appendAttributedString:attachmentStr];
NSMutableAttributedString *myString1 = [[NSMutableAttributedString alloc] initWithString:#"Find a Location (Zip Code or Name)"];
[myString appendAttributedString:myString1];
txtField.attributedPlaceholder = myString;
I am making an app that formats screenplays, I am using a NSAttributedString to format the text entered into a UITextView, but some of the lines are too close together.
I was wondering if anyone could provide a code example or a tip on how to alter the margin between these lines so there is more space between them.
Below is an image of another desktop screenwriting program that demonstrates what I mean, notice how there is a bit of space before each bit where it says "DOROTHY".
The following sample code uses paragraph style to adjust spacing between paragraphs of a text.
UIFont *font = [UIFont fontWithName:fontName size:fontSize];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.paragraphSpacing = 0.25 * font.lineHeight;
NSDictionary *attributes = #{NSFontAttributeName:font,
NSForegroundColorAttributeName:[UIColor whiteColor],
NSBackgroundColorAttributeName:[UIColor clearColor],
NSParagraphStyleAttributeName:paragraphStyle,
};
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:text attributes:attributes];
To selectively adjust spacing for certain paragraphs, apply the paragraph style to only those paragraphs.
Hope this helps.
Great answer #Joe Smith
In case anyone would like to see what this looks like in Swift 2.*:
let font = UIFont(name: String, size: CGFloat)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.paragraphSpacing = 0.25 * font.lineHeight
let attributes = [NSFontAttributeName:font, NSParagraphStyleAttributeName:paragraphStyle]
let attributedText = NSAttributedString(string: String, attributes: attributes)
self.textView.attributedText = attributedText
Here is Swift 4.* version:
let string =
"""
A multiline
string here
"""
let font = UIFont(name: "Avenir-Roman", size: 17.0)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.paragraphSpacing = 0.25 * (font?.lineHeight)!
let attributes = [NSAttributedStringKey.font: font as Any, NSAttributedStringKey.paragraphStyle: paragraphStyle]
let attrText = NSAttributedString(string: string, attributes: attributes)
self.textView.attributedText = attrText
I want to have two lines of text appear really close together (small line spacing) for a button. I have the following code:
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:#"50 WPM"];
NSMutableParagraphStyle *paragrapStyle = [[NSMutableParagraphStyle alloc] init];
paragrapStyle.alignment = NSTextAlignmentCenter;
paragrapStyle.lineSpacing = -10;
[string addAttribute:NSParagraphStyleAttributeName value:paragrapStyle range:NSMakeRange(0, string.length)];
UIFont *font1 = [UIFont systemFontOfSize:22.0];
[string addAttribute:NSFontAttributeName value:font1 range:NSMakeRange(0, string.length - 4)];
UIFont *font = [UIFont systemFontOfSize:15.0];
[string addAttribute:NSFontAttributeName value:font range:NSMakeRange(string.length - 3, 3)];
[string addAttribute:NSForegroundColorAttributeName value:[UIColor greenColor] range:NSMakeRange(0, string.length)];
[self.button setAttributedTitle:string forState:UIControlStateNormal];
But as linespacing can't be negative, it doesn't get nearly as close as I'd like it to be. It looks like this:
Is there any way to get them closer?
Well if you have an attribute string then everything should be possible. :) You just have to look more.
- (void)setMinimumLineHeight:(CGFloat)aFloat
- (void)setMaximumLineHeight:(CGFloat)aFloat
Try
[paragraphStyle setLineSpacing:0.0f];
[paragraphStyle setMaximumLineHeight:7.0f];
You will realise that maximumLineHeight is not maximumLineSpacing. ^^
This for example is with setMaximumLineHeight:12];
Here a little extension in Swift3 which supports negative lineSpacing
extension UILabel {
func set(lineSpacing: CGFloat, textAlignment: NSTextAlignment = NSTextAlignment.center) {
if let text = self.text {
let paragraphStyle = NSMutableParagraphStyle()
if lineSpacing < 0 {
paragraphStyle.lineSpacing = 0
paragraphStyle.maximumLineHeight = self.font.pointSize + lineSpacing
} else {
paragraphStyle.lineSpacing = lineSpacing
}
let attrString = NSMutableAttributedString(string: text)
attrString.addAttribute(NSParagraphStyleAttributeName, value:paragraphStyle, range:NSMakeRange(0, attrString.length))
self.attributedText = attrString
self.textAlignment = textAlignment
}
}
}
I would suggest reading up on TextKit that was introduced in iOS7. I do not have much experience from it, but I do know that it gives you a lot of possibilities when it comes to attributing your texts.
In Swift 3, you can achieve this by :
let paragraph = NSMutableParagraphStyle()
paragraph.lineSpacing = 0
paragraph.maximumLineHeight = 20.
Keep the lineSpacing = 0. You can adjust the maximumLineHeight to make it closer or to increase the spacing.
How about subclassing UIButton, and add 2 UILabels to the buttons view that are close together. Create properties for the labels and set approrpietly:
CustomButton *btn = [CustomButton new];
btn.textLine1 = #"Top";
btn.textLine2 = #"Bottom";
The only problem doing it this way is you will need to handle the text color when the state changes yourself.
How can I modify the gap between lines (line spacing) in a multiline UILabel?
Edit: Evidently NSAttributedString will do it, on iOS 6 and later. Instead of using an NSString to set the label's text, create an NSAttributedString, set attributes on it, then set it as the .attributedText on the label. The code you want will be something like this:
NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString:#"Sample text"];
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
[style setLineSpacing:24];
[attrString addAttribute:NSParagraphStyleAttributeName
value:style
range:NSMakeRange(0, strLength)];
uiLabel.attributedText = attrString;
NSAttributedString's old attributedStringWithString did the same thing, but now that is being deprecated.
For historical reasons, here's my original answer:
Short answer: you can't. To change the spacing between lines of text, you will have to subclass UILabel and roll your own drawTextInRect, create multiple labels, or use a different font (perhaps one edited for a specific line height, see Phillipe's answer).
Long answer: In the print and online world, the space between lines of text is known as "leading" (rhymes with 'heading', and comes from the lead metal used decades ago). Leading is a read-only property of UIFont, which was deprecated in 4.0 and replaced by lineHeight. As far as I know, there's no way to create a font with a specific set of parameters such as lineHeight; you get the system fonts and any custom font you add, but can't tweak them once installed.
There is no spacing parameter in UILabel, either.
I'm not particularly happy with UILabel's behavior as is, so I suggest writing your own subclass or using a 3rd-party library. That will make the behavior independent of your font choice and be the most reusable solution.
I wish there was more flexibility in UILabel, and I'd be happy to be proven wrong!
Starting in ios 6 you can set an attributed string in the UILabel:
NSString *labelText = #"some text";
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:labelText];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
[paragraphStyle setLineSpacing:40];
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [labelText length])];
cell.label.attributedText = attributedString ;
You can control line spacing in the storyboard:
duplicate question
From Interface Builder:
Programmatically:
SWift 4
Using label extension
extension UILabel {
func setLineSpacing(lineSpacing: CGFloat = 0.0, lineHeightMultiple: CGFloat = 0.0) {
guard let labelText = self.text else { return }
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = lineSpacing
paragraphStyle.lineHeightMultiple = lineHeightMultiple
let attributedString:NSMutableAttributedString
if let labelattributedText = self.attributedText {
attributedString = NSMutableAttributedString(attributedString: labelattributedText)
} else {
attributedString = NSMutableAttributedString(string: labelText)
}
// Line spacing attribute
attributedString.addAttribute(NSAttributedStringKey.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attributedString.length))
self.attributedText = attributedString
}
}
Now call extension function
let label = UILabel()
let stringValue = "How to\ncontrol\nthe\nline spacing\nin UILabel"
// Pass value for any one argument - lineSpacing or lineHeightMultiple
label.setLineSpacing(lineSpacing: 2.0) . // try values 1.0 to 5.0
// or try lineHeightMultiple
//label.setLineSpacing(lineHeightMultiple = 2.0) // try values 0.5 to 2.0
Or using label instance (Just copy & execute this code to see result)
let label = UILabel()
let stringValue = "Set\nUILabel\nline\nspacing"
let attrString = NSMutableAttributedString(string: stringValue)
var style = NSMutableParagraphStyle()
style.lineSpacing = 24 // change line spacing between paragraph like 36 or 48
style.minimumLineHeight = 20 // change line spacing between each line like 30 or 40
// Line spacing attribute
attrString.addAttribute(NSAttributedStringKey.paragraphStyle, value: style, range: NSRange(location: 0, length: stringValue.characters.count))
// Character spacing attribute
attrString.addAttribute(NSAttributedStringKey.kern, value: 2, range: NSMakeRange(0, attrString.length))
label.attributedText = attrString
Swift 3
let label = UILabel()
let stringValue = "Set\nUILabel\nline\nspacing"
let attrString = NSMutableAttributedString(string: stringValue)
var style = NSMutableParagraphStyle()
style.lineSpacing = 24 // change line spacing between paragraph like 36 or 48
style.minimumLineHeight = 20 // change line spacing between each line like 30 or 40
attrString.addAttribute(NSParagraphStyleAttributeName, value: style, range: NSRange(location: 0, length: stringValue.characters.count))
label.attributedText = attrString
My solution was to patch the font file itself and fix its line height definitely.
http://mbauman.net/geek/2009/03/15/minor-truetype-font-editing-on-a-mac/
I had to modify 'lineGap', 'ascender', 'descender' in the 'hhea' block (as in the blog example).
This guy created a class to get line-height (without using CoreText, as MTLabel library) : https://github.com/LemonCake/MSLabel
Best thing I found is: https://github.com/mattt/TTTAttributedLabel
It's a UILabel subclass so you can just drop it in, and then to change the line height:
myLabel.lineHeightMultiple = 0.85;
myLabel.leading = 2;
I've found 3rd Party Libraries Like this one:
https://github.com/Tuszy/MTLabel
To be the easiest solution.
Here's some swift-code for you to set the line spacing programmatically
let label = UILabel()
let attributedText = NSMutableAttributedString(string: "Your string")
let paragraphStyle = NSMutableParagraphStyle()
//SET THIS:
paragraphStyle.lineSpacing = 4
//OR SET THIS:
paragraphStyle.lineHeightMultiple = 4
//Or set both :)
let range = NSMakeRange(0, attributedText.length)
attributedText.addAttributes([NSParagraphStyleAttributeName : paragraphStyle], range: range)
label.attributedText = attributedText
Of course, Mike's answer doesn't work if you pass the string programmatically. In this case you need to pass a attributed string and change it's style.
NSMutableAttributedString * attrString = [[NSMutableAttributedString alloc] initWithString:#"Your \nregular \nstring"];
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
[style setLineSpacing:4];
[attrString addAttribute:NSParagraphStyleAttributeName
value:style
range:NSMakeRange(0, attrString.length)];
_label.attributedText = attrString;