I have a TableView and in each cell, I have a UISwitch which if enabled, modifies the label in the row.
Here is how I am modifying it:
#IBAction func completedTask(_ sender: UISwitch) {
//Getting original taskLabel
let initalLabel = taskLabel.text
//Modifying the string to have a line through it. Storing it in variable attributeString
let attributeString: NSMutableAttributedString = NSMutableAttributedString(string: taskLabel.text!)
attributeString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: 2, range: NSMakeRange(0, attributeString.length))
if sender.isOn{
print("attributed Label --> ",attributeString)
taskLabel.textColor = UIColor.red
taskLabel.attributedText = attributeString
}else{
print("initial Label --> ",initalLabel!)
taskLabel.text = initalLabel
taskLabel.textColor = UIColor.black
}
}
I am running into an issue to reset the label back to the original string. I will do a demo right now. I have added several print statements to help with debugging. We can see that initialLabel holds the correct cell value, but for some reason doesn't assign it.
Here is the demo:
Why is it not displaying my taskLabel with the right string?
you need to remove the strikethrough in off state
Removes the named attribute from the characters in the specified range. Ref removeAttribute:range:
#IBAction func completedTask(_ sender: UISwitch) {
//Modifying the string to have a line through it. Storing it in variable attributeString
let attributeString: NSMutableAttributedString = NSMutableAttributedString(string: taskLabel.text!)
if sender.isOn{
attributeString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: 2, range: NSMakeRange(0, attributeString.length))
}else{
attributeString.removeAttribute(NSAttributedStringKey.strikethroughStyle, value: 2, range: NSMakeRange(0, attributeString.length))
}
taskLabel.textColor = sender.isOn ? .red : .black
taskLabel.attributedText = attributeString
}
Related
I am working in an application where I have used "UITextField" as SEARCHBOX. when the user enter the search text in uitextfield UiTableView with sections will display the result, but I need to highlight the text entered in the textfield in the tableview. I have used attributed text but it is only possible within a range. So please help me an idea for implementing the highlighter.
myMutableString = NSMutableAttributedString(string: contactsList.familyName + contactsList.givenName, attributes: [NSAttributedStringKey.font:UIFont(name: "Catamaran", size: 14.0)!])
if contactsList.familyName.count >= 4 {
myMutableString.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.black, range: NSRange(location:0,length:4))
}else {
myMutableString.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.black, range: NSRange(location:0,length:2))
}
cell?.profileName.attributedText = myMutableString
I tried to change foreground color but it just changes text color not background color.
I don't know whether this is the right solution or not, but you can try something like this.
If I understand your problem correctly,
highlihted textfield
Add a label globally
let label = UILabel()
Set label frame in viewDidLoad
label.frame.size = CGSize(width: 10, height: textfield.frame.size.height)// your settings width should be 10 or smaller
label.font = UIFont(name: "Halvetica", size: 14)
label.backgroundColor = UIColor.red
Set textfield text color to textfield background color
textfield.textColor = UIColor.white
Add textFieldEditingChanged method like this:
#IBAction func changed(_ sender: Any) {
label.removeFromSuperview()
label.text = textfield.text
textfield.addSubview(label)
label.sizeToFit()
}
Don't forget to set label and textfield fonts same. If you don't, textfield cursor may appear behind or front of label
UPDATE:
You should create attributed string with html
extension String {
var html2AttributedString: NSAttributedString? {
do {
return try NSAttributedString(data: Data(utf8),
options: [.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue],
documentAttributes: nil)
} catch {
print("error: ", error)
return nil
}
}
var html2String: String {
return html2AttributedString?.string ?? ""
}// you can find several way to do this just google it
}
Data source:
var dataSource = ["foo","foo1","foo2","foo3","foo4","foo5","foo6"]
Create variable for found result indexes at range in data source strings
var indexes : [Int:Range<String.Index>] = [:]
cellForRowAt:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let string = dataSource[indexPath.row]
cell.textLabel?.attributedText = string.html2AttributedString
return cell
}
searchText editingChanged:
#IBAction func change(_ sender: UITextField) {
indexes.removeAll()
dataSource = ["foo","foo1","foo2","foo3","foo4","foo5","foo6"] // assign datasource again
for i in 0..<dataSource.count {
if let textRange = dataSource[i].range(of: textField.text!) {
indexes[i] = textRange
}
}
for (index,entry) in indexes {
let highlighted = "<text style='background-color:#FFFF00';>\(dataSource[index][entry])</text>"
dataSource[index].replaceSubrange(entry, with: highlighted)
}
tableView.reloadData()
}
You should change font and size in html after some searching.
Hope this helps
So I have two UITextFields: name and amount and two UIButtons: income, expense.
When I press the expense button I want my amount textfield color to change to red or green if income button is pressed.
This only works if amount textfield is in focus, if name textfield is in focus, the color is not changed for amount.
Is there a way to change the color of the textfield if it's not in focus ?
edit:
Here is my code where I change the color:
#IBAction func typeBtnPressed(_ sender: UIButton) {
if sender.tag == Buttons.expense.rawValue {
amountTxt.textColor = .red
} else {
amountTxt.textColor = .green
}
}
After the textColor is set, the value needs to be reassigned to the textField.
textField.color = newColor
textField.text = text
It seems iOS uses by default attributedText and not text, that is why nothing is happening, and on focus it seems it takes your textColor into account, just do
let color: UIColor
if sender.tag == Buttons.expense.rawValue {
color = .red
} else {
color = .green
}
let attributedText = NSMutableAttributedString(attributedString: amountTxt.attributedText!)
attributedText.setAttributes([NSAttributedStringKey.foregroundColor : color], range: NSMakeRange(0, attributedText.length))
amountTxt.attributedText = attributedText
This will then work as soon as button is pressed
Swift 4 Xcode 10 edition
let attributedText = NSMutableAttributedString(attributedString: textField.attributedText!)
attributedText.setAttributes([NSAttributedString.Key.foregroundColor : UIColor.red], range: NSMakeRange(0, attributedText.length))
textField.attributedText = attributedText
I have a UITextField for formatting phone numbers. I am trying to add a "+" sign prefix as the first character, that can't be removed. Formatting & checks are working fine, but having a prefix doesn't seem to work..
In the beginning, it doesn't present the "+" sign however, if I write a character and delete, it will present the "+" sign. I know this is because the shouldChangeCharactersInRange would not get called before I type the first number, however why doesn't the makePrefix() function add the prefix?
So I am stuck at the time where 'the user clicked on the UITextField but hasn't entered a character yet'..
override func viewDidLoad() {
super.viewDidLoad()
makePrefix()
}
func makePrefix() {
let attributedString = NSMutableAttributedString(string: "+")
attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.whiteColor(), range: NSMakeRange(0,1))
phoneTextField.attributedText = attributedString
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
if textField == phoneTextField {
textField.typingAttributes = [NSForegroundColorAttributeName:UIColor.whiteColor()]
return false
}
return true
}
func formattedPhoneNumber(updatedTextString: NSString, textField: UITextField) {
textField.text = "\(updatedTextString as String)"
print(updatedTextString)
}
Check textFieldDidBeginEditing, it will get called when the textField becomes first responder.
Check if the text is begin with "+", if not call your makePrefix().
Elegant solution using leftView to UITextField
let prefix = UILabel()
prefix.text = "+"
// set font, color etc.
prefix.sizeToFit()
phoneTextField.leftView = prefix
phoneTextField.leftViewMode = .always // or .whileEditing
From what i've read i would need to use rangeOfString to search a textview entry but am unsure about how to go about that. Is it possible to change the color of a text entered in textview in real time, for example if someone wrote "blue," could i change the word to blue the moment they typed it. If so how would i go about that? I'm very new to coding and even newer to swift.
You will have to use attributed text for your text view and then use the textView(textView: UITextView, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool method, which will be triggered whenever the text in the text view's text changes. Apply your own logic in there as to what range the colored text will fall into and how that will happen...
Make sure your controller conforms to the UITextViewDelegate protocol and make the textView's delegate your controller.
Demonstration:
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
self.textView.delegate = self // important! Otherwise the textView will not know where to call the delegate functions!
}
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
// first make sure that the text field is the one we want to actually change
if textView == self.textView{
let nsString = textView.text as NSString // we explicitly cast the Swift string to NSString so that we can use rangeOfString for example
let stringLength = textView.text.characters.count
// Arbitrarily check if the string in the text field is not empty
// Apply your own logic for when to update the string
if stringLength > 0{
let text = NSMutableAttributedString(string: textView.text)
// Currently the range is assumed to be the whole text (the range covers the whole string)
// You'll have to apply your own logic here
text.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: NSMakeRange(0, stringLength))
textView.attributedText = text
}
}
return true
}
}
For example, instead of using the above to color the whole text
text.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: NSMakeRange(0, stringLength))
Color the first occurence of "hello" in red:
text.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: nsString.rangeOfString("hello"))
Note that I explicitly cast the textView's text to NSString so that we can use the range functions such as (rangeOfString())
Changes were made to swift in which count no longer seems to work with String. I made a slight change to the answer given by the_critic (https://stackoverflow.com/users/1066899/the-critic). Thank you to all who helped
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
self.textView.delegate = self // important! Otherwise the textView will not know where to call the delegate functions!
}
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
// first make sure that the text field is the one we want to actually change
if textView == self.textView{
let nsString = textView.text as NSString // we explicitly cast the Swift string to NSString so that we can use rangeOfString for example
let stringLength = textView.text.characters.count
// Arbitrarily check if the string in the text field is not
empty
// Apply your own logic for when to update the string
if stringLength > 0{
let text = NSMutableAttributedString(string: textView.text)
// Currently the range is assumed to be the whole text (the range covers the whole string)
// You'll have to apply your own logic here
text.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: NSMakeRange(0, stringLength))
textView.attributedText = text
}
}
return true
}
}
I have an app in which the user has to type a four digit pin code. All digits have to be at a set distance from each other.
Is there a way to do this if the PinTextField is a subclass of UIView? I know in a ViewController you could use UITextFieldTextDidChangeNotification and set the attributed text for each change. Notifications don't seem to work in a UIView though.
Also I was wondering if there isn't a simpler way than making an attributed string for every update if you want to set the letter spacing of a UITextField text ?
Correct spacing:
Wrong spacing:
No need to go for attributedText, which to be honest, was a mess implementing with modified spacing. As soon as I closed the keyboard the spacing disappeared, which prompted me to dig further.
Every UITextField has a property called defaultTextAttributes, which according to Apple "returns a dictionary of text attributes with default values.". The Apple document also says that "this property applies the specified attributes to the entire text of the text field"
Just find a suitable place in your code, usually where the textfield is being initialized and then copy and paste the following.
Answered in Swift 3.0
textfield.defaultTextAttributes.updateValue(spacing, forKey: NSKernAttributeName)
where spacing is of CGFloat type. For example 2.0
This works for different fonts as well.
Cheers!!
The latest syntax seems to be:
yourField.defaultTextAttributes.updateValue(36.0,
forKey: NSAttributedString.Key.kern)
This is what eventually worked to set the kern for every change
textField.addTarget(self, action: "textFieldDidChange", forControlEvents: .EditingChanged)
func textFieldDidChange () {
let attributedString = NSMutableAttributedString(string: textField.text)
attributedString.addAttribute(NSKernAttributeName, value: 5, range: NSMakeRange(0, count(textField.text)))
attributedString.addAttribute(NSFontAttributeName, value: font, range: NSMakeRange(0, count(textField.text)))
attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.blackColor(), range: NSMakeRange(0, count(textField.text)))
textField.attributedText = attributedString
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if count(textField.text) < 4 {
return true
// Else if 4 and delete is allowed
}else if count(string) == 0 {
return true
// Else limit reached
}else{
return false
}
}
The problem however remains because different numbers have different widths, I will just resort back to making a UITextField for every digit.
Use the defaultTextAttributes property of UITextField. It will handle the conversion to NSAttributedString for you and apply the attributes you set. For example:
NSMutableDictionary *attrs = [self.textField.defaultTextAttributes mutableCopy];
[attrs addEntriesFromDictionary:#{
NSKernAttributeName: #18,
NSUnderlineColorAttributeName: [UIColor grayColor],
NSUnderlineStyleAttributeName: #(NSUnderlineStyleSingle | NSUnderlinePatternDash)
}];
self.textField.defaultTextAttributes = attrs;
Try this code After setting the delegate to the textfield.Hope it will work.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:textField.text];
[attributedString addAttribute:NSKernAttributeName
value:#(5.4)
range:NSMakeRange(0, textField.text.length)];
textField.attributedText = attributedString;
return YES;
}
Not really sure about any other solution instead of using attributed string.
But for the notification part, you can set the textFields delegate to UIView and define below method in the view.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;
The above method is called every time the text entered in the text field changes.
This is working fine in Swift 2.2. Hope this will help you for letter spacing in text field
override func viewDidLoad() {
// Do any additional setup after loading the view.
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(SignupVC.limitTextField(_:)), name: "UITextFieldTextDidChangeNotification", object: txtContactNumber)
}
func limitTextField(Notif:NSNotification) {
let limit=10;
let attributedString = NSMutableAttributedString(string: txtContactNumber.text!)
attributedString.addAttribute(NSKernAttributeName, value: 7, range: NSMakeRange(0, (txtContactNumber.text?.characters.count)!))
// attributedString.addAttribute(NSFontAttributeName, value: font, range: NSMakeRange(0, count(textField.text)))
attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.blackColor(), range: NSMakeRange(0,(txtContactNumber.text?.characters.count)!))
txtContactNumber.attributedText = attributedString
if(txtContactNumber.text?.characters.count>limit)
{
txtContactNumber.text=txtContactNumber.text?.substringToIndex(limit)
}
}
Need to count the kern for each character and remove it for the last character. There is example on Swift 5.3
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let maxLength = 6
let symbolWidth = CGFloat(43)
let font = UIFont.systemFont(ofSize: 30)
if string == "" { // when user remove text
return true
}
if textField.text!.count + string.count - range.length > maxLength { // when user type extra text
return false
}
let currentText = NSMutableAttributedString(attributedString: textField.attributedText ?? NSMutableAttributedString())
currentText.deleteCharacters(in: range) // delete selected text
var newStringLength = 0
for char in string{
let newSymbol = NSMutableAttributedString(string: String(char))
newSymbol.addAttribute(.font, value: font, range: NSMakeRange(0, 1))
let currentSymbolWidth = newSymbol.size().width
let kern = symbolWidth - currentSymbolWidth
newSymbol.addAttribute(.kern, value: kern, range: NSMakeRange(0,1))
currentText.insert(newSymbol, at: range.location + newStringLength)
newStringLength += 1
}
if currentText.length == maxLength{
currentText.addAttribute(.kern, value: 0, range: NSMakeRange(maxLength - 1, 1))
}
textField.attributedText = currentText
return false
}