Based on the answers here I made this code:
extension NSMutableAttributedString {
func bold(text:String, size:CGFloat) -> NSMutableAttributedString {
let attrs:[String:AnyObject] = [NSFontAttributeName : UIFont.boldSystemFontOfSize(size)]
let boldString = NSMutableAttributedString(string:"\(text)", attributes:attrs)
self.appendAttributedString(boldString)
return self
}
func normal(text:String)->NSMutableAttributedString {
let normal = NSAttributedString(string: text)
self.appendAttributedString(normal)
return self
}
}
and I use it like this:
#IBOutlet weak var m_field: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
let string = NSMutableAttributedString()
string.bold("Bold_text: ",size: 12).normal("normal text")
m_field.attributedText = string
}
but it doesn't work, all my text is the same (bold I think)
what am I doing wrong?
Enjoy - below code works for swift 3
let normalText = "Hi am normal"
let boldText = "And I am BOLD!"
let attributedString = NSMutableAttributedString(string:normalText)
let attrs = [NSFontAttributeName : UIFont.boldSystemFont(ofSize: 15)]
let boldString = NSMutableAttributedString(string:boldText, attributes:attrs)
attributedString.append(boldString)
txt.attributedText = attributedString
where txt is TextField outlet
assign "**string**" variable value to **attributedText** property
var m_field = UITextField()
m_field.frame = CGRect(x: 0, y: 66, width: 400, height: 100)
let string = NSMutableAttributedString()
string.bold("Bold_text: ",size: 15).normal("normal text")
m_field.attributedText = string
self.view .addSubview(m_field)
Related
I'm trying to upload a child nod to a reference to my Firebase Realtime Database, however I'm running into an issue. I'm trying to have my secondDateAndTimes and thirdDateAndTimes upload as empty strings if they are not filled out, but my handleConfirm() will only work if the two are filled out. What do I need to do so the child node uploads even if the two UILabels are empty/not showing? Thank you!
JobRequest Model
struct JobRequest {
let uid: String
let fullname: String
let username: String
let address: String
let firstDateAndTimes: String
let secondDateAndTimes: String
let thirdDateAndTimes: String
let timeConstraints: String
let jobDescription: String
init(dictionary: [String : Any]) {
self.uid = dictionary["uid"] as? String ?? ""
self.fullname = dictionary["fullname"] as? String ?? ""
self.username = dictionary["username"] as? String ?? ""
self.address = dictionary["address"] as? String ?? ""
self.firstDateAndTimes = dictionary["firstDateAndTimes"] as? String ?? ""
self.secondDateAndTimes = dictionary["secondDateAndTimes"] as? String ?? ""
self.thirdDateAndTimes = dictionary["thirdDateAndTimes"] as? String ?? ""
self.timeConstraints = dictionary["timeConstraints"] as? String ?? ""
self.jobDescription = dictionary["jobDescription"] as? String ?? ""
}
}
API Service
struct JobDetailCredentials {
var uid: String
var fullname: String
var username: String
var address: String
var firstDateAndTimes: String
var secondDateAndTimes: String
var thirdDateAndTimes: String
var timeConstraints: String
var jobDescription: String
}
// MARK: - CustomerService
struct CustomerService {
static func uploadCustomerAndJobRequest(jobDetails: JobDetailCredentials, completion: #escaping(DatabaseCompletion)) {
guard let uid = Auth.auth().currentUser?.uid else { return }
let values = ["uid" : jobDetails.uid,
"fullname" : jobDetails.fullname,
"username" : jobDetails.username,
"address" : jobDetails.address,
"firstDateAndTimes" : jobDetails.firstDateAndTimes,
"secondDateAndTimes" : jobDetails.secondDateAndTimes,
"thirdDateAndTimes" : jobDetails.thirdDateAndTimes,
"timeConstraints" : jobDetails.timeConstraints,
"jobDescription" : jobDetails.jobDescription] as [String : Any]
REF_CUSTOMERS.child(uid).child("job-request-details").childByAutoId().updateChildValues(values, withCompletionBlock: completion)
}
}
RequestJobConfirmationController
lazy var secondDateAndTimeDetailsLabel: UILabel = {
let label = UILabel()
label.textColor = .black
label.textAlignment = .center
label.font = UIFont(name: "AvenirNext-MediumItalic", size: 14)
label.numberOfLines = 0
return label
}()
lazy var thirdDateAndTimeDetailsLabel: UILabel = {
let label = UILabel()
label.textColor = .black
label.textAlignment = .center
label.font = UIFont(name: "AvenirNext-MediumItalic", size: 14)
label.numberOfLines = 0
return label
}()
#objc func handleConfirm() {
guard let uid = Auth.auth().currentUser?.uid ,
let fullname = fullnameDetailsLabel.text ,
let username = customer?.username ,
let address = addressDetailsLabel.text ,
let firstDateAndTimes = firstDateAndTimeDetailsLabel.text ,
let secondDateAndTimes = secondDateAndTimeDetailsLabel.text ,
let thirdDateAndTimes = thirdDateAndTimeDetailsLabel.text ,
let timeConstraints = timeConstraintsDetailsLabel.text ,
let jobDescription = jobDescriptionDetailsLabel.text else { return }
let jobDetails = JobDetailCredentials(uid: uid, fullname: fullname, username: username, address: address,
firstDateAndTimes: firstDateAndTimes, secondDateAndTimes: secondDateAndTimes,
thirdDateAndTimes: thirdDateAndTimes, timeConstraints: timeConstraints,
jobDescription: jobDescription)
CustomerService.uploadCustomerAndJobRequest(jobDetails: jobDetails) { error, ref in
if let _ = error {
self.simpleAlert(title: "Error", msg: "Unable to upload job details, please try again later.")
return
}
let controller = FindEmployeeJobRequestController()
controller.modalPresentationStyle = .fullScreen
self.present(controller, animated: true, completion: nil)
}
}
Instead of putting your secondDateAndTimes and thirdDateAndTimes inside the guard, which will guarantee that the function returns if they're nil, move them outside of that guard and optionally bind them:
guard let uid = Auth.auth().currentUser?.uid ,
let fullname = fullnameDetailsLabel.text ,
let username = customer?.username ,
let address = addressDetailsLabel.text ,
let firstDateAndTimes = firstDateAndTimeDetailsLabel.text ,
let timeConstraints = timeConstraintsDetailsLabel.text ,
let jobDescription = jobDescriptionDetailsLabel.text else { return }
let secondDateAndTimes = secondDateAndTimeDetailsLabel.text ?? "",
let thirdDateAndTimes = thirdDateAndTimeDetailsLabel.text ?? "",
//...
I figured what my problem was, I wasn't setting empty strings as the texts for my UILabels.
RequestJobConfirmationController
lazy var secondDateAndTimeDetailsLabel: UILabel = {
let label = UILabel()
label.text = ""
label.textColor = .black
label.textAlignment = .center
label.font = UIFont(name: "AvenirNext-MediumItalic", size: 14)
label.numberOfLines = 0
return label
}()
lazy var thirdDateAndTimeDetailsLabel: UILabel = {
let label = UILabel()
label.text = ""
label.textColor = .black
label.textAlignment = .center
label.font = UIFont(name: "AvenirNext-MediumItalic", size: 14)
label.numberOfLines = 0
return label
}()
I want to have one string with different paragraphs styles. The goal is to customize the paragraph/line spacing for different parts of the string. I researched and found this answer but since I added multiple new line characters, not sure how to implement.
Design
This is my goal in terms of layout:
Code
This is the code I have which makes it look like the left image above. Please see the comments Not working in the code. Notice how the spacing is set for the main string, but the other strings can't then set their own custom spacing:
struct BookModel: Codable {
let main: String
let detail: String
}
func createAttributedString(for model: BookModel) -> NSMutableAttributedString {
let fullString = NSMutableAttributedString()
let mainString = NSMutableAttributedString(string: model.main)
let mainStringParagraphStyle = NSMutableParagraphStyle()
mainStringParagraphStyle.alignment = .center
mainStringParagraphStyle.lineSpacing = 10
mainStringParagraphStyle.paragraphSpacing = 30
let mainStringAttributes: [NSAttributedString.Key: Any] = [.paragraphStyle: mainStringParagraphStyle]
let spacingAfterQuote = NSMutableAttributedString(string: "\n")
let lineImageAttachment = NSTextAttachment(image: #imageLiteral(resourceName: "line-image"))
let lineImageString = NSMutableAttributedString(attachment: lineImageAttachment)
let lineParagraphStyle = NSMutableParagraphStyle()
lineParagraphStyle.alignment = .left
lineParagraphStyle.lineSpacing = 0 // Not working - instead of 0 it is 30 from `mainStringParagraphStyle`
lineParagraphStyle.paragraphSpacing = 0 // Not working - instead of 0 it is 30 from `mainStringParagraphStyle`
let lineAttributes: [NSAttributedString.Key: Any] = [.paragraphStyle: lineParagraphStyle]
let spacingAfterSeparator = NSMutableAttributedString(string: "\n")
let spacingAfterSeparatorParagraphStyle = NSMutableParagraphStyle()
spacingAfterSeparatorParagraphStyle.alignment = .left
spacingAfterSeparatorParagraphStyle.lineSpacing = 0 // Not working - instead of 0 it is 30 from `mainStringParagraphStyle`
spacingAfterSeparatorParagraphStyle.paragraphSpacing = 5 // Not working - instead of 5 it is 30 from `mainStringParagraphStyle`
let spacingAfterSeparatorAttributes: [NSAttributedString.Key: Any] = [.paragraphStyle: spacingAfterSeparatorParagraphStyle]
let detailString = NSMutableAttributedString(string: model.detail)
let detailStringAttributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 20)]
fullString.append(mainString)
fullString.append(spacingAfterQuote)
fullString.append(lineImageString)
fullString.append(spacingAfterSeparator)
fullString.append(detailString)
fullString.addAttributes(mainStringAttributes, range: fullString.mutableString.range(of: model.main))
fullString.addAttributes(lineAttributes, range: fullString.mutableString.range(of: lineImageString.string))
fullString.addAttributes(spacingAfterSeparatorAttributes, range: fullString.mutableString.range(of: spacingAfterSeparator.string))
fullString.addAttributes(detailStringAttributes, range: fullString.mutableString.range(of: model.detail))
return fullString
}
Any thoughts on how to achieve the image on the right?
Question Update 1
The code below is working! There is only one slight problem. When I add lineSpacing, there is extra space at the end of the last line in main string. Notice that I have this set to zero: mainStringParagraphStyle.paragraphSpacing = 0, but there is still space at the end because mainStringParagraphStyle.lineSpacing = 60.
The reason I ask this is to have more fine grain control of spacing. For example, have a perfect number between the line image and main string. Any thoughts on this?
I put code and picture below:
Code:
func createAttributedString(for model: BookModel) -> NSMutableAttributedString {
let fullString = NSMutableAttributedString()
let mainStringParagraphStyle = NSMutableParagraphStyle()
mainStringParagraphStyle.alignment = .center
mainStringParagraphStyle.paragraphSpacing = 0 // The space after the end of the paragraph
mainStringParagraphStyle.lineSpacing = 60 // NOTE: This controls the spacing after the last line instead of just `paragraphSpacing`
let mainString = NSAttributedString(string: "\(model.main)\n",
attributes: [.paragraphStyle: mainStringParagraphStyle, .font: UIFont.systemFont(ofSize: 24)])
let lineImageStringParagraphStyle = NSMutableParagraphStyle()
lineImageStringParagraphStyle.alignment = .center
let lineImageAttachment = NSTextAttachment(image: #imageLiteral(resourceName: "line-view"))
let lineImageString = NSMutableAttributedString(attachment: lineImageAttachment)
lineImageString.addAttribute(.paragraphStyle, value: lineImageStringParagraphStyle, range: NSRange(location: 0, length: lineImageString.length))
let detailStringParagraphStyle = NSMutableParagraphStyle()
detailStringParagraphStyle.alignment = .center
detailStringParagraphStyle.paragraphSpacingBefore = 5 // The distance between the paragraph’s top and the beginning of its text content
detailStringParagraphStyle.lineSpacing = 0
let detailString = NSAttributedString(string: "\n\(model.detail)",
attributes: [.paragraphStyle: detailStringParagraphStyle, .font: UIFont.systemFont(ofSize: 12)])
fullString.append(mainString)
fullString.append(lineImageString)
fullString.append(detailString)
return fullString
}
Updated answer:
Here's a new example. I set the spacing at the top and at the bottom of the paragraph with the image. This allows line breaks to be used in model.main and model.detail if needed. Also, instead of lineSpacing, I used lineHeightMultiple. This parameter affects the indentation between lines without affecting the last line:
func createAttributedString(for model: BookModel) -> NSAttributedString {
let fullString = NSMutableAttributedString()
let mainStringParagraphStyle = NSMutableParagraphStyle()
mainStringParagraphStyle.alignment = .center
mainStringParagraphStyle.lineHeightMultiple = 2 // Note that this is a multiplier, not a value in points
let mainString = NSAttributedString(string: "\(model.main)\n", attributes: [.paragraphStyle: mainStringParagraphStyle, .font: UIFont.systemFont(ofSize: 24)])
let lineImageStringParagraphStyle = NSMutableParagraphStyle()
lineImageStringParagraphStyle.alignment = .center
lineImageStringParagraphStyle.paragraphSpacingBefore = 10 // The space before image
lineImageStringParagraphStyle.paragraphSpacing = 20 // The space after image
let lineImageAttachment = NSTextAttachment(image: #imageLiteral(resourceName: "line-image"))
let lineImageString = NSMutableAttributedString(attachment: lineImageAttachment)
lineImageString.addAttribute(.paragraphStyle, value: lineImageStringParagraphStyle, range: NSRange(location: 0, length: lineImageString.length))
let detailStringParagraphStyle = NSMutableParagraphStyle()
detailStringParagraphStyle.alignment = .center
let detailString = NSAttributedString(string: "\n\(model.detail)", attributes: [.paragraphStyle: detailStringParagraphStyle, .font: UIFont.systemFont(ofSize: 12)])
fullString.append(mainString)
fullString.append(lineImageString)
fullString.append(detailString)
return fullString
}
Also have a look at my library StringEx. It allows you to create a NSAttributedString from the template and apply styles without having to write a ton of code:
import StringEx
...
func createAttributedString(for model: BookModel) -> NSAttributedString {
let pattern = "<main />\n<image />\n<detail />"
let ex = pattern.ex
ex[.tag("main")]
.insert(model.main)
.style([
.aligment(.center),
.lineHeightMultiple(2),
.font(.systemFont(ofSize: 24))
])
let lineImageAttachment = NSTextAttachment(image: #imageLiteral(resourceName: "line-image"))
let lineImageString = NSAttributedString(attachment: lineImageAttachment)
ex[.tag("image")]
.insert(lineImageString)
.style([
.aligment(.center),
.paragraphSpacingBefore(10),
.paragraphSpacing(20)
])
ex[.tag("detail")]
.insert(model.detail)
.style([
.aligment(.center),
.font(.systemFont(ofSize: 12))
])
return ex.attributedString
}
Old answer:
I think you can just set the spacing at the end of the first paragraph (main string) and the spacing at the beginning of the last paragraph (detail string):
func createAttributedString(for model: BookModel) -> NSMutableAttributedString {
let fullString = NSMutableAttributedString()
let mainStringParagraphStyle = NSMutableParagraphStyle()
mainStringParagraphStyle.alignment = .center
mainStringParagraphStyle.paragraphSpacing = 30 // The space after the end of the paragraph
let mainString = NSAttributedString(string: "\(model.main)\n", attributes: [.paragraphStyle: mainStringParagraphStyle])
let lineImageStringParagraphStyle = NSMutableParagraphStyle()
lineImageStringParagraphStyle.alignment = .center
let lineImageAttachment = NSTextAttachment(image: #imageLiteral(resourceName: "line-image"))
let lineImageString = NSMutableAttributedString(attachment: lineImageAttachment)
lineImageString.addAttribute(.paragraphStyle, value: lineImageStringParagraphStyle, range: NSRange(location: 0, length: lineImageString.length))
let detailStringParagraphStyle = NSMutableParagraphStyle()
detailStringParagraphStyle.alignment = .center
detailStringParagraphStyle.paragraphSpacingBefore = 5 // The distance between the paragraph’s top and the beginning of its text content
let detailString = NSAttributedString(string: "\n\(model.detail)", attributes: [.paragraphStyle: detailStringParagraphStyle])
fullString.append(mainString)
fullString.append(lineImageString)
fullString.append(detailString)
return fullString
}
I have a label with text and want to add more test in running time, but I want to add different style to this text that was add. Is there a way to do this?
This is the code
label.text = (label.text ?? "") + " \n \(userName)"
How do I add style to userName without changing the style of the label?
use attributed text in UILabel:
here some code.
a) some useful typedefs:
typealias AttrDict = [NSAttributedString.Key : Any]
a) create some styles:
func textAttribute() -> AttrDict{
let textFont = UIFont.systemFont(ofSize: 32)
let stdAttrib = [NSAttributedString.Key.font: textFont,
//NSParagraphStyleAttributeName: paragraphStyle2,
NSAttributedString.Key.foregroundColor: UIColor.red] as AttrDict
return stdAttrib
}
func smallTextAttribute() -> AttrDict{
let textFont = UIFont.systemFont(ofSize: 10)
let stdAttrib = [NSAttributedString.Key.font: textFont,
NSAttributedString.Key.foregroundColor: UIColor.green] as AttrDict
return stdAttrib
}
c) build Your attributed String:
func myAttributedString() -> NSAttributedString {
let pieces = ["hello", "word"]
let resultAttributed = NSMutableAttributedString()
var s = ""
s = pieces[0] + "\n"
resultAttributed.append(NSAttributedString(string: s,
attributes: stdTextAttribute() ))
s = pieces[1] + "\n"
resultAttributed.append(NSAttributedString(string: s,
attributes: smallTextAttribute() ))
return resultAttributed
}
d) put in Your label/textView:
....
class ViewController: UIViewController {
#IBOutlet weak var myLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
myLabel.numberOfLines = 0
myLabel.attributedText = myAttributedString()
}
}
I made a GIST:
https://gist.github.com/ingconti/aefc78c6d0b22f5329f906094c312a21
PLS connect UIlabel..
:)
To do this, you need to use NSMutableAttributedString, NSAttributedString and use label.attributedText, How?
Doing this:
let userName = "StackOverflow"
let prefixText = NSAttributedString(string: "this is normal ")
let font = UIFont.systemFont(ofSize: 40)
let attributes: [NSAttributedString.Key: Any] = [
.font: font,
.foregroundColor: UIColor.blue
]
let userNameWithStyle = NSAttributedString(string: userName, attributes: attributes)
let finalString = NSMutableAttributedString(attributedString: prefixText)
finalString.append(userNameWithStyle)
self.label.attributedText = finalString
Image result
I would like to mask text of a UILabel to achieve the following result
In Swift, You can do it like this:
var attributedString = NSMutableAttributedString(string: "Your String")
let textAttachment = NSTextAttachment()
textAttachment.image = UIImage(named: "Your Image Name")
let attrStringWithImage = NSAttributedString(attachment: textAttachment)
attributedString.insert(attrStringWithImage, at: 0)
label.attributedText = attributedString
This will work for you .
extension UILabel
{
func addImage(imageName: String)
{
let attachment:NSTextAttachment = NSTextAttachment()
attachment.image = UIImage(named: imageName)
let attachmentString:NSAttributedString = NSAttributedString(attachment: attachment)
let myString:NSMutableAttributedString = NSMutableAttributedString(string: self.text!)
myString.appendAttributedString(attachmentString)
self.attributedText = myString
}
}
Another version of the code that allow adding the icon before or after the label.
extension UILabel
{
func addImage(imageName: String, afterLabel bolAfterLabel: Bool = false)
{
let attachment: NSTextAttachment = NSTextAttachment()
attachment.image = UIImage(named: imageName)
let attachmentString: NSAttributedString = NSAttributedString(attachment: attachment)
if (bolAfterLabel)
{
let strLabelText: NSMutableAttributedString = NSMutableAttributedString(string: self.text!)
strLabelText.appendAttributedString(attachmentString)
self.attributedText = strLabelText
}
else
{
let strLabelText: NSAttributedString = NSAttributedString(string: self.text!)
let mutableAttachmentString: NSMutableAttributedString = NSMutableAttributedString(attributedString: attachmentString)
mutableAttachmentString.appendAttributedString(strLabelText)
self.attributedText = mutableAttachmentString
}
}
//you can remove the image by calling this function
func removeImage()
{
let text = self.text
self.attributedText = nil
self.text = text
}
}
For this use a UITextView instead of UILabel.
let imgRectBezier = UIBezierPath(rect: imgView.frame)
txtView.textContainer.exclusionPaths = [imgRectBezier]
Using this, the text will be excluded from the frame area added in the exclusion paths. You can even exclude multiple frames.
How to set UILabel text attributes only once and then just change text (string)
mTextValue.attributedText = NSAttributedString(string: "STRING",
attributes:
[NSAttributedStringKey.strokeWidth: -3.0,
NSAttributedStringKey.strokeColor: UIColor.black])
mTextValue.text = "NEW STRING" // won't change anything
or to set new string do I have to set NSAttributedString to .attributedText again and again?
You could declare a mutableAttributed string separat and change the string of it like here:
let yourString = "my string"
let yourAttributes = [NSAttributedStringKey.strokeWidth: -3.0, NSAttributedStringKey.strokeColor: UIColor.black] as [NSAttributedStringKey : Any]
let mutableAttributedString = NSMutableAttributedString(string: yourString, attributes: yourAttributes)
let yourNewString = "my new string"
mutableAttributedString.mutableString.setString(yourNewString)
Full example:
import UIKit
class ViewController: UIViewController {
var mutableAttributedString = NSMutableAttributedString()
#IBAction func buttonTapped(_ sender: Any) {
mutableAttributedString.mutableString.setString("new string")
mainLabel.attributedText = mutableAttributedString
}
#IBOutlet weak var mainLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let yourString = "my string"
let yourAttributes = [NSAttributedStringKey.strokeWidth: -3.0, NSAttributedStringKey.strokeColor: UIColor.blue] as [NSAttributedStringKey : Any]
mutableAttributedString = NSMutableAttributedString(string: yourString, attributes: yourAttributes)
mainLabel.attributedText = mutableAttributedString
}
}