Set maximum characters (to one) in a NSTextfield in Swift
You don't need to limit the characters a user will enter just only look at the first character entered. In fact, it is probably better since you will always have to handle possible user errors. If you want to you can issue an alert that they entered too many by getting the characters.count. You might want an alert if they don't answer at all. The code below will work as is if you set up a storyboard with 1 NSTextField and one button and connect them. If you have more than one textfield, i.e. like a multiple choice test, just set up all the text fields the same way.
import Cocoa
class ViewController: NSViewController {
@IBOutlet weak var firstLetter: NSTextField!
Add as many text fields as you need:
@IBOutlet weak var secondLetter: NSTextField!
@IBOutlet weak var thirdLetter: NSTextField!
etc.
@IBAction func button(sender: AnyObject) {
var firstEntry = firstLetter!.stringValue
var index1 = firstEntry.startIndex
if firstEntry.characters.count > 1 {
runMyAlert("Bad USER! ONLY ONE Character!")
}
if firstEntry == "" { //left it blank
runMyAlert("You need to enter at least one character!")
exit(0) //or you'll crash on next line
}
var nameLetter1:Character = firstEntry[index1]
print( "First Letter == \(nameLetter1) ")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
func runMyAlert( alertMessage: String){
var myWindow = NSWindow.self
let alert = NSAlert()
alert.messageText = "ERROR ERROR ERROR"
alert.addButtonWithTitle("OK")
alert.informativeText = alertMessage
alert.runModal()
}
}
How to limit NSTextField text length and keep it always upper case?
I did as Graham Lee suggested and it works fine, here's the custom formatter code:
UPDATED: Added fix reported by Dave Gallagher. Thanks!
@interface CustomTextFieldFormatter : NSFormatter {
int maxLength;
}
- (void)setMaximumLength:(int)len;
- (int)maximumLength;
@end
@implementation CustomTextFieldFormatter
- (id)init {
if(self = [super init]){
maxLength = INT_MAX;
}
return self;
}
- (void)setMaximumLength:(int)len {
maxLength = len;
}
- (int)maximumLength {
return maxLength;
}
- (NSString *)stringForObjectValue:(id)object {
return (NSString *)object;
}
- (BOOL)getObjectValue:(id *)object forString:(NSString *)string errorDescription:(NSString **)error {
*object = string;
return YES;
}
- (BOOL)isPartialStringValid:(NSString **)partialStringPtr
proposedSelectedRange:(NSRangePointer)proposedSelRangePtr
originalString:(NSString *)origString
originalSelectedRange:(NSRange)origSelRange
errorDescription:(NSString **)error {
if ([*partialStringPtr length] > maxLength) {
return NO;
}
if (![*partialStringPtr isEqual:[*partialStringPtr uppercaseString]]) {
*partialStringPtr = [*partialStringPtr uppercaseString];
return NO;
}
return YES;
}
- (NSAttributedString *)attributedStringForObjectValue:(id)anObject withDefaultAttributes:(NSDictionary *)attributes {
return nil;
}
@end
SWIFT: Nstextfield allow only specified characters
First add a NSTextFieldDelegate to your class... and then
add this :
override func controlTextDidChange(obj: NSNotification) {
let characterSet: NSCharacterSet = NSCharacterSet(charactersInString: " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLKMNOPQRSTUVWXYZ0123456789-_").invertedSet
self.textField.stringValue = (self.textField.stringValue.componentsSeparatedByCharactersInSet(characterSet) as NSArray).componentsJoinedByString("")
}
you have to replace self.textfield with your own textfield which you want to control.
SWIFT 4 Edit
override func controlTextDidChange(_ obj: Notification) {
let characterSet: NSCharacterSet = NSCharacterSet(charactersIn: " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLKMNOPQRSTUVWXYZ0123456789-_").inverted as NSCharacterSet
self.textField.stringValue = (self.textField.stringValue.components(separatedBy: characterSet as CharacterSet) as NSArray).componentsJoined(by: "")
}
COCOA - How to make textfield doesn't allow text entry when max length is reached in NSTextfield?
At its simplest, all you need is to subclass Formatter
and override 3 methods:
class MyFormatter: Formatter {
var maxLength = Int.max
override func string(for obj: Any?) -> String? {
return obj as? String
}
override func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer<AnyObject?>?, for string: String, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {
obj?.pointee = string as NSString
return true
}
override func isPartialStringValid(_ partialString: String, newEditingString newString: AutoreleasingUnsafeMutablePointer<NSString?>?, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {
return partialString.count <= maxLength
}
}
Then set the formatter for your textfield:
let formatter = MyFormatter()
formatter.maxLength = 10
textField.formatter = formatter
This is enough to guard against users typing in more than 10 characters or paste in strings that are long than 10 characters. If you want more advanced features like taking the first 10 characters when pasting in a long string, you should override isPartialStringValid(_:proposedSelectedRange:originalString:originalSelectedRange:errorDescription:)
.
Restrict NSTextField to only allow numbers
Try to make your own NSNumberFormatter
subclass and check the input value in -isPartialStringValid:newEditingString:errorDescription:
method.
@interface OnlyIntegerValueFormatter : NSNumberFormatter
@end
@implementation OnlyIntegerValueFormatter
- (BOOL)isPartialStringValid:(NSString*)partialString newEditingString:(NSString**)newString errorDescription:(NSString**)error
{
if([partialString length] == 0) {
return YES;
}
NSScanner* scanner = [NSScanner scannerWithString:partialString];
if(!([scanner scanInt:0] && [scanner isAtEnd])) {
NSBeep();
return NO;
}
return YES;
}
@end
And then set this formatter to your NSTextField
:
OnlyIntegerValueFormatter *formatter = [[[OnlyIntegerValueFormatter alloc] init] autorelease];
[textField setFormatter:formatter];
How do I programatically move the cursor from one NSTextField to another after a character count has been reached?
The answer to part of this this question was posted here by @cheesey
here is the complete code to create a window that takes the license key for a product from a user (This is using swift 4)
.
First set the Text Fields as the delegates and first responders in the viewDidLoad
Function and then change the first responder once the string limit is hit
class CommercialActivationView: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
@IBOutlet weak var firsttextfield: NSTextField!
@IBOutlet weak var secondtextfield: NSTextField!
@IBOutlet weak var thirdtextfield: NSTextField!
firsttextfield.window?.makeFirstResponder(firsttextfield)
firsttextfield.delegate = self
}
func makeFirstResponder() {
if firsttextfield.stringValue.count == 5 {
firsttextfield.window?.makeFirstResponder(secondtextfield)
}
if secondtextfield.stringValue.count == 5 {
secondtextfield.window?.makeFirstResponder(thirdtextfield)
}
}
}
Now to create the extension that creates the character limit or the text field every time the user edits the TextField
(Here i'm limiting the number of characters per text field to 5)
.
extension CommercialActivationView: NSTextFieldDelegate {
func controlTextDidChange(_ obj: Notification) {
let object = obj.object as! NSTextField
if object.stringValue.count > 5{
object.stringValue = String(object.stringValue.dropLast())
makeFirstResponder()
}
}
This works such that once 5 characters are reached in 1 TextField
it switches to the next one automatically. Also the code I've posted is for 3 TextFields
more text fields can be add if needed.
Set the maximum character length of a UITextField
While the UITextField
class has no max length property, it's relatively simple to get this functionality by setting the text field's delegate
and implementing the following delegate method:
Objective-C
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
// Prevent crashing undo bug – see note below.
if(range.length + range.location > textField.text.length)
{
return NO;
}
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return newLength <= 25;
}
Swift
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let currentCharacterCount = textField.text?.count ?? 0
if range.length + range.location > currentCharacterCount {
return false
}
let newLength = currentCharacterCount + string.count - range.length
return newLength <= 25
}
Before the text field changes, the UITextField asks the delegate if the specified text should be changed. The text field has not changed at this point, so we grab it's current length and the string length we're inserting (either through pasting copied text or typing a single character using the keyboard), minus the range length. If this value is too long (more than 25 characters in this example), return NO
to prohibit the change.
When typing in a single character at the end of a text field, the range.location
will be the current field's length, and range.length
will be 0 because we're not replacing/deleting anything. Inserting into the middle of a text field just means a different range.location
, and pasting multiple characters just means string
has more than one character in it.
Deleting single characters or cutting multiple characters is specified by a range
with a non-zero length, and an empty string. Replacement is just a range deletion with a non-empty string.
A note on the crashing "undo" bug
As is mentioned in the comments, there is a bug with UITextField
that can lead to a crash.
If you paste in to the field, but the paste is prevented by your validation implementation, the paste operation is still recorded in the application's undo buffer. If you then fire an undo (by shaking the device and confirming an Undo), the UITextField
will attempt to replace the string it thinks it pasted in to itself with an empty string. This will crash because it never actually pasted the string in to itself. It will try to replace a part of the string that doesn't exist.
Fortunately you can protect the UITextField
from killing itself like this. You just need to ensure that the range it proposes to replace does exist within its current string. This is what the initial sanity check above does.
swift 3.0 with copy and paste working fine.
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let str = (textView.text + text)
if str.characters.count <= 10 {
return true
}
textView.text = str.substring(to: str.index(str.startIndex, offsetBy: 10))
return false
}
Hope it's helpful to you.
Related Topics
iOS Charts - Single Values Not Showing Swift
A Warning "'Init()' Is Deprecated". [Swift, iOS App, Learning Model]
Swift Spritekit Get Visible Frame Size
Heightanchor.Constraint Not Change Height of View
Why Is There Multiple Collision Calls Sprite Kit Swift
How to Properly Map JSON Properties to Model Properties in Realm.Create
Create Objects/Instances in Variables Swift
Swift: Method Overriding in Parameterized Class
Execute Code on Main Thread from Async F#
Swift - Nsurl Fileurlwithpath Not Unwrapped
Cannot Assign Value of Type 'Menuview' to Type 'some View'