Attributed text with two text alignments - ios

Does anyone know how to achieve two different text alignments in one string?
This is what I want the textView to show:
label value
My code:
let txtView = cell.viewWithTag(77) as! UITextView
let leftStyle = NSMutableParagraphStyle()
leftStyle.alignment = NSTextAlignment.Left
let rightStyle = NSMutableParagraphStyle()
rightStyle.alignment = NSTextAlignment.Right
let attText = NSMutableAttributedString(string: "label", attributes: [NSParagraphStyleAttributeName: leftStyle])
attText.appendAttributedString(NSAttributedString(string: " "))
attText.appendAttributedString(NSAttributedString(string: "value", attributes: [NSParagraphStyleAttributeName: rightStyle]))
txtView.attributedText = attText
What I get instead:
label value

Using NSMutableParagraphStyle with NSTextTab:
let paragraph = NSMutableParagraphStyle()
paragraph.tabStops = [
NSTextTab(textAlignment: .Right, location: 100, options: [:]),
]
let str = "Label\tValue\n"
+ "foo\tbar\n"
let attributed = NSAttributedString(
string: str,
attributes: [NSParagraphStyleAttributeName: paragraph]
)
let view = UITextView(frame: CGRectMake(0, 0, 120, 120))
view.textContainer.lineFragmentPadding = 10
view.attributedText = attributed
Of course, this aligns to "tabstop", but not to the edge of UITextView. When you modify the size of the view, you have to also modify the location of NSTextTab.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
let cell = Placestableview.dequeueReusableCell(withIdentifier: "cell", for: indexPath);
//first text
let attributestitle = [NSAttributedStringKey.font:
UIFont(name: "Helvetica-Bold", size: 15.0)!,
NSAttributedStringKey.foregroundColor: UIColor.black] as [NSAttributedStringKey: Any]
//second text
let attributedString = NSMutableAttributedString(string: "\t"+String(places[indexPath.row].distance!)+" miles", attributes: [NSAttributedStringKey.font: UIFont(name: "Helvetica-Bold", size: 8.0)!,NSAttributedStringKey.foregroundColor: UIColor.black])
let myParagraphStyle = NSMutableParagraphStyle()
myParagraphStyle.alignment = .right
myParagraphStyle.tabStops = [
NSTextTab(textAlignment: .right, location: 300, options: [:]),
]
let attributedStringtitle = NSMutableAttributedString(string: places[indexPath.row].title!, attributes: attributestitle)
//adding the right alignment to the second text alone
attributedString.addAttributes([.paragraphStyle: myParagraphStyle], range: NSRange(location: 0, length: attributedString.length))
//combining two texts with different alignments.
let combination = NSMutableAttributedString()
combination.append(attributedStringtitle)
combination.append(attributedString)
cell.textLabel?.attributedText = combination
return cell;
}

Related

Make half of the TextView's text color Different than other 50% of the text SWIFT

I've got a large text in my UITextView and I want to make the 50% of the text's color red and the other 50% green . I've added NSMutableAttributedString in the TextView but it works's for the full range of the text . How to divide the textView's text into two sections and color them into red and green ?
let strNumber: NSString = self.text as NSString // TextView Text
let range = (strNumber).range(of: strNumber as String)
let attribute = NSMutableAttributedString.init(string: strNumber as String)
attribute.addAttributes([NSAttributedString.Key.font : UIFont.systemFont(ofSize: 14) , NSAttributedString.Key.foregroundColor : UIColor.red], range: range)
self.attributedText = attribute
It seems you have an extension to UITextView. The following extension function will make the existing attributed text of a text view be half red and half green. All other existing attributes, if any, will remain.
extension UITextView {
func makeHalfRedGreen() {
if let text = self.text {
let half = text.count / 2
let halfIndex = text.index(text.startIndex, offsetBy: half)
let firstRange = NSRange(..<halfIndex, in: text)
let secondRange = NSRange(halfIndex..., in: text)
let attrTxt = NSMutableAttributedString(attributedString: attributedText)
attrTxt.addAttribute(.foregroundColor, value: UIColor.red, range: firstRange)
attrTxt.addAttribute(.foregroundColor, value: UIColor.green, range: secondRange)
attributedText = attrTxt
}
}
}
Try to use function like below
text_lbl.attributedText = self.decorateText(txt1: "Red Color", txt2: “Blue Color”)
func decorateText(txt1:String, txt2:String)->NSAttributedString{
let textAttributesOne = [NSAttributedStringKey.foregroundColor: UIColor.red, NSAttributedStringKey.font: UIFont(name: "Poppins-Regular", size: 12.0)!] as [NSAttributedStringKey : Any]
let textAttributesTwo = [NSAttributedStringKey.foregroundColor: UIColor.blue, NSAttributedStringKey.font: UIFont(name: "Poppins-SemiBold", size: 14.0)!] as [NSAttributedStringKey : Any]
let textPartOne = NSMutableAttributedString(string: txt1, attributes: textAttributesOne)
let textPartTwo = NSMutableAttributedString(string: txt2, attributes: textAttributesTwo)
let textCombination = NSMutableAttributedString()
textCombination.append(textPartOne)
textCombination.append(textPartTwo)
return textCombination
}

Wrong height for UILabel when using custom lineSpacing and kern

I am getting wrong height for an UILabel if I use NSAttributedString that has custom kern and lineSpacing.
Here is how I set the custom kern and line spacing:
override func viewDidLoad() {
super.viewDidLoad()
let shortText = "Single line"
self.label.attributedText = self.getAttributedText(text: shortText, kern: 0.2, lineSpacing: 8)
self.label2.attributedText = self.getAttributedText(text: shortText, kern: 0, lineSpacing: 8)
}
private func getAttributedText(text: String, kern: CGFloat, lineSpacing: CGFloat) -> NSAttributedString {
let attributedString = NSMutableAttributedString(string: text)
let style = NSMutableParagraphStyle()
style.lineSpacing = lineSpacing
let attributes: [NSAttributedStringKey : Any] =
[.paragraphStyle : style,
.kern: kern]
attributedString.addAttributes(attributes,
range: NSMakeRange(0, attributedString.length))
return attributedString
}
And here is what I get:
The first label (the one that has custom kern), has its height wrong. It's exactly 8 points taller than it should be - that's the custom line height that I am using.
This only happens for single line labels. If I use text that is on a couple of lines, it works as expected.
This is a bug with NSAttributedStringKey.kern. As a workaround, you can calculate the number of lines of your UILabel with the suggestions in this answer. If it has one line only, set lineSpacing to 0.
private func getAttributedText(text: String, kern: CGFloat, lineSpacing: CGFloat) -> NSAttributedString {
let attributedString = NSMutableAttributedString(string: text)
let font = UIFont.systemFont(ofSize: 16)
let attributes: [NSAttributedStringKey : Any] = [.kern: kern,
.font: font]
attributedString.addAttributes(attributes, range: NSMakeRange(0, attributedString.length))
let maxSize = CGSize(width: [custom width], height: CGFloat.greatestFiniteMagnitude)
let sizeOfLabel = attributedString.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, context: nil)
if sizeOfLabel.height > font.lineHeight {
let style = NSMutableParagraphStyle()
style.lineSpacing = lineSpacing
attributedString.addAttribute(.paragraphStyle, value: style, range: NSMakeRange(0, attributedString.length))
}
return attributedString
}

Strikethrough UITableView cell text after cell containing String

I'd like to strikethrough all cell text in UITableView after a specific line of text.
Example (after "+")
I think I'm close, but the strikethrough isn't applied in the Simulator. (build is successful)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "Cell")
if indexPath.row > dailyTasks.index(of: "+")! {
let attributeString: NSMutableAttributedString = NSMutableAttributedString(string: dailyTasks[indexPath.row])
attributeString.addAttribute(NSAttributedStringKey.strikethroughStyle, value: 1, range: NSMakeRange(0, attributeString.length))
cell.textLabel?.attributedText = attributeString
} else {
cell.textLabel?.text = dailyTasks[indexPath.row]
}
cell.textLabel?.font = UIFont(name: "GillSans-Light", size: 17)
cell.indentationLevel = 1
cell.textLabel?.addCharactersSpacing(1.2)
cell.backgroundColor = UIColor(red:0.99, green:0.97, blue:0.91, alpha:1.0)
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
}
Don't set the font on the label if you are using an attributed string; instead set the font as an attribute. Here is a playground that shows how to do it:
import UIKit
import PlaygroundSupport
let label = UILabel()
let text = "sample text"
let view = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
let attributes: [NSAttributedStringKey: Any] =
[NSAttributedStringKey.font: UIFont(name: "GillSans-Light", size: 17)!,
NSAttributedStringKey.foregroundColor : UIColor.white,
NSAttributedStringKey.strikethroughStyle: 1]
label.attributedText = NSAttributedString(string: text, attributes: attributes)
view.addSubview(label)
label.sizeToFit()
PlaygroundPage.current.liveView = view

why multiline attributedString UITextView has a different line height?

I get different line height in textView with using same font
How to set fixed line height?
I have done a lot of attempts, any help is appreciated, thanks
set NSMutableParagraphStyle lineSpacing is useless
set lineHeightMultiple is to make the difference more obvious
[
demo
import UIKit
import PlaygroundSupport
let view = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 1000))
let data: [String] = [
"商品名称: 巧克力",
"商品名称: 巧克力",
"商品名称: 巧克力",
"注册未成功,请验证电子邮件",
"注册未成功,请验证电子邮件",
"注册未成功,请验证电子邮件",
"测试文字, 测试文字,测试文字",
"测试文字, 测试文字,测试文字",
"测试文字, 测试文字,测试文字",
]
let textView = UITextView(frame: view.frame)
let paragraphStyle = NSMutableParagraphStyle()
let bodyFont = UIFont.systemFont(ofSize: 20.0)
paragraphStyle.lineHeightMultiple = 4
var stripe = false
// attributedString
let mutableAttributedString = NSMutableAttributedString(string: "Test TextViewAttributedString\n", attributes: [
NSFontAttributeName: UIFont.systemFont(ofSize: 18.0)
])
for text: String in data {
var backgroundColor = UIColor(red:0.13, green:0.38, blue:0.95, alpha:1.00)
if stripe {
backgroundColor = UIColor(red:0.92, green:0.12, blue:0.38, alpha:1.00)
}
let contentAttributedString = NSAttributedString(string: text, attributes: [
NSBackgroundColorAttributeName: backgroundColor,
NSParagraphStyleAttributeName: paragraphStyle,
NSFontAttributeName: bodyFont
])
mutableAttributedString.append(contentAttributedString)
stripe = !stripe
// add newline character
let newlineAttributedString = NSAttributedString(string: "\n")
mutableAttributedString.append(newlineAttributedString)
}
textView.attributedText = mutableAttributedString
view.addSubview(textView)
PlaygroundPage.current.liveView = view
I found the reason, the newlineAttributedString also need NSFontAttributeName
let newlineAttributedString = NSAttributedString(string: "\n", attributes: [
NSFontAttributeName: bodyFont
])
mutableAttributedString.appendAttributedString(newlineAttributedString)

NSTextAttachment image alignment

I am following this cool tutorial Implementing Rich Text with Images on OS X and iOS by #Duncan Groenewald and was able to display images in my UITextView. However, these images are not centered they way I would like them to be. See image
As you can see, I would like my image to be centered on the X-axis.
I tried returning the rect with appropriate values in -attachmentBoundsForTextContainer:proposedLineFragment:glyphPosition:characterIndex but that did not help.
I also tried setting the NSKernAttributeName for the NSTextAttachment attributed string. But all it did was hide the image some how.
Try setting the paragraph style on your attachment with a center alignment.
If your images are embedded in an attributed string as attachments, you can access them by enumerating through the attributed string's attachment attributes.
For example:
attributedContent.enumerateAttribute(NSAttachmentAttributeName, inRange: NSRange(location: 0, length: attributedContent.length), options: nil) { (attribute, range, stop) -> Void in
if let attachment = attribute as? NSTextAttachment {
// this example assumes you want to center all attachments. You can provide additional logic here. For example, check for attachment.image.
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .Center
attributedContent.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: range)
}
}
This is Swift 3.1 using extension:
extension NSMutableAttributedString {
func setAttachmentsAlignment(_ alignment: NSTextAlignment) {
self.enumerateAttribute(NSAttachmentAttributeName, in: NSRange(location: 0, length: self.length), options: .longestEffectiveRangeNotRequired) { (attribute, range, stop) -> Void in
if attribute is NSTextAttachment {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = alignment
self.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: range)
}
}
}
}
In this way you can you easily apply alignment for attachments on attributed string:
let attributeString = NSMutableAttributedString(string: "")
// add attachments
attributeString.setAttachmentsAlignment(.center)
Here's another way how to set the alignment for an NSTextAttachment image. Hopefully this will also help someone struggling with this. I'm using the code below in a func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
var buttonText = "My Button";
let align = NSMutableParagraphStyle();
align.alignment = NSTextAlignment.center;
align.firstLineHeadIndent = 10.0;
align.headIndent = 10.0;
align.tailIndent = -10.0;
let para = NSMutableAttributedString();
// top padding
para.append(NSAttributedString(string: "\n", attributes: [NSParagraphStyleAttributeName: align, NSFontAttributeName: UIFont(name: "Helvetica", size: 10.0)!, NSForegroundColorAttributeName: UIColor.white]));
// image
let img = NSTextAttachment();
img.image = UIImage(named: "MyIcon");
img.bounds = CGRect(x: 0, y: UIFont(name: "Helvetica", size: 18.0)!.descender, width: img.image!.size.width, height: img.image!.size.height);
let nas = NSAttributedString(attachment: img).mutableCopy() as! NSMutableAttributedString;
nas.addAttribute(NSParagraphStyleAttributeName, value: align, range: NSRange(location: 0, length: nas.length));
para.append(nas);
// space to text
buttonText = " " + buttonText;
// text
para.append(NSAttributedString(
string: buttonText,
attributes: [NSParagraphStyleAttributeName: align, NSFontAttributeName: UIFont(name: "Helvetica", size: 18.0)!, NSForegroundColorAttributeName: UIColor.black]));
// bottom padding
para.append(NSAttributedString(string: "\n", attributes: [NSParagraphStyleAttributeName: align, NSFontAttributeName: UIFont(name: "Helvetica", size: 10.0)!, NSForegroundColorAttributeName: UIColor.white]));
// set cell label
let label = cell.textLabel!;
label.numberOfLines = 0;
label.layer.borderWidth = 0;
label.layer.masksToBounds = false;
label.backgroundColor = UIColor.clear;
label.layer.backgroundColor = UIColor.green;
label.attributedText = para;

Resources