Как преобразовать локализованный строковый номер в двойной?

В моем коде есть число, заданное пользователем через TextField.

struct ContentView: View {
    @State private var numberText: String = ""
    
    var body: some View {
        TextField("0", text: $numberText)
    }
}

Мне нравится хранить это число как двойное. Мне известны следующие подходы:

// Option A
Double(numberText)

// Option B
try? Double(self, format: .number)

// Option C
(self as? NSString)?.doubleValue

// Option D
let formatStyle     = FloatingPointFormatStyle<Double>(locale: locale)
let parseStrategy   = FloatingPointParseStrategy(format: formatStyle, lenient: true)
let formatInput     = try? parseStrategy.parse(self)

// Option E
Manually parse by removing the locale.groupingSeparator and by replacing the locale.decimalSeparator with a '.' and then parse it with Double().

Проблема возникает в следующем сценарии. Это мои настройки:

  • Английский язык
  • Регион: Нидерланды
  • Форматер чисел: 1 234 567,89

Варианты A, B и C не будут работать, поскольку они не учитывают локаль. Они всегда будут пытаться проанализировать строку с точкой «23,45». Итак, вариант D подходит ближе всего, но если я укажу ему текущую локаль. При этом учитывается регион Нидерландов. Это означает, что он ожидает 23,45, а не 23.45. Однако клавиатура использует настройки средства форматирования чисел, поэтому точка в качестве десятичного разделителя будет отображаться правильно. Это означает, что пользователь наберет 23.45, и парсер выдаст неправильное число.

Вариант ответа

extension String {
    
    var doubleValue: Double? {
        var string = self.trimmingCharacters(in: .whitespacesAndNewlines)
        
        if let groupingSeparator = Locale.current.groupingSeparator {
            string = string.replacingOccurrences(of: groupingSeparator, with: "")
        }
        
        if let decimalSeparator = Locale.current.decimalSeparator, decimalSeparator != "." {
            string = string.replacingOccurrences(of: decimalSeparator, with: ".")
        }
        
        return Double(string)
    }
    
}

Почему бы не работать с двойным значением напрямую, init(_:value:format:prompt:)?

Joakim Danielson 25.05.2024 14:02

У него та же проблема. Если пользователь вводит 23,45, вместо этого он меняет числовой формат в настройках на использование .. TextField этого не отражает, но клавиатура (decimalPad) отражает. Таким образом, если пользователь удалил ,45 и теперь ввел .45, результат двойного значения станет 2345.0 вместо ожидаемого 23.45.

Mark 25.05.2024 15:08
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
59
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Похоже, что новые FormatStyle API не учитывают настройки форматирования чисел устройства и просто используют то, что установлено по умолчанию для локали, установленной в настройке «регион».

С другой стороны, старый NumberFormatter делает это правильно:

let formatter = NumberFormatter()
formatter.numberStyle = .decimal
// locale is Locale.current by default. You can optionally set this to the local from @Environment(\.locale)
// formatter.locale = ...
print(formatter.number(from: "1,6"))

Вы также можете передать NumberFormatter в TextField напрямую:

TextField("Foo", value: $value, formatter: someFormatter)

Вы подтверждаете мои мысли. Что API FormatStyle не учитывает настройки форматирования чисел устройства. NumberFormatter действительно так делает. Это исправило, спасибо за помощь. Также жаль, что TextField не обновляется автоматически до нового числового формата. Таким образом, . меняется на , при изменении форматирования чисел.

Mark 25.05.2024 15:40

Другие вопросы по теме