I have a UITextView, and I want to allow the user to highlight a portion of text and copy it with a button instead of using the default Apple method. The problem is that I can't get the text within the selected range.
Here's what I have:
#IBAction func copyButton(_ sender: Any) {
let selectedRange: UITextRange? = textView.selectedTextRange
selectedText = textView.textInRange(selectedRange)
UIPasteboard.general.string = selectedText
}
But I'm getting
UITextView has no member textInRange
and I'm not sure what I should be using instead.
What is happening is that UITextView method textInRange have been renamed to text(in: Range) since Swift 3. Btw you forgot to add the let keyword in your sentence:
if let range = textView.selectedTextRange {
UIPasteboard.general.string = textView.text(in: range)
}
I have this code in my hands:
if let text = trimText?.mutableCopy() as? NSMutableString {
text.insertString("\(prefix) ", atIndex: 0)
textStorage.replaceCharactersInRange(range, withString: text as String)
}
When I try to change my text as:
text = attributedTextFunc(text)
where
func attributedTextFunc(str: NSString) -> NSAttributedString {
var attributedString = NSMutableAttributedString(string: str as String, attributes: [NSFontAttributeName:UIFont.systemFontOfSize(15.0)])
let boldFontAttribute = [NSFontAttributeName: UIFont.boldSystemFontOfSize(15.0)]
attributedString.addAttributes(boldFontAttribute, range: str.rangeOfString("More"))
return attributedString
}
and I get this error:
Cannot assign value of type 'NSAttributedString' to type 'NSMutableString'
How can I make it bold?
You cannot assing NSAttributedString to text. It's two different types.
String is not subclassed from NSAttributedString.
You should set:
attributedText = attributedTextFunc(text)
Then if you want to present it on UILabel
label.attributedText = attributedText
UPDATE
Struct String doesn't know anything about UIKit and Bold styles.
NSAttributedString knows about UIKit and contains any text styles you want
UPDATE 2
In your case
ReadMoreTextView.attributedTrimText = attributedText
You cannot reassign text because:
text is constant (let)
text is NSMutableString, but attributedTextFunc return NSAttributedString
You have to store result of attributedTextFunc in variable as a NSAttributeString and set attributeText of UILabel instead of text
if let text = trimText?.mutableCopy() as? NSMutableString {
// ...
let attributeText = attributedTextFunc(text)
someLabel.attributeText = attributeText
}
Use this code and pass your normal String and Bold String (which is needed to be bold).
func attributeStrings(first: String, second : String) -> NSMutableAttributedString{
let myNormalAttributedTitle = NSAttributedString(string: first,
attributes: [NSFontAttributeName : UIFont.boldSystemFontOfSize(15)])
let myAttributedTitle = NSAttributedString(string: second,
attributes: [NSForegroundColorAttributeName : UIColor.blackColor()])
let result = NSMutableAttributedString()
result.appendAttributedString(myNormalAttributedTitle)
result.appendAttributedString(myAttributedTitle)
return result
}
and assign the return value of this function to
someLabel.attributeText = attributeStrings("My Name is", second : "Himanshu")
It is because, your text is type of NSMutableString and your function attributedTextFunc is type of NSString.
That is the problem so just change it from NSString to NSMutableString.
i've a single UIButton to set the text bold and unbold in UITextview
let range = mainTextView.selectedRange
let string = NSMutableAttributedString(attributedString: mainTextView.attributedText)
let attributes = [NSFontAttributeName: UIFont.boldSystemFontOfSize(12)]
string.addAttributes(attributes, range: mainTextView.selectedRange)
mainTextView.attributedText = string
My question is how to unbold the selected text if it is already bold? how to check if the selectedRange is already bold or not?
I know that the UITextView default can detect URL, but how can i make it detect hashtags(#)?
It doesn't needs to detect hashtags while typing, but then viewDidLoad the text is set in the UITextView, so i want to detect hashtags as a color or something.
I have been using ActiveLabel, but that is only for UILabel, and i need the scroll function that the UITextView has.
This should work for you
Create a new swift file with any name(UITextViewHashtagExtension.swift)
Insert this code below:
import UIKit
extension UITextView {
func resolveHashTags(){
// turn string in to NSString
let nsText:NSString = self.text
// this needs to be an array of NSString. String does not work.
let words:[NSString] = nsText.componentsSeparatedByString(" ")
// you can't set the font size in the storyboard anymore, since it gets overridden here.
let attrs = [
NSFontAttributeName : UIFont.systemFontOfSize(17.0)
]
// you can staple URLs onto attributed strings
let attrString = NSMutableAttributedString(string: nsText as String, attributes:attrs)
// tag each word if it has a hashtag
for word in words {
// found a word that is prepended by a hashtag!
// homework for you: implement #mentions here too.
if word.hasPrefix("#") {
// a range is the character position, followed by how many characters are in the word.
// we need this because we staple the "href" to this range.
let matchRange:NSRange = nsText.rangeOfString(word as String)
// convert the word from NSString to String
// this allows us to call "dropFirst" to remove the hashtag
var stringifiedWord:String = word as String
// drop the hashtag
stringifiedWord = String(stringifiedWord.characters.dropFirst())
// check to see if the hashtag has numbers.
// ribl is "#1" shouldn't be considered a hashtag.
let digits = NSCharacterSet.decimalDigitCharacterSet()
if let numbersExist = stringifiedWord.rangeOfCharacterFromSet(digits) {
// hashtag contains a number, like "#1"
// so don't make it clickable
} else {
// set a link for when the user clicks on this word.
// it's not enough to use the word "hash", but you need the url scheme syntax "hash://"
// note: since it's a URL now, the color is set to the project's tint color
attrString.addAttribute(NSLinkAttributeName, value: "hash:\(stringifiedWord)", range: matchRange)
}
}
}
// we're used to textView.text
// but here we use textView.attributedText
// again, this will also wipe out any fonts and colors from the storyboard,
// so remember to re-add them in the attrs dictionary above
self.attributedText = attrString
}
}
To use this you can do something like this:
self.textView.text = "This is an #example test"
self.textView.resolveHashTags()
Updated for Swift 4.0:
extension UITextView {
func resolveHashTags() {
// turn string in to NSString
let nsText = NSString(string: self.text)
// this needs to be an array of NSString. String does not work.
let words = nsText.components(separatedBy: CharacterSet(charactersIn: "#ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_").inverted)
// you can staple URLs onto attributed strings
let attrString = NSMutableAttributedString()
attrString.setAttributedString(self.attributedText)
// tag each word if it has a hashtag
for word in words {
if word.count < 3 {
continue
}
// found a word that is prepended by a hashtag!
// homework for you: implement #mentions here too.
if word.hasPrefix("#") {
// a range is the character position, followed by how many characters are in the word.
// we need this because we staple the "href" to this range.
let matchRange:NSRange = nsText.range(of: word as String)
// drop the hashtag
let stringifiedWord = word.dropFirst()
if let firstChar = stringifiedWord.unicodeScalars.first, NSCharacterSet.decimalDigits.contains(firstChar) {
// hashtag contains a number, like "#1"
// so don't make it clickable
} else {
// set a link for when the user clicks on this word.
// it's not enough to use the word "hash", but you need the url scheme syntax "hash://"
// note: since it's a URL now, the color is set to the project's tint color
attrString.addAttribute(NSAttributedStringKey.link, value: "hash:\(stringifiedWord)", range: matchRange)
}
}
}
// we're used to textView.text
// but here we use textView.attributedText
// again, this will also wipe out any fonts and colors from the storyboard,
// so remember to re-add them in the attrs dictionary above
self.attributedText = attrString
}
}
One option would be to use an NSAttributedString, something like this...
func convertHashtags(text:String) -> NSAttributedString {
let attrString = NSMutableAttributedString(string: text)
attrString.beginEditing()
// match all hashtags
do {
// Find all the hashtags in our string
let regex = try NSRegularExpression(pattern: "(?:\\s|^)(#(?:[a-zA-Z].*?|\\d+[a-zA-Z]+.*?))\\b", options: NSRegularExpressionOptions.AnchorsMatchLines)
let results = regex.matchesInString(text,
options: NSMatchingOptions.WithoutAnchoringBounds, range: NSMakeRange(0, text.characters.count))
let array = results.map { (text as NSString).substringWithRange($0.range) }
for hashtag in array {
// get range of the hashtag in the main string
let range = (attrString.string as NSString).rangeOfString(hashtag)
// add a colour to the hashtag
attrString.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor() , range: range)
}
attrString.endEditing()
}
catch {
attrString.endEditing()
}
return attrString
}
Then assign your attributedText like this...
let myText = "some text with a #hashtag in side of it #itsnoteasy"
self.textView.attributedText = convertHashtags(myText)
For swift 4.0
extension UITextView {
func resolveHashTags() {
// turn string in to NSString
let nsText = NSString(string: self.text)
// this needs to be an array of NSString. String does not work.
let words = nsText.components(separatedBy: CharacterSet(charactersIn: "#ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_").inverted)
// you can staple URLs onto attributed strings
let attrString = NSMutableAttributedString()
attrString.setAttributedString(self.attributedText)
// tag each word if it has a hashtag
for word in words {
if word.count < 3 {
continue
}
// found a word that is prepended by a hashtag!
// homework for you: implement #mentions here too.
if word.hasPrefix("#") {
// a range is the character position, followed by how many characters are in the word.
// we need this because we staple the "href" to this range.
let matchRange:NSRange = nsText.range(of: word as String)
// drop the hashtag
let stringifiedWord = word.dropFirst()
if let firstChar = stringifiedWord.unicodeScalars.first, NSCharacterSet.decimalDigits.contains(firstChar) {
// hashtag contains a number, like "#1"
// so don't make it clickable
} else {
// set a link for when the user clicks on this word.
// it's not enough to use the word "hash", but you need the url scheme syntax "hash://"
// note: since it's a URL now, the color is set to the project's tint color
attrString.addAttribute(NSAttributedStringKey.link, value: "hash:\(stringifiedWord)", range: matchRange)
}
}
}
// we're used to textView.text
// but here we use textView.attributedText
// again, this will also wipe out any fonts and colors from the storyboard,
// so remember to re-add them in the attrs dictionary above
self.attributedText = attrString
}
}
For Swift 3 +
extension UITextView {
func convertHashtags(text:String) -> NSAttributedString {
let attr = [
NSFontAttributeName : UIFont.systemFont(ofSize: 17.0),
NSForegroundColorAttributeName : clr_golden,
NSLinkAttributeName : "https://Laitkor.com"
] as [String : Any]
let attrString = NSMutableAttributedString(string: text)
attrString.beginEditing()
// match all hashtags
do {
// Find all the hashtags in our string
let regex = try NSRegularExpression(pattern: "(?:\\s|^)(#(?:[a-zA-Z].*?|\\d+[a-zA-Z]+.*?))\\b", options: NSRegularExpression.Options.anchorsMatchLines)
let results = regex.matches(in: text,
options: NSRegularExpression.MatchingOptions.withoutAnchoringBounds, range: NSMakeRange(0, text.characters.count))
let array = results.map { (text as NSString).substring(with: $0.range) }
for hashtag in array {
// get range of the hashtag in the main string
let range = (attrString.string as NSString).range(of: hashtag)
// add a colour to the hashtag
//attrString.addAttribute(NSForegroundColorAttributeName, value: clr_golden , range: range)
attrString.addAttributes(attr, range: range)
}
attrString.endEditing()
}
catch {
attrString.endEditing()
}
return attrString
}
}
Add UITextViewDelegate in your class and Use like this
self.tv_yourTextView.delegate = self
self.tv_yourTextView.attributedText = tv_yourTextView.convertHashtags(text: "This is an #museer test")
delegate funtion
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
print("hastag Selected!")
return true
}
-> Modified #Wez Answer
when to make mention or hashtag or any word, yo can make it by:
func convertRegex(text:String, regex: String) -> NSAttributedString {
let attr:[NSAttributedString.Key:Any] = [
NSAttributedString.Key.font : UIFont.systemFont(ofSize: 17.0),
NSAttributedString.Key.foregroundColor : UIColor.green,
] as [NSAttributedString.Key : Any]
let attrString = NSMutableAttributedString(string: text)
attrString.beginEditing()
do {
// Find all the specific word in our string
let regex = try NSRegularExpression(pattern: "\\s\(regex)\\b" , options: NSRegularExpression.Options.anchorsMatchLines)
let results = regex.matches(in: text,
options: NSRegularExpression.MatchingOptions.withoutAnchoringBounds, range: NSMakeRange(0, text.count))
let array = results.map { (text as NSString).substring(with: $0.range) }
for word in array {
let range = (attrString.string as NSString).range(of: word)
attrString.addAttributes(attr, range: range)
}
attrString.endEditing()
}
catch {
attrString.endEditing()
}
return attrString
}
I'm trying to set some attributes of a string in code, but can't get NSAttributedString to work. This is the function, that's supposed to change the string:
func getAttributedString(string: String) -> NSAttributedString
{
var attrString = NSMutableAttributedString(string: string)
var attrs = [NSFontAttributeName : UIFont.boldSystemFontOfSize(18.0)]
attrString.setAttributes(attrs, range: NSMakeRange(0, attrString.length))
return attrString
}
And this is how I use it:
if (self.product.packageDimensions != nil) {
self.descriptionLabel.text =
self.descriptionLabel.text + self.getAttributedString("Package dimensions:").string +
"\n\(self.product.packageDimensions) \n"
}
But the font stays the same. What am I doing wrong ?
You make 2 errors in your code.
setAttributes needs a Dictionary, not an Array
when you use the string attribute, you will only get a String, all attributes are lost.
To add or change attributes to a attributedString it has to be mutable. You only get a NSMutableString from the attributedText attribute. If you want to change it create a mutable version from it and change it. Then you may set the attributedText to the new mutable version.
If you can give the attributed string as an argument, I will give you an example that works:
func setFontFor(attrString: NSAttributedString) -> NSMutableAttributedString {
var mutableAttrString: NSMutableAttributedString = NSMutableAttributedString(attributedString: attrString)
let headerStart: Int = 0
let headerEnd: Int = 13
mutableAttrString.addAttribute(NSFontAttributeName, value: UIFont.boldSystemFontOfSize(18.0), range: NSMakeRange(headerStart, headerEnd))
return mutableAttrString
}
Usage:
myLabel.attributedText = setFontFor(myLabel.attributedText)
As you can see I used the attributedText property of the UILabel class, it also works for UITextView and others. If you have another label, you can create a new NSAttributedString with the initializer NSAttributedString(normalString) as you already used in the question code.
if (self.product.packageDimensions != nil) {
self.descriptionLabel.attributedText =
self.descriptionLabel.attributedText + self.getAttributedString("Package dimensions:").string +
"\n\(self.product.packageDimensions) \n"
}
You should use the attributedText method