web-dev-qa-db-fra.com

Quel est le meilleur moyen d'entrer des valeurs numériques avec des points décimaux?

Dans mon application, les utilisateurs doivent pouvoir saisir des valeurs numériques avec des décimales. L'iPhone ne fournit pas de clavier spécifique à cet effet - seulement un pavé numérique et un clavier avec des chiffres et des symboles.

Existe-t-il un moyen simple d’utiliser ce dernier et d’empêcher toute saisie non numérique sans devoir regex le résultat final?

Merci!

50
Andrew Grant

Une solution plus élégante s'avère être aussi la plus simple.

Vous n'avez pas besoin d'une clé de séparation décimale

Pourquoi? Parce que vous pouvez simplement le déduire de l'entrée de l'utilisateur. Par exemple, aux États-Unis, lorsque vous indiquez 1,23 USD, vous commencez par entrer les chiffres 1-2-3 (dans cet ordre). Dans le système, chaque caractère saisi est reconnu comme suit:

  • l'utilisateur entre 1: 0,01 $
  • l'utilisateur entre 2: 0,12 $
  • l'utilisateur entre 3: 1,23 $

Remarquez comment nous avons déduit le séparateur décimal en fonction des entrées de l'utilisateur. Maintenant, si l'utilisateur veut entrer 1,00 $, il lui suffit d'entrer les chiffres 1-0-0.

Pour que votre code puisse gérer les devises de paramètres régionaux différents, vous devez obtenir le nombre maximal de fractions de chiffres de la devise. Cela peut être fait avec l'extrait de code suivant:

NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
int currencyScale = [currencyFormatter maximumFractionDigits];

Par exemple, le yen japonais a un chiffre de fraction maximum de 0. Ainsi, lorsqu'il s'agit d'une entrée de yen, il n'y a pas de séparateur décimal et par conséquent, vous n'avez même pas à vous soucier des montants fractionnaires.

Cette approche du problème vous permet d’utiliser le clavier d’entrée numérique fourni par Apple sans les soucis liés aux claviers personnalisés, à la validation de regex, etc.

49
shek

Je pense qu'il serait bon de souligner que depuis iOS 4.1, vous pouvez utiliser la nouvelle variable UIKeyboardTypeDecimalPad.

Alors maintenant, il vous suffit de:

myTextField.keyboardType=UIKeyboardTypeDecimalPad;
52
Zebs

Voici un exemple de solution proposée dans la réponse acceptée. Cela ne gère pas les autres monnaies ou quoi que ce soit - dans mon cas, je n'avais besoin que d'une prise en charge des dollars, peu importe les paramètres régionaux/la monnaie, ce qui me convenait:

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range
    replacementString:(NSString *)string {

    double currentValue = [textField.text doubleValue];
    //Replace line above with this
    //double currentValue = [[textField text] substringFromIndex:1] doubleValue];
    double cents = round(currentValue * 100.0f);

    if ([string length]) {
        for (size_t i = 0; i < [string length]; i++) {
            unichar c = [string characterAtIndex:i];
            if (isnumber(c)) {
                cents *= 10;
                cents += c - '0'; 
            }            
        }
    } else {
        // back Space
        cents = floor(cents / 10);
    }

    textField.text = [NSString stringWithFormat:@"%.2f", cents / 100.0f];
    //Add this line
    //[textField setText:[NSString stringWithFormat:@"$%@",[textField text]]];
    return NO;
}

Les tours et les planchers sont importants a) car la représentation en virgule flottante perd parfois .00001 ou autre chose et b) la chaîne de format arrondissant toute précision que nous avons supprimée dans la partie arrière.

12
Mike Weller

Je voulais faire exactement la même chose, sauf avec des devises plutôt que des valeurs décimales droites. 

J'ai fini par créer une vue personnalisée contenant un UITextField et un UILabel. L'étiquette couvre le champ de texte, mais le champ de texte reçoit toujours des touches. J'utilise la notification UITextFieldTextDidChange pour observer les modifications dans le champ de texte (et j'ai utilisé NSNumberFormatter pour convertir le nombre obtenu en une valeur monétaire formatée) pour mettre à jour le libellé.

Pour désactiver la loupe qui permet à l'utilisateur de repositionner le point d'insertion, vous devez utiliser une sous-classe UITextField personnalisée et remplacer touchBegan: withEvent: et la définir pour ne rien faire.

Ma solution peut être différente de ce dont vous avez besoin car le point décimal est toujours fixe. J'utilise le paramètre de devise du système pour déterminer le nombre de chiffres après le point décimal. Cependant, le pavé numérique n'a pas de point décimal. Et vous ne pouvez pas ajouter de boutons au clavier (ce qui est particulièrement aggravant car il y a un bouton vide dans le coin inférieur gauche du clavier qui serait parfait pour une virgule décimale!). Je n'ai donc pas de solution pour cela. malheureusement.

3
Alex

Vous voudrez peut-être utiliser un curseur (comme suggéré par Martin v. Löwis) ou un UIPickerView avec une roue distincte pour chacun des chiffres.

2
Jeroen Heijmans

J'ai construit un contrôleur d'affichage personnalisé avec un point décimal ... regardez-le:

http://sites.google.com/site/psychopupnet/iphone-sdk/tenkeypadcustom10-keypadwithdecimal

2
Richard

En fonction de l'application spécifique, fournir un curseur sur lequel l'utilisateur peut sélectionner une position peut constituer un meilleur choix sur l'iphone. Ensuite, aucun chiffre ne doit être entré du tout.

2
Martin v. Löwis

Voici comment le faire sans utiliser float, round () ou ceil () de manière agnostique.

Dans votre vue contrôleur, configurez les variables d'instance suivantes (avec les instructions @property associées si c'est votre sac):

@interface MyViewController : UIViewController <UITextFieldDelegate> {
@private
    UITextField *firstResponder;
    NSNumberFormatter *formatter;
    NSInteger currencyScale;
    NSString *enteredDigits;
}

@property (nonatomic, readwrite, assign) UITextField *firstResponder;
@property (nonatomic, readwrite, retain) NSNumberFormatter *formatter;
@property (nonatomic, readwrite, retain) NSString *enteredDigits;

@end

et votre méthode viewDidLoad devrait contenir les éléments suivants:

- (void)viewDidLoad {
    [super viewDidLoad];

    NSNumberFormatter *aFormatter = [[NSNumberFormatter alloc] init];
    [aFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
    currencyScale = -1 * [aFormatter maximumFractionDigits];
    self.formatter = aFormatter;
    [aFormatter release];
}

Puis implémentez vos méthodes UITextFieldDelegate comme suit:

#pragma mark -
#pragma mark UITextFieldDelegate methods

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    // Keep a pointer to the field, so we can resign it from a toolbar
    self.firstResponder = textField;
    self.enteredDigits = @"";
}

- (void)textFieldDidEndEditing:(UITextField *)textField {
    if ([self.enteredDigits length] > 0) {
        // Get the amount
        NSDecimalNumber *result = [[NSDecimalNumber decimalNumberWithString:self.enteredDigits] decimalNumberByMultiplyingByPowerOf10:currencyScale];
        NSLog(@"result: %@", result);
    }
}

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {

    // Check the length of the string
    if ([string length]) {
        self.enteredDigits = [self.enteredDigits stringByAppendingFormat:@"%d", [string integerValue]];
    } else {
        // This is a backspace
        NSUInteger len = [self.enteredDigits length];
        if (len > 1) {
            self.enteredDigits = [self.enteredDigits substringWithRange:NSMakeRange(0, len - 1)];
        } else {
            self.enteredDigits = @"";
        }
    }

    NSDecimalNumber *decimal = nil;

    if ( ![self.enteredDigits isEqualToString:@""]) {
        decimal = [[NSDecimalNumber decimalNumberWithString:self.enteredDigits] decimalNumberByMultiplyingByPowerOf10:currencyScale];
    } else {
        decimal = [NSDecimalNumber zero];
    }

    // Replace the text with the localized decimal number
    textField.text = [self.formatter stringFromNumber:decimal];

    return NO;  
}

Seulement testé cela avec des livres et pence, mais cela devrait fonctionner avec le yen japonais aussi. Si vous souhaitez formater des décimales à des fins non monétaires, lisez simplement la documentation sur NSNumberFormatter et définissez le format/maximumFractionDigits souhaité.

1
Daniel Thorpe

Depuis iOS4.1, il existe un nouveau type de clavier UIKeyboardTypeNumberPad, mais malheureusement, il ne semble pas encore apparaître dans la liste de sélection Interface Builder.

1
Screwtape

Vous pouvez utiliser STATextField et définir currencyReprésentation sur YES qui:

Veille à ce qu’il n’y ait pas plus d’une virgule décimale et pas plus de 2 chiffres à la droite de cette virgule.

Il y a aussi STAATMTextField qui prend en charge le mode monétaire et Entrée de texte ATM par défaut:

Fournit un champ de texte qui imite le comportement d'entrée de la machine ATM.

0
Stunner

Une mise en œuvre Swift 2 du message de Mike Weller, également en USD:

 func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    guard let str = textField.text else {
        textField.text = "0.00"
        return false
    }

    let value = (str as NSString).doubleValue

    var cents = round(value * 100)
    if string.characters.count > 0 {
        for c in string.characters {
            if let num = Int(String(c)) {
                cents *= 10
                cents += Double(num)
            }
        }
    }
    else {
        cents = floor(cents / 10)
    }

    textField.text = NSString(format: "%.2f", cents/100) as String

    return false
}
0
ccwasden