i'm using the library "PhoneNumberKit"
to format a UITextfield i'm using on my app that i use it to input phone number.
i am using PartialFormatter to format the input into a phone number mask style.
#IBAction func onPhoneNumberChanged(_ sender: Any) {
if (phoneNumberTextField.text?.count ?? 0 < 15) {
phoneNumberTextField.text = PartialFormatter().formatPartial(phoneNumberTextField.text ?? "")
} else {
phoneNumberTextField.deleteBackward()
}
}
my problem is that it always format the string to a LOCAL phone number, i want to force it to format it into a US phone number mask.
the library say:
The default region code is automatically computed but can be overridden if needed.
but doesn't give any examples on force region code to be to a specific country
here is the library i'm using:
https://github.com/marmelroy/PhoneNumberKit
does someone here has any exmaples on how to do it ?
thanks
fixed it by doing:
phoneNumberTextField.text = PartialFormatter(phoneNumberKit: PhoneNumberKit(), defaultRegion: "US", withPrefix: true).formatPartial(phoneNumberTextField.text ?? "")
The default region code is automatically computed but can be overridden if needed like given below
class MyTextField: PhoneNumberTextField {
override var defaultRegion: String {
get {
return "US"
}
set {} // exists for backward compatibility
}
}
Reference
Related
this works great for just integer values, i found this from another stackoverflow question, but how do i set the style to allow for a decimal place as well. I'm very new to macOS, any help is appreciated!
class OnlyIntegerValueFormatter: NumberFormatter {
override func isPartialStringValid(_ partialString: String, newEditingString newString: AutoreleasingUnsafeMutablePointer<NSString?>?, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {
// Ability to reset your field (otherwise you can't delete the content)
// You can check if the field is empty later
if partialString.isEmpty {
return true
}
// Optional: limit input length
if partialString.count > 8 {
return false
}
// Actual check
return Int(partialString) != nil
}
}
To allow decimal numbers, you could update your return statement to the following
// ...
return number(from: partialString) != nil
}
Then you are allowing integer as well as decimal numbers
There are even further options available (e.g. maximumIntegerDigits) to restrict your input by using NumberFormatter (https://developer.apple.com/documentation/foundation/numberformatter)
I have a piece of code intended to allow a user to change which unit of weight they are using. If they've already entered a value it is automatically converted between kilograms and pounds. It is changed by tapping on a UISegmentedControl where segment 0 is kilogram and segment 1 is pound.
Changing the first time works perfectly regardless of if the user went from kilogram to pound or vice versa. However, upon attempting to make a second conversion the program immediately crashes claiming that the value in the text field containing weight is nil even though it clearly contains text.
Here is my code as of now:
#IBAction func unitChanged(_ sender: UISegmentedControl) {
if let text = weightInput.text, text.isEmpty {
//Does nothing since there is no weight to change the unit of
}
else {
let weight: Int? = Int(weightInput.text!)
var weightDouble: Double = Double(weight!)
if (weightTypeInput.selectedSegmentIndex == 0) {
//The user is trying to change from pounds to kilograms
weightDouble = weightDouble * 0.45359237
}
else {
//The user is trying to change from kilograms to pounds
weightDouble = weightDouble * 2.2046226218
}
weightInput.text = String(weightDouble)
}
}
Here is what the code looks like in practice
Finally here is the error message I get:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
If I put a default value for weightDouble it is always used for all subsequent conversion attempts. Has anyone here encountered a similar issue? I attempted to search online but I couldn't find anything to help.
Are you sure that the text value of your TextField is nil? Since you're simply checking it in the first if in my opinion that would not be the case.
Int can't parse numbers with a floating-point in them. It returns nil when you do this. You might want to parse it into a Double first and then convert it to Int.
I think your code should be as follows:
#IBAction func unitChanged(_ sender: UISegmentedControl) {
guard let text = weightInput.text, !text.isEmpty, var weightDouble = Double(text) else { return }
if (weightTypeInput.selectedSegmentIndex == 0) {
//The user is trying to change from pounds to kilograms
weightDouble = weightDouble * 0.45359237
}
else {
//The user is trying to change from kilograms to pounds
weightDouble = weightDouble * 2.2046226218
}
weightInput.text = String(Int(weightDouble))
}
I’ve found this awesome extension that remove everything except the characters in the quotation marks.
extension String.UnicodeScalarView {
var removeCharacters: String {
return String(filter(("cfh".unicodeScalars).contains))
}
}
print("abcd123efg".unicodeScalars.removeCharacters) // it prints “cf”, my desirable result is to make it print “abd123eg”
It prints “cf”, my desirable result is to make it print “abd123eg”.
Can you help me invert it to remove only the characters that are between the quotation marks and leave everything else?
Note:It is also important that it recognize (unicodeScalars) so please don’t remove this part.
You need to negate the call to contains:
return String(filter { !"cfh".unicodeScalars.contains($0) })
Taking #rmaddy into consideration, if you are using extension then make sure you think about generics and work in any case. Like you have kept "cfh" as a static String, what if you want to remove "abc", then it won't work. So, here is the modified version:
extension String.UnicodeScalarView {
func removeCharacters(_ characters: String) -> String {
return String(filter { !characters.unicodeScalars.contains($0) })
}
}
Usage: "abcd123efg".unicodeScalars.removeCharacters("cfh")
Create an extension for String like below
extension String {
func removeGroupOfCharacters(removalString : String) -> String {
// return an Array without the removal Characters
let filteredCharacters = Array(self).filter { !Array(removalString).contains($0) }
// build a String with the filtered Array
let filtered = String(filteredCharacters)
return filtered
}
}
override func viewDidLoad() {
super.viewDidLoad()
let unfiltered = "abcd123efg"
let removal = "cfh"
print(unfiltered.removeGroupOfCharacters(removalString: removal))
// => output will be like this "abd123eg\n"
}
It's a basic example. You can do like that.
extension String {
return aString.replacingOccurrences(of: "X", with: "")
}
Hello Sultans of Swift!
I am new to Swift although I have used C, C++ and C# a lot. I have come upon a situation that is really puzzling me. I'm going to post a code snippet here:
#IBAction func myFunc(sender: AnyObject)
{
let dirPicker: NSOpenPanel = NSOpenPanel()
dirPicker.allowsMultipleSelection = false
dirPicker.canChooseFiles = true
dirPicker.canChooseDirectories = false
dirPicker.runModal()
let selection = dirPicker.URL
if(selection != nil)
{
do
{
print(selection)
let mp3File = try MP3File(path: (selection?.path)!)
let title = mp3File.getTitle()
// This prints OK
print("Title:\t\(mp3File.getTitle())")
// This prints OK too
print("Title:\t\(title)")
print("Artist:\t\(mp3File.getArtist())")
print("Album:\t\(mp3File.getAlbum())")
print("Lyrics:\n\(mp3File.getLyrics())")
fileName.stringValue = (selection?.path)!
// This sets the label songTitle to an empty space and I can't see why.
// If I initialise title to:
// let title = "STRING CONSTANT"
// ...instead of
// let title = mp3File.getTitle()
// ...then it does actually set the correct text on the label songTitle.
// In both cases, printing to the console works fine! Its just setting
// the text on the label that is eluding me!
songTitle.stringValue = title
}
catch ID3EditErrors.FileDoesNotExist
{
print("Error: File Does Not Exist")
}
catch ID3EditErrors.NotAnMP3
{
print("Error: Not an MP3")
}
catch let e
{
print(e)
}
}
}
When I try to set the text in a label by setting its stringValue property to a variable it just displays empty space, yet I can actually print the variable to the console just fine. The variable is set as the return value of a function. Now if I instead set the variable explicitly to a string constant then it works. So this will be perhaps related to the uncertainty of the return value of the function, but I know it contains the text because I can print it to the console.
Can anyone spot what on earth is happening here?
Thanks
EDIT: I just fixed the comments in the code to refer to songTitle instead of fileName - sorry for the confusion. This is about setting songTitle.stringValue = title
EDIT: This is the definition of songTitle:
#IBOutlet weak var fileName: NSTextField!
#IBOutlet weak var songTitle: NSTextField!
Note that setting the stringValue property of these does actually work so long as I am not using the a variable that is assigned the return value of mp3File.getTitle(). Note also that mp3File.getTitle() does return a value and I can print it to the console OK.
When you have a String value which is printed fine:
print(title) //->I Ran
But cannot be processed well:
songTitle.stringValue = title //->`songTitle` shows nothing!!!
(Of course, you have confirmed that songTitle is fine by assigning constant string to it.)
One possible reason may be existing some control characters in the String.
You can use debugPrint to reveal such cases:
debugPrint(title) //->"\0I Ran\0"
debugPrint uses String literal-like format to show control characters.
In this case, you have NUL characters (U+0000) at both ends.
So, one quick fix is trimming them at each time you get such strings:
Swift 2:
let title = mp3File.getTitle().stringByTrimmingCharactersInSet(NSCharacterSet(charactersInString: "\0"))
Swift 3:
let title = mp3File.getTitle().trimmingCharacters(in: CharacterSet(charactersIn: "\0"))
Or you can write an extension, if you cannot touch the original source of the library:
Swift 2:
extension MP3File {
var title: String {
return self.getTitle().stringByTrimmingCharactersInSet(NSCharacterSet(charactersInString: "\0"))
}
}
Swift 3:
extension MP3File {
var title: String {
return self.getTitle().trimmingCharacters(in: CharacterSet(charactersIn: "\0"))
}
}
(Assuming MP3File does not have a property named title.)
and use it as:
let title = mp3File.title
I'm trying to implement search inside my app that I'm making. I have an array that I'm trying to search and I find this code online:
func filterContentForSearchText(searchText: String) {
filteredCandies = candies.filter({( candy : Candies) -> Bool in
if candy.name.lowercaseString.containsString(searchText.lowercaseString) == true {
return true
} else {
return false
}
})
tableView.reloadData()
}
The issue is that the database that I'm trying to implement search on has text that is all scrambled because it was supposed to shortened. How can I make it so that the search will check if all the letters are there instead of searching exactly the right name. Example of object from database (USDA): CRAB, DUNGINESS, RAW
If you have an answer, please make it fast enough for live searching. Non live searching makes searching terrible (at least for me)!
I'm using Swift 2.2 and Xcode 7
As an improvement to #appzYourLife's solution, you could do this with a native Swift Set, as a counted set isn't necessarily needed in this case. This will save having to map(_:) over the characters of each name and bridging them to Objective-C. You can now just use a set of Characters, as they're Hashable.
For example:
struct Candy {
let name: String
}
let candies = [Candy(name: "CRAB"), Candy(name: "DUNGINESS"), Candy(name: "RAW")]
var filteredCandies = [Candy]()
func filterContentForSearchText(searchText: String) {
let searchCharacters = Set(searchText.lowercaseString.characters)
filteredCandies = candies.filter {Set($0.name.lowercaseString.characters).isSupersetOf(searchCharacters)}
tableView.reloadData()
}
filterContentForSearchText("RA")
print(filteredCandies) // [Candy(name: "CRAB"), Candy(name: "RAW")]
filterContentForSearchText("ED")
print(filteredCandies) // Candy(name: "DUNGINESS")]
Also depending on whether you can identify this as a performance bottleneck (you should do some profiling first) – you could potentially optimise the above further by caching the sets containing the characters of your 'candy' names, saving from having to recreate them at each search (although you'll have to ensure that they're updated if you update your candies data).
When you come to search, you can then use zip(_:_:) and flatMap(_:) in order to filter out the corresponding candies.
let candies = [Candy(name: "CRAB"), Candy(name: "DUNGINESS"), Candy(name: "RAW")]
// cached sets of (lowercased) candy name characters
let candyNameCharacterSets = candies.map {Set($0.name.lowercaseString.characters)}
var filteredCandies = [Candy]()
func filterContentForSearchText(searchText: String) {
let searchCharacters = Set(searchText.lowercaseString.characters)
filteredCandies = zip(candyNameCharacterSets, candies).flatMap {$0.isSupersetOf(searchCharacters) ? $1 : nil}
tableView.reloadData()
}
First of all a block of code like this
if someCondition == true {
return true
} else {
return false
}
can also be written this ways
return someCondition
right? :)
Refactoring
So your original code would look like this
func filterContentForSearchText(searchText: String) {
filteredCandies = candies.filter { $0.name.lowercaseString.containsString(searchText.lowercaseString) }
tableView.reloadData()
}
Scrambled search
Now, given a string A, your want to know if another string B contains all the character of A right?
For this we need CountedSet which is available from Swift 3. Since you are using Swift 2.2 we'll use the old NSCountedSet but some bridging to Objective-C is needed.
Here's the code.
struct Candy {
let name: String
}
let candies = [Candy]()
var filteredCandies = [Candy]()
func filterContentForSearchText(searchText: String) {
let keywordChars = NSCountedSet(array:Array(searchText.lowercaseString.characters).map { String($0) })
filteredCandies = candies.filter {
let candyChars = NSCountedSet(array:Array($0.name.lowercaseString.characters).map { String($0) }) as Set<NSObject>
return keywordChars.isSubsetOfSet(candyChars)
}
tableView.reloadData()
}
Swift 3 code update :
func filterContentForSearchText(searchText: String, scope: String = "All") {
filteredCandies = candies.filter { candy in
return candy.name.localizedLowercase.contains(searchText.lowercased())
}
tableView.reloadData()
}