Skip to content

Commit 4ad2bdf

Browse files
committed
feat(currencyTF): option to specify decimal places
1 parent e521a8b commit 4ad2bdf

File tree

1 file changed

+43
-30
lines changed

1 file changed

+43
-30
lines changed

Sources/SwiftUIKit/views/CurrencyTextField.swift

Lines changed: 43 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public struct CurrencyTextField: UIViewRepresentable {
1414
private var isResponder: Binding<Bool>?
1515
private var tag: Int
1616
private var alwaysShowFractions: Bool
17+
private var numberOfDecimalPlaces: Int
1718

1819
private var placeholder: String
1920

@@ -44,7 +45,7 @@ public struct CurrencyTextField: UIViewRepresentable {
4445
isResponder: Binding<Bool>? = nil,
4546
tag: Int = 0,
4647
alwaysShowFractions: Bool = false,
47-
48+
numberOfDecimalPlaces: Int = 2,
4849
font: UIFont? = nil,
4950
foregroundColor: UIColor? = nil,
5051
accentColor: UIColor? = nil,
@@ -65,6 +66,7 @@ public struct CurrencyTextField: UIViewRepresentable {
6566
self.isResponder = isResponder
6667
self.tag = tag
6768
self.alwaysShowFractions = alwaysShowFractions
69+
self.numberOfDecimalPlaces = numberOfDecimalPlaces
6870

6971
self.font = font
7072
self.foregroundColor = foregroundColor
@@ -92,7 +94,7 @@ public struct CurrencyTextField: UIViewRepresentable {
9294

9395
// initial value
9496
if let v = self.value {
95-
textField.text = v.currencyFormat(decimalPlaces: self.alwaysShowFractions ? 2 : nil)
97+
textField.text = v.currencyFormat(decimalPlaces: self.numberOfDecimalPlaces, forceShowDecimalPlaces: self.alwaysShowFractions)
9698
}
9799

98100
// tag
@@ -170,7 +172,7 @@ public struct CurrencyTextField: UIViewRepresentable {
170172
}
171173

172174
public func makeCoordinator() -> CurrencyTextField.Coordinator {
173-
Coordinator(value: $value, isResponder: self.isResponder, alwaysShowFractions: self.alwaysShowFractions, onReturn: self.onReturn){ flag in
175+
Coordinator(value: $value, isResponder: self.isResponder, alwaysShowFractions: self.alwaysShowFractions, numberOfDecimalPlaces: self.numberOfDecimalPlaces, onReturn: self.onReturn){ flag in
174176
self.onEditingChanged(flag)
175177
}
176178
}
@@ -182,7 +184,7 @@ public struct CurrencyTextField: UIViewRepresentable {
182184
if self.value == nil {
183185
textField.text = nil
184186
} else {
185-
textField.text = self.value!.currencyFormat(decimalPlaces: self.alwaysShowFractions ? 2 : nil)
187+
textField.text = self.value!.currencyFormat(decimalPlaces: self.numberOfDecimalPlaces, forceShowDecimalPlaces: self.alwaysShowFractions)
186188
}
187189
}
188190

@@ -207,16 +209,24 @@ public struct CurrencyTextField: UIViewRepresentable {
207209
private var isResponder: Binding<Bool>?
208210
private var onReturn: ()->()
209211
private var alwaysShowFractions: Bool
212+
private var numberOfDecimalPlaces: Int
210213
var internalValue: Double?
211214
var onEditingChanged: (Bool)->()
212215
var didBecomeFirstResponder = false
213216

214-
init(value: Binding<Double?>, isResponder: Binding<Bool>?, alwaysShowFractions: Bool = false, onReturn: @escaping () -> Void = {}, onEditingChanged: @escaping (Bool) -> Void = { _ in }) {
217+
init(value: Binding<Double?>,
218+
isResponder: Binding<Bool>?,
219+
alwaysShowFractions: Bool,
220+
numberOfDecimalPlaces: Int,
221+
onReturn: @escaping () -> Void = {},
222+
onEditingChanged: @escaping (Bool) -> Void = { _ in }
223+
) {
215224
print("coordinator init")
216225
_value = value
217226
internalValue = value.wrappedValue
218227
self.isResponder = isResponder
219228
self.alwaysShowFractions = alwaysShowFractions
229+
self.numberOfDecimalPlaces = numberOfDecimalPlaces
220230
self.onReturn = onReturn
221231
self.onEditingChanged = onEditingChanged
222232
}
@@ -226,7 +236,7 @@ public struct CurrencyTextField: UIViewRepresentable {
226236
let originalText = textField.text
227237
let text = textField.text as NSString?
228238
let newValue = text?.replacingCharacters(in: range, with: string)
229-
let display = newValue?.currencyFormat
239+
let display = newValue?.currencyFormat(decimalPlaces: self.numberOfDecimalPlaces)
230240

231241
// validate change
232242
if !shouldAllowChange(oldValue: textField.text ?? "", newValue: newValue ?? "") {
@@ -293,7 +303,7 @@ public struct CurrencyTextField: UIViewRepresentable {
293303
}
294304

295305
// limits fractions length
296-
if newValue.fractions?.count ?? 0 > 2 {
306+
if newValue.fractions?.count ?? 0 > self.numberOfDecimalPlaces {
297307
return false
298308
}
299309

@@ -307,7 +317,7 @@ public struct CurrencyTextField: UIViewRepresentable {
307317
}
308318

309319
public func textFieldDidEndEditing(_ textField: UITextField) {
310-
textField.text = self.value?.currencyFormat(decimalPlaces: self.alwaysShowFractions ? 2 : nil)
320+
textField.text = self.value?.currencyFormat(decimalPlaces: self.numberOfDecimalPlaces, forceShowDecimalPlaces: self.alwaysShowFractions)
311321
DispatchQueue.main.async {
312322
self.isResponder?.wrappedValue = false
313323
}
@@ -370,25 +380,24 @@ fileprivate extension String {
370380
return Double(decimals) ?? 0
371381
}
372382

373-
var currencyFormat: String? {
383+
// args:
384+
// decimalPlaces - the max number of decimal places
385+
func currencyFormat(decimalPlaces: Int? = nil) -> String? {
374386
// uses self.double
375387
// logic for varying the number of fraction digits
376-
377388
guard let double = double else {
378389
return nil
379390
}
380391

381392
let formatter = Formatter.currency
382-
let fractionDigits = fractions?.count ?? 0
393+
383394
// if has fractions, show fractions
384395
if fractions != nil {
385-
if fractionDigits == 0 {
386-
formatter.maximumFractionDigits = 0
387-
} else if fractionDigits == 1 {
388-
formatter.maximumFractionDigits = 1
389-
} else {
390-
formatter.maximumFractionDigits = 2
391-
}
396+
// the number of decimal points in the string
397+
let fractionDigits = fractions?.count ?? 0
398+
// limited to the decimalPlaces specified in the argument
399+
formatter.minimumFractionDigits = min(fractionDigits, decimalPlaces != nil ? decimalPlaces! : 2)
400+
formatter.maximumFractionDigits = min(fractionDigits, decimalPlaces != nil ? decimalPlaces! : 2)
392401

393402
let formatted = formatter.string(from: NSNumber(value: double))
394403

@@ -397,33 +406,37 @@ fileprivate extension String {
397406
return "\(formatted)."
398407
}
399408

400-
return formatted
401-
} else {
402-
formatter.maximumFractionDigits = 0
403-
let formatted = formatter.string(from: NSNumber(value: double))
404409
return formatted
405410
}
411+
412+
formatter.maximumFractionDigits = 0
413+
let formatted = formatter.string(from: NSNumber(value: double))
414+
return formatted
406415
}
407416
}
408417

409418
fileprivate extension Double {
410-
func currencyFormat(decimalPlaces: Int? = nil) -> String? {
419+
// args:
420+
// decimalPlaces - number of decimal places
421+
// forceShowDecimalPlaces - whether to force show fractions
422+
func currencyFormat(decimalPlaces: Int? = nil, forceShowDecimalPlaces: Bool = false) -> String? {
411423
let formatter = Formatter.currency
412424
var integer = 0.0
413-
414-
// if decimal places specified
415-
if let decimalPlaces = decimalPlaces {
416-
formatter.maximumFractionDigits = 2
425+
let d = decimalPlaces != nil ? decimalPlaces! : 2
426+
427+
if forceShowDecimalPlaces {
428+
formatter.minimumFractionDigits = d
429+
formatter.maximumFractionDigits = d
417430
} else {
418-
// format to 2 decimal places if there's fractions
431+
// show fractions if exist
419432
let fraction = modf(self, &integer)
420433
if fraction > 0 {
421-
formatter.maximumFractionDigits = 2
434+
formatter.maximumFractionDigits = d
422435
} else {
423436
formatter.maximumFractionDigits = 0
424437
}
425438
}
426-
439+
427440
return formatter.string(from: NSNumber(value: self))
428441
}
429442
}

0 commit comments

Comments
 (0)