I'm trying to make the words split by spaces green in a UITextField, kind of like the way it works when you compose of a new iMessage. I commented out the part of my code that's giving me a runtime error. Please let me know if you have any ideas:
func textChanged(sender : UITextField) {
var myMutableString = NSMutableAttributedString()
let arr = sender.text!.componentsSeparatedByString(" ")
var c = 0
for i in arr {
/*
myMutableString.addAttribute(NSForegroundColorAttributeName, value: UIColor.greenColor(), range: NSRange(location:c,length:i.characters.count))
sender.attributedText = myMutableString
*/
print(c,i.characters.count)
c += i.characters.count + 1
}
}
Your code has at least two parts needed to be fixed.
var myMutableString = NSMutableAttributedString()
This line creates an empty NSMutableAttributedString. Any access to the content may cause runtime error.
The other is i.characters.count. You should not use Character based locations and counts, when the APIs you want use is based on the behaviour of NSString. Use UTF-16 based count.
And one more, this is not critical, but you should use sort of meaningful names for variables.
So, all included:
func textChanged(sender: UITextField) {
let text = sender.text ?? ""
let myMutableString = NSMutableAttributedString(string: text)
let components = text.componentsSeparatedByString(" ")
var currentPosition = 0
for component in components {
myMutableString.addAttribute(NSForegroundColorAttributeName, value: UIColor.greenColor(), range: NSRange(location: currentPosition,length: component.utf16.count))
sender.attributedText = myMutableString
print(currentPosition, component.utf16.count)
currentPosition += component.utf16.count + 1
}
}
But whether this works as you expect or not depends on when this method is called.
You create an empty attributed string but never install any text into it.
The addAttribute call apples attributes to text in a string. If you try to apply attributes to a range that does not contain text, you will crash.
You need to install the content of the unattributed string into the attributed string, then apply attributes.
Note that you should probably move the line
sender.attributedText = myMutableString
Outside of your for loop. There is no good reason to install the attributed string to the text field repeatedly as you add color attributes to each word.
Note this bit from the Xcode docs on addAttribute:
Raises... an NSRangeException if any part of aRange lies beyond the
end of the receiver’s characters.
If you are getting an NSRangeException that would be a clue as to what is wrong with your current code. Pay careful attention to the error messages you get. They usually offer important clues as to what's going wrong.
Related
So I have the following question:
I have attributed string contained in a UITextView. Attributed string is contained of two parts - the normal one is ordinary text, and the second part has NSURL in it - so for better visualisation it would look like:
For more information READ HERE
The problem that I am having is that if the text fits in the one line I must keep it in one line, and if "HERE" falls in second line I must put READ also in the second line.
So, the first case - if all fits
For more information READ HERE
All other cases -
For more information
READ HERE
I tried to do it with checking if size of the screen is bigger than textfield bounds but it didn't work:
if (label.bounds.size.width < size.width) ...
I tried also other similar solutions but i think they are all addable on normal UILabels and not modified attributed texts.
If you have any idea how to deal with this I would appreciate it.
Thanks :)
There are multiple solution to resolve this.
First one is to use non-breakable space
simply add "\u{00a0}" in between click here like CLICK\u{00a0}HERE
here is the link
Second one is instead of adding space you can add "_" into it.
e.g.
"CLICK_HERE" and you can replace the color of "_" with clear.
here is the code
class ViewController: UIViewController {
#IBOutlet weak var temp : UILabel!
override func viewDidLoad() {
let myString = "For more detail information READ_HERE"
let attributedString: NSMutableAttributedString = NSMutableAttributedString(string: myString)
attributedString.setColorForText(textForAttribute: "_", withColor: .clear)
temp.attributedText = attributedString
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
extension NSMutableAttributedString {
func setColorForText(textForAttribute: String, withColor color: UIColor) {
let range: NSRange = self.mutableString.range(of: textForAttribute, options: .caseInsensitive)
self.addAttribute(NSAttributedString.Key.foregroundColor, value: color, range: range)
}
}
I have a UILabel that dynamically gets updated every time I click a button, the data will be fetched from firebase and displayed on the uilabel.I am able to display the text as shown below. I would like to insert a line break when a specific delimiter(say '.' PERIOD) is encountered in the text. I have looked into many solutions about UIlabel line break but couldn't find one what exactly I am looking for, every solution deals with static text that we provide in the storyboard itself. Any help will be appreciated. Thank you.
)
Created an outlet for the label and have taken a string which has three sentences separated by ".". The content in the image attached will be this string. Try using replacingOccurences() function as given below.
#IBOutlet weak var trialLabel: UILabel!
var string = "This is a trial text.Let us jump to a new line when the full stop is encountered.Hope it helps."
string = string.replacingOccurrences(of: ".", with: ".\n")
trialLabel.text = string
Check https://developer.apple.com/documentation/foundation/nsstring/1412937-replacingoccurrences for further reference. Happy Coding!!
You can achieve this with the attributed text property of UILabel. Try to find and replace the character with html line break and then assign this text to the UILabel attributed text.
You can replace string by
let str = "This is the string to be replaced by a new string"
let replacedStr = str.replacingOccurrences(of: "string", with: "str")
use below code
// HTML Tag Remove Method
extension String{
func removeHtmlFromString(inPutString: String) -> String{
return inPutString.replacingOccurrences(of: ".", with: ".\n", options: .regularExpression, range: nil)
}
}
let str = "Lorem Ipsum is simply dummy text.Lorem Ipsum has been the industry's standard dummy text ever since the 1500."
let postDiscription = str!.removeHtmlFromString(inPutString: str!)
You can add a \n in the text string to where you want to create a line break.
I would like to have a label with an attributed string that changes dynamically at runtime. Something like this
Is there a better way to dynamically change the price value without hardcoding the attributed keys dictionaries and manually building the NSAttributedString?
The best way to approach Attributed Strings on iOS is by using the built-in Attributed Text editor in the interface builder and avoid uneccessary hardcoding NSAtrributedStringKeys in your source files.
You can later dynamically replace placehoderls at runtime by using this extension:
extension NSAttributedString {
func replacing(placeholder:String, with valueString:String) -> NSAttributedString {
if let range = self.string.range(of:placeholder) {
let nsRange = NSRange(range,in:valueString)
let mutableText = NSMutableAttributedString(attributedString: self)
mutableText.replaceCharacters(in: nsRange, with: valueString)
return mutableText as NSAttributedString
}
return self
}
}
Add a storyboard label with attributed text looking like this.
Then you simply update the value each time you need like this:
label.attributedText = initalAttributedString.replacing(placeholder: "<price>", with: newValue)
Make sure to save into initalAttributedString the original value.
You can better understand this approach by reading this article:
https://medium.com/mobile-appetite/text-attributes-on-ios-the-effortless-approach-ff086588173e
May not know, this is what you're looking for but will solve your problem.
You may use labels for each and update amount easily without touching attributed string.
Here is result:
Sample code, I've tried for this demo:
#IBOutlet weak var lblAmount: UILabel!
func pricelabel() -> Void {
var amount = 0
Timer.scheduledTimer(withTimeInterval: 0.3, repeats: true) { (timer) in
amount += 10
self.lblAmount.text = "\(amount)"
}.fire()
}
I have a function that finds the current word a user has selected in a UITextView. However, if I call this function when an emoji is in the UITextView.text property, I see a crash. I believe this is because of the different character counts in String vs NSString.
How do I properly convert this?
func currentWord() -> String {
let cursorPosition = selectedRange.location
let separationCharacters = NSCharacterSet(charactersInString: " ")
// crash occurs here
let beginRange = Range(text.startIndex.advancedBy(0) ..< text.startIndex.advancedBy(cursorPosition))
let endRange = Range(text.startIndex.advancedBy(cursorPosition) ..< text.startIndex.advancedBy(text.characters.count))
let beginPhrase = text.substringWithRange(beginRange)
let endPhrase = text.substringWithRange(endRange)
let beginWords = beginPhrase.componentsSeparatedByCharactersInSet(separationCharacters)
let endWords = endPhrase.componentsSeparatedByCharactersInSet(separationCharacters)
return beginWords.last! + endWords.first!
}
I believe this is because of the different character counts in String vs NSString
You're right about that. You are shifting back and forth between using NSRange (Cocoa) and Range (Swift) — and they work differently. And NSString (Cocoa) and String (Swift) have different ideas of where the character boundaries are. You need to be consistent.
Once you've used selectedRange in the first line, you are in the Cocoa world of NSRange. You need to stay consistently in the Cocoa world. Don't use any Swift Ranges! Don't use any Swift characters!
Form your beginRange entirely using NSRange — for example, call NSMakeRange. Don't use characters.count; stay in the NSString world and use the string's length (in Swift, that is its utf16.count). Then all will be well.
I have a subclass of NSTextStorage and I'm trying to remove the foreground color of a paragraph the following way:
var paragraphRange = self.string.paragraphRangeForRange(
advance(self.string.startIndex, theRange.location)..advance(self.string.startIndex, theRange.location + theRange.length))
self.removeAttribute(NSForegroundColorAttributeName, range: paragraphRange)
However, I get the following error Cannot invoke 'removeAttribute' with an argument list of type '(String, range: (Range<String.Index>))'
Help Please. I think TextKit on Swift is a mess. Some methods receive/return NSRange but String works with Range<String.Index> making it a hell to work with.
The problem here is that the NSString returned by self.string
is automatically bridged to a Swift String. A possible solution is
to convert it back to NSString explicitly:
func removeColorForRange(theRange : NSRange) {
let paragraphRange = (self.string as NSString).paragraphRangeForRange(theRange)
self.removeAttribute(NSForegroundColorAttributeName, range: paragraphRange)
}
Note also that the range operator .. has been replaced by ..<
in newer Swift versions (to avoid confusion with ... and to
emphasize that the upper bound is not included).