How to Keep Static Prefix in UITextField When Pasting String - uitextfield

I have a UITextField that handles input of currency. The initial state of the text field has a set value of $0.00 but changes to just $ when it becomes the first responder.
I have successfully disabled the user from deleting the $ prefix of the text field's text by adding this:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if textField == priceTextField {
if range.length == 1 && count(string) == 0 {
// Deleting text
if range.location <= 0 {
return false
}
}
}
return true
}
However, this only works when the user enters text with keys and then deletes text by pressing the backspace key. If the user copies text, highlights the $ prefix and then pastes in the text, the $ prefix gets replaced with the pasted text.
If the user selects the location before the $ prefix and pastes the text, I move the $ prefix to the replacement string by handling it with:
if range.location == 0 && count(string) >= 1 {
textField.text = "$\(string)"
return false
}
I realize that I need to create some logic for using the range of the text in the text field, but I'm not sure about where to start.
I'm not asking for a handout of a code snippet, but rather if someone can point me in the direction of what sort of logic I am needing to implement so that the $ prefix is not editable and always at the beginning of the text (even when selected and pasted over)?

I would suggest you to,
1.If textfield is empty, use placeholder property of UITextField to indicate what needs to be entered.
2.If the textfield has a value,
i) in textFieldDidBeginEditing delegate, remove the $ symbol from the value, i.e. when textfield is active.
ii) in textFieldDidEndEditing delegate, add the $ symbol to the value entered, i.e. when textfield is inactive.

Related

NSTextField Loses Focus When Neighboring NSTableView Reloaded

I have a search field (NSTextField) called searchField and when you type in it, it refreshes the data shown in a NSTableView. The problem is that this refresh also triggers the selection of a table row, and that takes the focus out of the NSTextField.
The user types in the search field:
func controlTextDidChange(_ obj: Notification) {
if let field = obj.object as? NSTextField, field == searchField{
refreshData()
}
}
Then the NSTableView gets reloaded here:
func refreshData(){
//Process search term and filter data array
//...
//Restore previously selected row (if available)
let index = tableView.selectedRow
tableView.reloadData()
//Select the previously selected row
tableView.selectRowIndexes(NSIndexSet(index: index) as IndexSet, byExtendingSelection: false)
}
If I comment-out both the reloadData() and the selectRowIndexes then the search field behaves as intended (I can keep typing and it keeps the focus in the field). But if include either or both of those methods, the search field loses focus after I type the first character and refreshData() is called.
How can I keep focus in my search field and not let the table reload hijack the focus?
Ugh... it turns out I could never get the NSTableView to let go of the focus because it wasn't set to allow an Empty selection state. Checking a box in Interface Builder fixed it.

Replacing the ESC button on a NSTouchBar

I am building a NSTouchBar for my app using storyboard.
I want to replace the ESC button with something else.
As usual, there is no doc telling you how to do that.
I have searched the web and I have found vague informations like
You can change the content of "esc" to something else, though, like
"done" or anything, even an icon, by using
escapeKeyReplacementItemIdentifier with the NSTouchBarItem.
But this is too vague to understand.
Any ideas?
This is what I did so far.
I have added a button to NSTouchBar on storyboard and changed its identifier to newESC. I added this line programmatically:
self.touchBar.escapeKeyReplacementItemIdentifier = #"newESC";
When I run the App the ESC key is now invisible but still occupies its space on the bar. The button that was supposed to replace it, appears next to it. So that bar that was
`ESC`, `NEW_ESC`, `BUTTON1`, `BUTTON2`, ...
is now
`ESC` (invisible), `NEW_ESC`, `BUTTON1`, `BUTTON2`, ...
The old ESC is still occupying its space on the bar.
This is done by creating a touch bar item, let's say a NSCustomTouchBarItem containing a NSButton, and associating this item with its own identifier.
Then with another identifier you do your usual logic but you add the previously created identifier as the ESC replacement.
Quick example in Swift:
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItemIdentifier) -> NSTouchBarItem? {
switch identifier {
case NSTouchBarItemIdentifier.identifierForESCItem:
let item = NSCustomTouchBarItem(identifier: identifier)
let button = NSButton(title: "Button!", target: self, action: #selector(escTapped))
item.view = button
return item
case NSTouchBarItemIdentifier.yourUsualIdentifier:
let item = NSCustomTouchBarItem(identifier: identifier)
item.view = NSTextField(labelWithString: "Example")
touchBar.escapeKeyReplacementItemIdentifier = .identifierForESCItem
return item
default:
return nil
}
}
func escTapped() {
// do additional logic when user taps ESC (optional)
}
I also suggest making an extension (category) for the identifiers, it avoids making typos with string literals:
#available(OSX 10.12.2, *)
extension NSTouchBarItemIdentifier {
static let identifierForESCItem = NSTouchBarItemIdentifier("com.yourdomain.yourapp.touchBar.identifierForESCItem")
static let yourUsualIdentifier = NSTouchBarItemIdentifier("com.yourdomain.yourapp.touchBar.yourUsualIdentifier")
}

Restrict input on NSTextField

I want to restrict character input on NSTextField, i.e. so that disallowed characters aren't even appearing. Most of what I found about this topic were solutions that only validate after text input finished or using NSFormatter which still allows the character to appear.
So far I came up with this solution, sub-classing NSTextField:
class RestrictedTextField : NSTextField
{
static let VALID_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'-.& ";
override func textDidChange(notification:NSNotification)
{
for c in stringValue
{
if (RestrictedTextField.VALID_CHARACTERS.rangeOfString("\(c)") == nil)
{
stringValue = stringValue.stringByReplacingOccurrencesOfString("\(c)", withString: "", options: .LiteralSearch, range: nil);
break;
}
}
}
}
It works but isn't really optimal because the textcursor still moves by one space if an invalid character is tried to be entered. I also think the loop shouldn't be necessary so I wonder does somebody know a more elegant solution for this?
You have complete control with a subclass of NSFormatter. I'm not sure why you think you don't.
Override isPartialStringValid(_:proposedSelectedRange:originalString:originalSelectedRange:errorDescription:) and implement the desired logic. From the docs (with some minor edits by me):
In a subclass implementation, evaluate [the string pointed to by *partialStringPtr] according to the context. Return YES if partialStringPtr is acceptable and NO if partialStringPtr is unacceptable. Assign a new string to partialStringPtr and a new range to proposedSelRangePtr and return NO if you want to replace the string and change the selection range.
So, if the user tries to insert disallowed characters, you can either reject their edit in its entirety or modify it to strip those disallowed characters. (Remember that user changes can include pasting, so it's not necessarily just a single typed character.) To reject the change entirely, assign origString to *partialStringPtr and origSelRange to *proposedSelRangePtr.

textView breaking lines automatically - Swift

I'm trying to populate a textView with the content of an array. For each item in my array i have a flag setting true or false for this item, if its true i copy the content in a var called "nome" to the textview.
I did this with the code below:
for item in lista {
if item.disp == true { //checking if its true
var tempString = "\(item.nome)\n" //concatenate string with the a new line
textView_Lista_Pecas.text! += tempString //set the textView text
}
}
... the string printed must be shown line by line, but as it is a long string, the textView break the line when the string texts gets to the edge of textView.
I tried to enabled the horizontal scrollview to maximize the size of the textView, but only the vertical one is enabled when i check the option.
Anyone could give me a hint on this? I'm using swift and xcode 6.3.1.

theEvent charactersIgnoringModifiers - Get characters without modifiers

Im trying to implement a keyboard class in my game that has two modes. The game mode takes input that uses lowercase, unmodified keys (unmodified meaning if I type a '0' with the shift it still returns '0' instead of ')'). I have tracked it down as far as using the charactersIgnoringModifiers method of the NSEvent class but this method excludes all the modifier keys except for the shift key.
You can use -[NSEvent keyCode] and then translate the key code to a character without using any modifiers. Doing the latter is easier said than done. Here's a long mailing list thread on the techniques and gotchas.
The best option I could find so far for ignoring the <Shift> modifier is by using NSEvent.characters(byApplyingModifiers:) with a modifier that doesn't change the key glyph, i.e. .numericPad:
func onKeyDown(event: NSEvent) {
let characters = event.characters(byApplyingModifiers: .numericPad)
print("Key pressed: \(characters)")
}
Ideally you'd be able to pass in a mask that represents no modifiers at all, but the API doesn't seem to support it.
For completeness, here's how you could start writing a function that takes a UInt16 (CGKeyCode) and returns a string representation according to the user's keyboard:
func keyCodeToString(code: UInt16) -> String {
switch code {
// Keys that are the same across keyboards
// TODO: Fill in the rest
case 0x7A: return "<F1>"
case 0x24: return "<Enter>"
case 0x35: return "<Escape>"
// Keys that change between keyboards
default:
let cgEvent = CGEvent(keyboardEventSource: nil, virtualKey: code, keyDown: true)!
let nsEvent = NSEvent(cgEvent: cgEvent)!
let characters = nsEvent.characters(byApplyingModifiers: .numericPad)
return String(characters?.uppercased() ?? "<KeyCode: \(code)>")
}
}
The goal being for the F1 key to display <F1>, but the ";" key to display ; on US keyboards but Ñ on Spanish keyboards.

Resources