Last active
July 15, 2017 16:00
-
-
Save nickkohrn/6b8884d6fd706e61cb9048efac186ca8 to your computer and use it in GitHub Desktop.
This gist provides code for handling appropriate live-formatting of currency input for the current locale with respect to the placement of the currency's symbol.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
internal final class CurrencyService { | |
// MARK: - Currency Symbol Position | |
internal static var currencySymbolPosition: CurrencySymbolPosition { | |
let currencyFormat = CFNumberFormatterGetFormat(CFNumberFormatterCreate(nil, Locale.current as CFLocale, .currencyStyle)) as NSString | |
let positiveNumberFormat = currencyFormat.components(separatedBy: ";")[0] as NSString | |
let currencySymbolLocation = positiveNumberFormat.range(of: "¤").location | |
let position: CurrencySymbolPosition = currencySymbolLocation == 0 ? .before : .after | |
return position | |
} | |
// MARK: - CurrencySymbolPosition | |
internal enum CurrencySymbolPosition { | |
case before | |
case after | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
internal final class HorizontallyExpandableTextField: UITextField { | |
// MARK: - Properties | |
private var previousText: String? | |
internal override var intrinsicContentSize: CGSize { | |
var size = super.intrinsicContentSize | |
if isEditing, let lastTextBeforeEditing = previousText { | |
let originalSize = (lastTextBeforeEditing as NSString).size(attributes: typingAttributes) | |
size.width = size.width - originalSize.width | |
} | |
return size | |
} | |
// MARK: - Initialization | |
internal override init(frame: CGRect) { | |
super.init(frame: frame) | |
setupTextChangeNotification() | |
} | |
internal required init?(coder aDecoder: NSCoder) { | |
super.init(coder: aDecoder) | |
setupTextChangeNotification() | |
} | |
deinit { NotificationCenter.default.removeObserver(self) } | |
// MARK: - Notification Handling | |
internal func setupTextChangeNotification() { | |
NotificationCenter.default.addObserver(forName: .UITextFieldTextDidChange, object: self, queue: OperationQueue.main) { (notification) in | |
self.invalidateIntrinsicContentSize() | |
} | |
NotificationCenter.default.addObserver(forName: .UITextFieldTextDidBeginEditing, object: self, queue: OperationQueue.main) { (notification) in | |
self.previousText = self.text | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
internal func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { | |
if string.characters.count == 0 && range.length >= 1 { return true } | |
guard let text = textField.text else { return false } | |
guard let currencySymbol = formatter.currencySymbol else { return false } | |
guard let decimalSeparator = formatter.currencyDecimalSeparator else { return false } | |
guard let groupingSeparator = formatter.currencyGroupingSeparator else { return false } | |
guard let newText = (text as NSString?)?.replacingCharacters(in: range, with: string) else { return false } | |
let symbolsStripped = newText.replacingOccurrences(of: currencySymbol, with: "").replacingOccurrences(of: decimalSeparator, with: "").replacingOccurrences(of: groupingSeparator, with: "") | |
guard let integer = Int(symbolsStripped) else { return false } | |
let divisor = Int(pow(Double(10), Double(formatter.maximumFractionDigits))) | |
let double = Double(integer) / Double(divisor) | |
guard let formatted = formatter.string(from: double as NSNumber) else { return false } | |
textField.text = formatted.replacingOccurrences(of: currencySymbol, with: "").trimmingCharacters(in: .whitespaces) | |
return false | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This code is useful for handling formatted currency input for the current locale. It takes into account the location of the currency's symbol so that the currency can be live-formatted upon input without having to worry about disabling the ability to delete the currency symbol in the text input view.
For example, if you place an
UILabel
on either side of anUITextField
, you can determine the position of the currency's symbol and set the appropriate label to contain the symbol. You can then set thetext
of the unused label tonil
. This will display the currency symbol and the currency's value appropriately.Coupled with an instance of
HorizontallyExpandableTextField
, the trailing label can be configured to be pinned to the trailing edge of the text field. This will allow the label to keep an explicit distance from the trailing edge of the text field for currencies that display the symbol after the value.