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].
Related
Attached is an image of what I'm trying to center and the attempts I have made using XCode 11.2.1
How do I center the 3 labels of different sizes (the data for the number will be dynamic also) horizontally in the view?
Things I have tried to do:
I tried putting a horizontal constraint on the LateBEDView (which contains the 3 labels), but when you view it on the Simulator or Actual Device it puts all the text to the right of the center. I have tried using spacers (empty views) on both sides but can't figure out what the settings should be? Any help is greatly appreciated!
Here's how I would do it. I'd make this one label, which after all is easily centered. Okay, I'll assume you know how to do that.
Then I'd use an attributed string to create the different parts of the string, including the "subscripting". So, first I'd extend the attributed string keys to include the three parts:
extension NSAttributedString.Key {
static let part1 = NSAttributedString.Key(rawValue: "part1")
static let part2 = NSAttributedString.Key(rawValue: "part2")
static let part3 = NSAttributedString.Key(rawValue: "part3")
}
Then I'd set the label's attributed text:
let mas = NSMutableAttributedString()
mas.append(NSAttributedString(string: "Late BED = ", attributes: [
.font:UIFont.systemFont(ofSize: 15),
.foregroundColor:UIColor.black,
.part1:"part1"
]))
mas.append(NSAttributedString(string: "20.0", attributes: [
.font:UIFont.systemFont(ofSize: 15),
.foregroundColor:UIColor.black,
.part2:"part2"
]))
mas.append(NSAttributedString(string: " Gy(3.0)", attributes: [
.font:UIFont.systemFont(ofSize: 9),
.foregroundColor:UIColor.black,
.baselineOffset:-10,
.part3:"part3"
]))
self.label.attributedText = mas
The result looks like what you've got, and of course you can tweak it as needed:
Okay, and here's the really clever part. Because I demarcated the three parts of the attributed string, I can find and change the text of any of them, at will. As you say, the data for each number needs to be able to change. That's why you used three labels! But now I'm showing how to do that with one label.
For example, let's say I want to change "20.0" to "30.4". That is your second label, and my .part2. Here's how you do it:
let s = self.label.attributedText
// skip past part 1 and find part 2
var r = NSRange()
let mas = s?.mutableCopy() as! NSMutableAttributedString
let _ = mas.attribute(.part2, at: 0, longestEffectiveRange: &r,
in: NSRange(location: 0, length: 100))
// find range of part 2
let _ = mas.attribute(.part2, at: r.length, longestEffectiveRange: &r,
in: NSRange(location: 0, length: 100))
mas.replaceCharacters(in: r, with: "30.4")
self.label.attributedText = mas
I have tried but it was not a long style shadow.
let myString = "Show Your Creativity"
myLabel.layer.masksToBounds = false
myLabel.layer.shouldRasterize = true
myLabel.layer.rasterizationScale = 10
// Create a shadow
let myShadow = NSShadow()
myShadow.shadowBlurRadius = 1
myShadow.shadowOffset = CGSize(width: 40, height: 40)
myShadow.shadowColor = UIColor.gray
// Create an attribute from the shadow
let myAttribute = [ NSAttributedStringKey.shadow: myShadow ]
// Add the attribute to the string
let myAttrString = NSAttributedString(string: myString, attributes: myAttribute)
// set the attributed text on a label
myLabel.attributedText = myAttrString // can also use with UITextView
Current result ...
I want an effect like given image.
The effect you want to reproduce is not made by adding a shadow to text - as a shadow will follow and extend the curves in the characters.
The image you have linked to seems to be using a linear gradient masked by the tops of the text.
Reproducing this programmatically would be easier done in Sketch/PaintCode using text outlines to form masks and the linear gradient, and then export the result to iOS-compliant code. This will give you resolution independence and a small file size.
Or ship the bitmap image from an illustration app.
Or do you need dynamic text?
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)
I'm using
titleLabel.Lines = 2;
titleLabel.LineBreakMode = UILineBreakMode.TailTruncation;
Now long text is broken by a ... in the end.
Now I would like to know if the titleLabel is tail truncated , contains "..." ? Any easy suggestions for this ?? as I cannot see the ... characters in the actual titleLabel.Text field
Your question is similar to Change the default '...' at the end of a text if the content of a UILabel doesn't fit
There is no direct option to access ellipsis(the three dot). You need to do it yourself. Code to count the size of your string, clip the string and add a ellipsis with the color you want when the string exceed the view.
Define a NSAttributesString
let atttext = NSAttributedString(string: text!, attributes: [NSForegroundColorAttributeName: UIColor.redColor()])
Calculate the size of the string
let bounds = atttext.boundingRectWithSize(label.bounds.size, options: [], context: nil)
Do something to the string when it exceed the view
if bounds.size.width > 10 {
//Do something here, like assign a new value to `attributedText` of label or change the color
label.attributedText = NSAttributedString(string: "Labelfdjkfdsjkfdsjkf...", attributes: [NSForegroundColorAttributeName: UIColor.blackColor()])
}
For more detail, you can have a look at the last answer of the question I mentioned above.
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])