2009-12-25 8 views
0

Après plus d'une journée de fouiner avec ce problème, je vais voir si je peux obtenir de l'aide. Cette question a été plus ou moins posée auparavant, mais il semble que personne ne donne de réponse complète, alors j'espère que nous pourrons l'obtenir maintenant. En utilisant un UILabel et un UITextView (clavier w/number), je souhaite obtenir un comportement similaire à celui de l'ATM, en laissant les utilisateurs taper simplement les nombres et les mettre en forme comme devise dans l'étiquette. L'idée est essentiellement décrit ici:Conversion NSString en monnaie - L'histoire complète

What is the best way to enter numeric values with decimal points?

Le seul problème est qu'il n'a jamais dit explicitement comment nous pouvons aller d'avoir un entier comme 123 dans le champ de texte et l'affichage de l'étiquette 1,23 $ ou 123 ¥ etc. Quelqu'un at-il un code qui fait cela?

Répondre

3

J'ai trouvé une solution, et dans le but de cette question, je vais fournir une réponse complète pour ceux qui ont ce problème dans le futur. J'ai d'abord créé une nouvelle classe d'assistance appelée NumberFormatting et créé deux méthodes.

// 
// NumberFormatting.h 
// Created by Noah Hendrix on 12/26/09. 
// 

#import <Foundation/Foundation.h> 


@interface NumberFormatting : NSObject { 

} 

-(NSString *)stringToCurrency:(NSString *)aString; 
-(NSString *)decimalToIntString:(NSDecimalNumber *)aDecimal; 

@end 

et est ici le fichier de mise en œuvre:

// 
// NumberFormatting.m 
// Created by Noah Hendrix on 12/26/09. 
// 

#import "NumberFormatting.h" 


@implementation NumberFormatting 

    -(NSString *)stringToCurrency:(NSString *)aString { 
    NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init]; 
    [currencyFormatter setGeneratesDecimalNumbers:YES]; 
    [currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle]; 

    if ([aString length] == 0) 
     aString = @"0"; 

    //convert the integer value of the price to a decimal number i.e. 123 = 1.23 
    //[currencyFormatter maximumFractionDigits] gives number of decimal places we need to have 
    //multiply by -1 so the decimal moves inward 
    //we are only dealing with positive values so the number is not negative 
    NSDecimalNumber *value = [NSDecimalNumber decimalNumberWithMantissa:[aString integerValue] 
                   exponent:(-1 * [currencyFormatter maximumFractionDigits]) 
                   isNegative:NO]; 

    return [currencyFormatter stringFromNumber:value]; 
    } 

    -(NSString *)decimalToIntString:(NSDecimalNumber *)aDecimal { 
    NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init]; 
    [currencyFormatter setGeneratesDecimalNumbers:YES]; 
    [currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle]; 

    if (aDecimal == nil) 
     aDecimal = [NSDecimalNumber zero]; 

    NSDecimalNumber *price = [NSDecimalNumber decimalNumberWithMantissa:[aDecimal integerValue] 
                   exponent:([currencyFormatter maximumFractionDigits]) 
                   isNegative:NO]; 

    return [price stringValue]; 
    } 

@end 

La première méthode, stringToCurrency, prendra un nombre entier (passé à partir d'un champ de texte dans ce cas) et le convertir en une valeur décimale en utilisant déplacer le point décimal en fonction des paramètres régionaux des utilisateurs. Il renvoie ensuite une représentation de chaîne formatée en tant que devise à l'aide de NSNumberFormatter.

La deuxième méthode fait l'inverse, il prend une valeur comme 1.23 et le convertit en 123 en utilisant une méthode similaire.

Voici un exemple de la façon dont je l'ai utilisé

... 
self.accountBalanceCell.textField.text = [[NumberFormatting alloc] decimalToIntString:account.accountBalance]; 
... 
[self.accountBalanceCell.textField addTarget:self 
              action:@selector(updateBalance:) 
            forControlEvents:UIControlEventEditingChanged]; 

Ici nous définissons la valeur du champ de texte à la valeur décimale de la banque de données, puis nous avons fixé un observateur à surveiller les changements au texte champ et exécutez la méthode updateBalance

- (void)updateBalance:(id)sender { 
    UILabel *balanceLabel = (UILabel *)[accountBalanceCell.contentView viewWithTag:1000]; 
    NSString *value  = ((UITextField *)sender).text; 
    balanceLabel.text  = [[NumberFormatting alloc] stringToCurrency:value]; 
} 

qui prend simplement la valeur textfield et l'exécuter par la méthode de stringToCurrency décrit ci-dessus.

Pour moi, cela semble hackish alors s'il vous plaît prendre le temps de regarder et de le nettoyer si vous êtes intéressé à l'utiliser. Aussi je remarque pour les grandes valeurs que ça casse.

2

Jetez un coup d'œil à NSNumberFormatter, qui formatera les données numériques en fonction des paramètres régionaux actuels ou spécifiés.

+0

J'ai examiné, mais il faudra 123 et le convertir en 123,00 $ quand l'idée est de Convertissez-le en $ 1.23 –

+0

Peut-être pourriez-vous lire la longueur de la chaîne et, en tenant compte des paramètres régionaux, la convertir en 1,23 - formaté comme 1,23 $ - au lieu de 123,00, formaté comme $ 123.00. –

+0

Par exemple, vous pouvez appeler '-currencyDecimalSeparator' et' -currencyGroupingSeparator' sur le NSNumberFormatter pour savoir comment gérer la mise en forme. –

0

Comme je ne voyais toujours pas correctement Les réponses à cette question, je vais partager ma solution sans utiliser le NSScanner (le scanner ne semble pas fonctionner pour moi). Est-ce une combinaison de ce "What is the best way to enter numeric values with decimal points?" et de ces réponses "Remove all but numbers from NSString".

D'abord, je présente un NSString avec les utilisateurs des paramètres en monnaie locale dans un UITextField comme celui-ci:

//currencyFormatter is of type NSNumberFormatter 
if (currencyFormatter == nil) { 
    currencyFormatter = [[NSNumberFormatter alloc] init]; 
    [currencyFormatter setLocale:[NSLocale currentLocale]]; 
    [currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle]; 
    //[currencyFormatter setGeneratesDecimalNumbers:YES]; 
    decimalSeperator = [currencyFormatter decimalSeparator]; //NSString 
    currencyScale = [currencyFormatter maximumFractionDigits]; //short 
    //[currencyFormatter release]; don't forget to release the Formatter at one point 

} 


//costField is of type UITextField 
NSDecimalNumber *nullValue = [NSDecimalNumber decimalNumberWithMantissa:0 exponent:currencyScale isNegative:NO]; 
[costField setText:[currencyFormatter stringFromNumber:nullValue]]; 

Vous pouvez le faire dans la méthode viewControllers viewDidLoad :. En fonction des paramètres de l'utilisateur, une chaîne de caractères s'affichera comme ceci: $ 0.00 (pour les paramètres locaux United Stated).Selon votre situation, vous pourriez vouloir présenter une valeur de votre modèle de données.

Lorsque l'utilisateur touche l'intérieur du champ de texte, je présenterai un clavier de type:

costField.keyboardType = UIKeyboardTypeDecimalPad; 

Cela empêche l'utilisateur d'entrer quoi que ce soit d'autre, mais les chiffres.

Dans la méthode déléguée UITextField suivante, je sépare la chaîne pour obtenir uniquement les numéros (ici, j'évite d'utiliser NSScanner). Cela est possible, parce que je sais où placer le séparateur décimal en utilisant le avant spécifié la valeur « currencyScale »:

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

if (textField == costField) { 

    //if for what ever reason ther currency scale is not available set it to 2 
    //which is the most common scale value 
    if (!currencyScale) { 
     currencyScale = 2; 
    } 

    // separate string from all but numbers 
    // https://stackoverflow.com/questions/1129521/remove-all-but-numbers-from-nsstring/1163595#1163595 
    NSString *aString = [textField text]; 
    NSMutableString *strippedString = [NSMutableString stringWithCapacity:10]; 
    for (int i=0; i<[aString length]; i++) { 
     if (isdigit([aString characterAtIndex:i])) { 
      [strippedString appendFormat:@"%c",[aString characterAtIndex:i]]; 
     } 
    } 

    //add the newly entered character as a number 
    // https://stackoverflow.com/questions/276382/what-is-the-best-way-to-enter-numeric-values-with-decimal-points/2636699#2636699 
    double cents = [strippedString doubleValue]; 
    NSLog(@"Cents:%f ",[strippedString doubleValue]); 
    if ([string length]) { 
     for (size_t i = 0; i < [string length]; i++) { 
      unichar c = [string characterAtIndex:i]; 
      if (isnumber(c)) { 
       cents *= 10;  //multiply by 10 to add a 0 at the end 
       cents += c - '0'; // makes a number out of the charactor and replace the 0 (see ASCII Table) 
      }    
     } 
    } 
    else { 
     // back Space if the user delete a number 
     cents = floor(cents/10); 
    } 

    //like this you could save the value as a NSDecimalNumber in your data model 
    //costPerHour is of type NSDecimalNumber 
    self.costPerHour = [NSDecimalNumber decimalNumberWithMantissa:cents exponent:-currencyScale isNegative:NO]; 

    //creat the string with the currency symbol and the currency separator 
    [textField setText:[currencyFormatter stringFromNumber:costPerHour]]; 

    return NO; 
    } 

return YES; 
} 

De cette façon, la monnaie saisie par l'utilisateur sera toujours correcte et il n'y a pas besoin de vérifier il. Peu importe les paramètres de devise sélectionnés, la devise correctement mise en forme sera toujours utilisée.

0

Je n'ai pas vraiment aimé les réponses existantes ici, donc j'ai combiné quelques techniques. J'ai utilisé un UITextField caché avec le clavier du pavé numérique pour la saisie, et un UILabel visible pour le formatage.

Je dois propriétés qui accrochons à tout:

@property (weak, nonatomic) IBOutlet UILabel *amountLabel; 
@property (weak, nonatomic) IBOutlet UITextField *amountText; 
@property (retain, nonatomic) NSDecimalNumber *amount; 

J'ai la quantité et NSNumberFormatter comme Ivars:

NSDecimalNumber *amount_; 
NSNumberFormatter *formatter; 

configurer mon formatter à Init:

- (id)initWithCoder:(NSCoder *)aDecoder 
{ 
    self = [super initWithCoder:aDecoder]; 
    if (self) { 
     // Custom initialization 
     formatter = [NSNumberFormatter new]; 
     formatter.numberStyle = NSNumberFormatterCurrencyStyle; 
    } 
    return self; 
} 

Voici le code que j'utilise pour valider l'entrée convertir en

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string 
{ 
    NSString *asText = [textField.text stringByReplacingCharactersInRange:range withString:string]; 
    if ([asText length] == 0) { 
     [self setAmount:[NSDecimalNumber zero]]; 
     return YES; 
    } 
    // We just want digits so cast the string to an integer then compare it 
    // to itself. If it's unchanged then it's workable. 
    NSInteger asInteger = [asText integerValue]; 
    NSNumber *asNumber = [NSNumber numberWithInteger:asInteger]; 
    if ([[asNumber stringValue] isEqualToString:asText]) { 
     // Convert it to a decimal and shift it over by the fractional part. 
     NSDecimalNumber *newAmount = [NSDecimalNumber decimalNumberWithDecimal:[asNumber decimalValue]]; 
     [self setAmount:[newAmount decimalNumberByMultiplyingByPowerOf10:-formatter.maximumFractionDigits]]; 
     return YES; 
    } 
    return NO; 
} 

J'ai ce sertisseur que les poignées mise en forme l'étiquette et permettant le bouton fait:

-(void)setAmount:(NSDecimalNumber *)amount 
{ 
    amount_ = amount; 
    amountLabel.text = [formatter stringFromNumber:amount]; 
    self.navigationItem.rightBarButtonItem.enabled = [self isValid]; 
}