How to add indents to Multiple lines of UILabel? - ios

While I found how to add indentations to first line (FirstLineHeadIndent) and to the rest of lines (HeadIndent), I cannot find how to add indents to only first two/three lines in order to achieve something like this:
PS: This is not a duplicate, because I'm not asking how to indent only first line, as one user suggested.

Using TextKit Framework in ios
CGRect checkBoxFrame = [self.textView convertRect:self.checkView.bounds fromView:self.checkView];
checkBoxFrame.origin.x -= self.textView.textContainerInset.left;
checkBoxFrame.origin.y -= self.textView.textContainerInset.top;
UIBezierPath *checkBoxPath = [UIBezierPath bezierPathWithOvalInRect:checkBoxFrame];
self.textView.textContainer.exclusionPaths = #[checkBoxPath];
It will exclude the image path inside content of TextView

You need to set your UILabel text as Attributed string in storyboard.
Then you can edit the indentation of each line, and you can also paste any text you've created with text editor and it will keep its indentation as well as other attributes.
You can of course manipulate these attributes programmatically, here is an example:
#IBOutlet weak var label: UILabel!
let text = "\tfirst line\n \tsecond line\nthird line\nforth line"
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.tabStops = [NSTextTab(textAlignment: NSTextAlignment.left, location: 15, options: [:])]
paragraphStyle.headIndent = 10
label.attributedText = NSAttributedString(string: text, attributes: [NSParagraphStyleAttributeName: paragraphStyle])
Here is an example of how to configure it:
Here is how to configure indentation:
Here is the example on the simulator:

You would need to use the stringattribute property of the UILabel.AttributedText.
its actually of type NSMutableAttributedString so I first casted
label.AttributedText to the mutable type then I could manipulate it.‡
To do this you would use the following:
var mutable = Control.AttributedText as NSMutableAttributedString;
UIStringAttributes uiString=new UIStringAttributes();
Then you need to set an indent on the first line (Do this the way you already know how to), and then you would set the headIndent of its paragraph style like below.
this is converted from objective-c so might not be perfect: ‡
NSMutableParagraphStyle paragraphStyle = new NSMutableParagraphStyle();
paragraphStyle.headIndent = 14;
NSDictionary attributes (){
StyleAttributeName = paragraphStyle;
};
mutable.AddAttribute(attributes);
Control.attributedText = mutable;
I believe something like this in combination with your 'FirstLineHeadIndent' code should do the trick.
‡ How to manipulate NSAttributedString
‡ Objective-C second line with indent

Related

iOS - image attachment for the text view changes attributes

I've a UITextView described as follows with the given attributes:
lazy var inputTextView: UITextView = {
let tv = UITextView()
tv.backgroundColor = .white
tv.textContainerInset = UIEdgeInsetsMake(12, 12, 12, 12) // Posicionamento do texto
let spacing = NSMutableParagraphStyle()
spacing.lineSpacing = 4
let attr = [NSParagraphStyleAttributeName : spacing, NSFontAttributeName: UIFont.systemFont(ofSize: 16), NSForegroundColorAttributeName: UIColor.blue]
tv.typingAttributes = attr
return tv
}()
Everything works as expected until I attach an image to the UITextView.
The image gets inserted in the desired position but after its inserted it overrides my textView attributes.
The text becomes small and in a different color than the attributes I've implemented in its declaration.
I'm attaching the image as follows:
let att = NSTextAttachment()
att.image = image
let attrString = NSAttributedString(attachment: att)
self.inputTextView.textStorage.insert(attrString, at: self.currentCursorLocation)
What's causing this issue?
I've even tried to reenforce its attributes whenever I insert an UIImage to its content.
I've tried the following when adding the image:
let att = NSTextAttachment()
att.image = image
let attrString = NSAttributedString(attachment: att)
self.inputTextView.textStorage.insert(attrString, at: self.currentCursorLocation)
let spacing = NSMutableParagraphStyle()
spacing.lineSpacing = 4
let attr = [NSParagraphStyleAttributeName : spacing, NSFontAttributeName: UIFont.systemFont(ofSize: 16), NSForegroundColorAttributeName: UIColor.blue]
self.inputTextView.typingAttributes = attr
And it still doesn't change its attributes.
Whats causing this issue? Any tip?
Thanks
Edit
As suggested here's how I'm setting the cursor position
func textViewDidChange(_ textView: UITextView) {
currentCursorLocation = textView.selectedRange.location
}
I do this to insert the image at the current location of the text blinking cursor
[Edit: Unfortunately this does not solve Ivan's problem - I leave the answer because it is interesting detail for those who do not understand Unicode character encoding].
String range specification is non-intuitive due to the subtleties of Unicode. I expect your issue is that the cursor position at which you are inserting your image is not where you think it is relative to the text and you are inserting the image at a Unicode scalar position that is not between Unicode code points, such that you are corrupting a unicode code. To understand why this can happen, see this Apple article.
Strings in Swift 2
I would suggest using the following notation when specifying string ranges (taken from this Stack Overflow answer: NSAttributedString and emojis: issue with positions and lengths).
// Convert to NSRange by computing the integer distances:
let nsRange = NSRange(location: text.utf16.distance(from: text.utf16.startIndex, to: from16),
length: text.utf16.distance(from: from16, to: to16))
However without seeing how you set your cursor position, it is not possible for me to be sure this is the source of your problem. [Update: thanks for updating the question to show the cursor position - we got there in the end but for others, note, after setting the cursor position this way (which would have been fine), he was incrementing it by 1, which meant the issue I have referred to about Unicode scalars versus code points was in fact the issue].

Multi-line editable piece of text: editable UILabel?

I am trying to create a large plot of editable text but there seems to be 1 option: using a small UITextField.
I know UILabels can be big and wide but I do not know how to make an editable UILabel.
I experimented with UILabel properties and the .layer method but nothing seems to be really working. Anybody have a recommendation as to what I should do?
To summarize, I am looking for a multi-line editable piece of text.
UITextView for the win!!
UITextViews allow for multiple line manipulation of texts and if you use the UITextViewDelegate, it can provide for methods that allow specific things when the textView is clicked on, etc...!
With a UITextView you can provide a specific amount of lines (if you only want 3, you can specify it) and also provide hyperlinks, if need be.
Here is an example I have (changed a little) to show an example for ya...
let textBox:UITextView = UITextView(frame: CGRect(x: firstBox.frame.width*0, y: firstBox.frame.height*0.375, width: firstBox.frame.width*1, height: firstBox.frame.height*0.5))
textBox.backgroundColor = UIColor.clearColor()
let websiteName = "http://stackoverflow.com/posts/38035564"
textBox.text = "SO is an awesome coding site! Please visit\n\(websiteName)"
//No need to set number of lines, it will auto set to as many as needed!
textBox.editable = false
textBox.selectable = true
//Register the hyperlink
textBox.dataDetectorTypes = UIDataDetectorTypes.All
textBox.textColor = UIColor.grayColor()
//Change only the hyperlink part
let textRange = NSMakeRange(textBox.text.characters.count-websiteName.characters.count, websiteName.characters.count)
let style = NSMutableParagraphStyle()
style.alignment = NSTextAlignment.Center
let attributedText = NSMutableAttributedString(string: textBox.text, attributes: [NSFontAttributeName:UIFont(
name: (textBox.font?.fontName)!,
size:13/15*fontSize)!,
NSParagraphStyleAttributeName: style])
attributedText.addAttribute(NSUnderlineStyleAttributeName , value:NSUnderlineStyle.StyleSingle.rawValue, range: textRange)
textBox.attributedText = attributedText
firstBox.addSubview(textBox)

UITextView attributed Alignment and size

First assume math is correct.
let textViewText = NSMutableAttributedString(string: "34\n+ 10\n+ 32344\n= 23424")
Im using a Textview to display input from the user. To make it easier to read I'm trying to get the text format like this
34
+ 10
+ 32344
= 23424
The other issue I'm having is with wrapping. Is there a way to resize each line to fit on its line?
34
= 23424
4356356
Is your text dynamic or static? If static, then all you need to do is to put the correct amount of spacing between your numbers and plus signs and then right justify the text.
self.textView.attributedText = NSMutableAttributedString(string: "34\n+ 10\n+ 32344\n= 23424")
self.textView.textAlignment = NSTextAlignment.Right
Result:
You can accomplish this by using a right-aligned tab stop in your paragraph style, and separating your operators and values with a tab.
let string = "\t34\n+\t10\n+\t32344\n=\t23424"
let paragraph = NSMutableParagraphStyle()
paragraph.tabStops = [NSTextTab(textAlignment: .Right, location: 200, options: [:])]
let attributedString = NSAttributedString(string: string, attributes:[NSParagraphStyleAttributeName: paragraph])

Change paragraph height (not line spacing) in a UILabel

Is it possible to limit the distance between paragraphs that were created using \n\n smaller in UILabels using attributed strings?
So for example, I would like this:
To look like this:
Would this involve replace \n\n with something else? Or is there a much simpler solution using NSAttributedString?
First at all: The usage of \n\n to create distances between two paragraphs is no good idea at all. \n has the semantic meaning of a new paragraph, so you have three paragraphs, where two are semantically meant. This is like a amateurish secretary deals with paragraph distances. You should replace them with a single \n.
However, you should not use font sizes to adjust line spacing or paragraph spacing. This highly relies on the shape of a font and its definition. Things break fast.
Add a paragraph style, because they are built for paragraph spacing. Set the line height or paragraph spacing properties.
The solution I outlined in my comment works. You can set the font size of the the empty line / paragraph spacing as something that pleases your eyes:
[myAttributedString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:5.0] range:NSMakeRange(6, 1)];
The following code finds all occurrences of \n\n and specify the second one to have a specific size:
unsigned long length = myAttributedString.length;
NSRange range = NSMakeRange(0, length);
NSRange found;
while (NSNotFound != (found =[myAttributedString.string rangeOfString:#"\n\n" options:0 range:range]).location) {
[myAttributedString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:8.0] range:NSMakeRange(found.location + 1, 1)];
range = NSMakeRange(found.location + 2, length - found.location - 2);
}
One thing I didn't mention in the question, which I had thought was obvious from the example is that the description is not within my control, it is generated by users. Therefore, the carriage return characters are added by them when they are creating the text.
So the solution I came up with is the following:
Firstly, I replace any \n\n characters with a single carriage return. This was inspired by amin-negm-awad's answer. \n\n is not a desirable way to generate a paragraph space.
I am doing this using the following piece of code:
func sanitize() -> String {
var output = NSMutableString(string: self)
var numberOfReplacements = 0
do {
let range = NSMakeRange(0, output.length)
numberOfReplacements = newString.replaceOccurrencesOfString("\n\n", withString: "\n", options: NSStringCompareOptions.CaseInsensitiveSearch, range: range)
} while (numberOfReplacements > 0)
return output as String
}
The next part is to apply a paragraph style with an attributed string. Here is an example function that is fairly flexible:
func textAttributesWithFont(font: UIFont, andColor color: UIColor,
lineSpacing: CGFloat = 0,
maximumLineHeight: CGFloat = 0,
textAlignment: NSTextAlignment = .Natural) -> [NSObject: AnyObject] {
var attributes = [NSFontAttributeName : font, NSForegroundColorAttributeName : color]
var paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = lineSpacing
paragraphStyle.alignment = textAlignment
paragraphStyle.maximumLineHeight = maximumLineHeight
paragraphStyle.paragraphSpacing = 4
attributes[NSParagraphStyleAttributeName] = paragraphStyle
return attributes
}
Finally the label is constructed using the attributes:
var label1 = UILabel()
let text1 = "This is a test that is supposed😓😛😠😑 to wrap with some paragaphs\n\nThis is a paragraph"
label1.attributedText = NSAttributedString(string:sanitizeComment(text1), attributes: attributes)
label1.numberOfLines = 0

UILabel Line Spacing

I am trying to set the line spacing in a UILabel as specified by Mike Slutsky here. It works correctly for the text I specify from the Storyboard. When I try to set the UILabel.text in code, it reverts back to the default line spacing. Can someone help me understand how to either:
Keep it from reverting to default, and use the settings I specified on the Storyboard or
Set the value in code.
I've seen a lot of examples around using NSAttribute and NSParagraph, but since it's now possible to set in the Storyboard, I would expect their may be a more straightforward answer. Many thanks for the help!
I set the "Height Multiple" as illustrated in the above link, and my only code is as follows:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var textView: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
textView.text = "Some example text"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
If I remove the textView.text line, it displays correctly, otherwise it's set back to the default line spacing (or Height Multiple).
Here's what's going on. You set things up in the storyboard with custom line spacing. This means, even though you may not know it, that in the storyboard you have set the label's attributedText.
But if you then come along and set the label's text in code, as you are doing, you throw away the attributedText and therefore all the attributes that it had. That is why, as you rightly say, things revert to the default look of the label.
The solution is: instead of setting the label's text, set its attributedText. In particular, fetch the label's existing attributedText; assign it into an NSMutableAttributedString so you can change it; replace its string while keeping the attributes; and assign it back to the label's attributedText.
So for example (I have called my label lab - your textView is a bad choice, as a text view is whole different animal):
let text = self.lab.attributedText
let mas = NSMutableAttributedString(attributedString:text)
mas.replaceCharactersInRange(NSMakeRange(0, count(mas.string.utf16)),
withString: "Little poltergeists make up the principle form of material manifestation")
self.lab.attributedText = mas
UILabel has
#property(nonatomic, copy) NSAttributedString *attributedText
since iOS 6.0,
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.
If you set textView.text = "Some example text" again, you will loose your attributes. You should only pick one of them and not switching between them if you are sure what you are doing
Here is #matt 's answer in Obj. C:
NSMutableAttributedString *mas = [self.lab.attributedText mutableCopy];
[mas replaceCharactersInRange:NSMakeRange(0, [mas.string length])
withString:#"Little poltergeists make up the principle form of material manifestation"];
self.lab.attributedText = mas;
Hope this helps someone!
Swift 3
Just copy & execute this code to see result
let label = UILabel()
let stringValue = "UILabel\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

Resources