iOS - Voice Over - Accessibility for large amounts - ios

I am not able to make iOS voice over / Accessiblity read large amounts in money format for example £782284.00 , this should read as seven hundered eighty two thousand , two hundered and eight four, but iOS voice over reads this as seven eight two two eight four.

The best way to get your purpose is to format perfectly your numbers to be vocalized as desired by VoiceOver.
Use of NumberFormatter and .spellOut style to read out the accessibilityLabel are important tools to adapt the VoiceOver vocalization for large amounts.
I deeply encourage you to try and vocalize numbers as they should : the application content MUST be adapted to the VoiceOver users, not the opposite.

It is really important to make sure you do all you can to make the app easier to use for VoiceOver users. I have been running an app for sighted and visually impaired players, you can see an example of this method running in the inventory section of the app:
https://apps.apple.com/us/app/swordy-quest-an-rpg-adventure/id1446641513
The number of requests I got from blind & visually impaired players to read out millions as millions, rather than individual digits, was huge. Please do take the extra effort to make it fully VoiceOver compatible. It makes life so much easier for VoiceOver users. Here is a method I created solely for this purpose, that VoiceOver seems to like. You are basically adding thousand comma separators:
// e.g. 1000 -> "1,000"
public static func addCommaSeperatorsToInt(number: Int) -> String {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = NumberFormatter.Style.decimal
return numberFormatter.string(from: NSNumber(value: number))!
}

I agree to #aadrian is suggesting, try not to break convention VoiceOver users are used to. Because some large numbers are read in a long time, then the users have slow navigation across numbers.
However, if that is the case you need it hard, here you can have (I couldnot find sth for swift/objc but you will get the idea) a number to word converter and then you can set that to _.accessbilityLabel of the UIView or whatever. Then it will read as you like.
Also see this

Related

Optimize NSRegularExpression performance

My app has a feed with posts that can contain URL's, #hashtags and #mentions. I display them using a pod called ActiveLabel. This pod does a good job, but my feed slightly lags when scrolling. My feed is a UICollectionView, and the cell generation is slightly lagging. I profiled my app when scrolling, and analysed the lag spikes. The lag is almost unnoticeable, but it annoys me.
As you can see, the main offender is the NSRegularExpression search.
I tried to optimize this slightly by disabling data detection when there is no instances of the data type, using .contains(). This made it marginally faster, but the lag spikes remains.
let enabledTypes:[ActiveType] = {
var types = [ActiveType]()
if ad.caption.current.string.contains("#") { types.append(.hashtag) }
if ad.caption.current.string.contains("#") { types.append(.mention) }
if ad.caption.current.string.contains("://") { types.append(.url) }
if ad.caption.canExpand { types.append(seeMore) }
return types
}()
label.enabledTypes = enabledTypes
I also followed every step in this article, which slighty helped, but not enough. So I need to fix the regex.
The regex statements ActiveLabel uses is
static let hashtagPattern = "(?:^|\\s|$)#[\\p{L}0-9_]*"
static let mentionPattern = "(?:^|\\s|$|[.])#[\\p{L}0-9_]*"
static let urlPattern = "(^|[\\s.:;?\\-\\]<\\(])" + "((https?://|www\\.|pic\\.)[-\\w;/?:#&=+$\\|\\_.!~*\\|'()\\[\\]%#,☺]+[\\w/#](\\(\\))?)" + "(?=$|[\\s',\\|\\(\\).:;?\\-\\[\\]>\\)])"
and it uses them with
static func getElements(from text: String, with pattern: String, range: NSRange) -> [NSTextCheckingResult]{
guard let elementRegex = try? NSRegularExpression(pattern: pattern, options: [.caseInsensitive]) else { return [] }
return elementRegex.matches(in: text, options: [], range: range)
}
Is searched around for other regexes to detect hashtags and mentions, but I didn't find anything that made a difference.
I tried to layout the label on a background thread, but that obviously crashed because UI doesn't like being done on a background thread. I could rewrite ActiveLabel to work mainly on background threads where I can and use callbacks instead of return types, but I'd like to avoid that.
Some samples of strings that I detect data on:
"Arnie says, Aspen. Str. Small. Varm og god jakke. Veldig fin på! Fremstår ubrukt. Kun brukt et par ganger, rett og slett fordi jeg har alt for mange jakker🙈 #urban #arnie #says #aspen #ubrukt"
"Skjorte pent brukt i organisk bomull fra tom tailor originalpris 300kr #organisk #bomullsjorte #bomull #flower #floral"
"Jean Paul genser i 100% ull, pent brukt✨ er i str.m, men veldig liten, passer xs-s! \n #jeanpaul #genser #classy #litebrukt #brun #ull"
As you can see, our users mainly hashtag stuff, so that one is the most important one.
Is there any way I can improve either NSRegularExpression or the regex statements to avoid the performance hit?
As #raidfive suggests, most likely your best course of action here is to create one or more NSRegularExpression instances ahead of time and reuse them whenever needed.
Note that since it's the creation/compiling of regexes that makes the biggest difference in your time profile (at least, in as much of the time profile as you've shared), caching regexes may win you back enough performance that you no longer need your intermediate optimization of enabling only the detection elements you need. In that case, you need only one regex (representing detection of all possible element types), so caching/reuse is easy.
Note furthermore that your intermediate "optimization" may not actually improve performance to begin with — it might even harm performance. Matching a regex, however complicated, requires searching the entire string in its entirety (roughly) once. Trying to decide which element types to detect means searching the string multiple times — once for each contains("#") (etc) test, then once more to evaluate the string against the regex. Repeated string searches might well cost more than the compilation of a single regex.
If you find after implementing the single cached universal regex that you're still (somehow) hamstrung on regex performance, you can cache multiple regexes, one for each search scenario you're processing. The combinatorics presumably work out such that you still have far fewer different regexes than you have strings to process, so if you compile them all before the user even starts scrolling, you're not paying the time cost of compiling them during scrolling. Per the previous paragraph, though, this only makes sense if you have a cheap (i.e. not string search) way of detecting which regex you need for each string.
You could try creating and storing the NSRegularExpression instances in a variable (class or instance) so you're only creating them once.

Larger than Unsigned Long Long

I'm working on an iOS Objective C app where you accumulate a large amount of wealth. By the end of the app, the amount of money users can accumulate is more than a long long can handle. What data type should I use instead? I know I could use an unsigned long, but that only adds a little bit more. I need users to have like 6 more digits to be safe, so instead of the max being 18,446,744,073,709,551,615 (about 1.8x10^19), it would be ideal to have something like 1.8x10^25 as my maximum value.
Precision isn't actually all that important in the end, but it would defiantly save me time to not have to do more than just changing data types throughout my application. Any ideas?
Short Answer
Go for a 3rd party library.
Long Answer
When dealing with large numbers, probably one of the most fundamental design decisions is how am I going to represent the large number?
Will it be a string, an array, a list, or custom (homegrown) storage class.
After that decision is made, the actual math operations can be broken down in smaller parts and then executed with native language types such as int or integer.
Even with strings there is a limit in the number of characters or "numbers" in the number, as indicated here:
What is the maximum possible length of a .NET string?
You might also want to check: Arbitrary description Arithmetic

How to print a random String in Swift in TextView?

I've stored several sentences in an array:
let sentences = ["This is example 2","This is the second example", "The last example"]
One of these string should be shown in my TextView by random.
Can you help me how I can do this with Swift?
I am answering this question keeping in mind a general approach. I am not keeping in mind the Programming language you are working on. Keeping in mind the time factor. Get the timestamp from system in milliseconds and take mod of 3 this way you will always get a random number in [0,1,2]
index = timestamp_in_milliseconds % 3
sentences[index]

limit UITextField to specific number

I have a UITextField and I want to limit the user to only input decimal numbers that are between 0.0 and 24.00.
The main idea is that user is entering Hours in that field. So It can't go over 24 hours in a day.
is it possible to automatically enter a decimal after 2 digits? So when a user enter "18" for example, a decimal "." automatically shows up.
Right now, I am limiting the user to only enter full hours. So they can only enter 2 digits. I really need to change that. This is what I have now.
txtFld_Hours.ShouldChangeCharacters = (textField, range, replacementString) => {
var newLength = textField.Text.Length + replacementString.Length - range.Length;
return newLength <= 2;
};
Thank you for your time.
Everything is possible of course, but like other people mentioned, it would be much better to use UIDatePicker control for a time picker (it even handles localisation for you on iOS, as some countries use 12-hour time with AM/PM marker for example). Even MonoTouch documentation says there are differences between platforms and you should structure your code in a way that only the difference in UI code is written for each platform, but all other code is common to all platforms.
But if you absolutely have to do this on your own, this requires a bit more complex solution, so we cannot write this for you directly. Search over GitHub for some code samples, I am sure that you can easily convert Objective-C into MonoTouch, since it is supposed to be a 1:1 mapping of the API.
Here are a few things on where to start:
See IUITextFieldDelegate interface documentation (which you already know about)
Update the ShouldBeginEditing method with code that will handle the dot.
When 2 characters are reached, add the dot programatically and move the cursor behind the dot using SelectedTextRange property.
The maximum length of the text, should now be 5.
Handle the automatic removal of the dot when text length is 3 and user taps backspace.
Following this and you should have your own UITextField with your custom hour picker.

Reintroducing spaces into a document

Imagine we have some reference text on hand
Four score and seven years ago our fathers brought forth on this
continent a new nation, conceived in liberty, and dedicated to the
proposition that all men are created equal. Now we are engaged in a
great civil war, testing whether that nation, or any nation, so
conceived and so dedicated, can long endure. We are met on a great
battle-field of that war. We have come to dedicate a portion of that
field, as a final resting place for those who here gave their lives
that that nation might live. It is altogether fitting and proper that
we should do this. But, in a larger sense, we can not dedicate, we can
not consecrate, we can not hallow this ground. The brave men, living
and dead, who struggled here, have consecrated it, far above our poor
power to add or detract. The world will little note, nor long remember
what we say here, but it can never forget what they did here. It is
for us the living, rather, to be dedicated here to the unfinished work
which they who fought here have thus far so nobly advanced. It is
rather for us to be here dedicated to the great task remaining before
us—that from these honored dead we take increased devotion to that
cause for which they gave the last full measure of devotion—that we
here highly resolve that these dead shall not have died in vain—that
this nation, under God, shall have a new birth of freedom—and that
government of the people, by the people, for the people, shall not
perish from the earth.
and we receive snippets of that text back to us with no spaces or punctuation, and some characters deleted, inserted, and substituted
ieldasafinalrTstingplaceforwhofoughtheregavetheirliZesthatthatn
Using the reference text what are some tools (in any programming language) we can use to try properly space the words
ield as a final rTsting place for who fought here gave their liZes that that n
correcting errors is not necessary, just spacing
Weird problem you've got there :)
If you can't rely on capitalization for hints, just lowercase everything to start with.
Then get a dictionary of words. Maybe just a wordlist, or you could try Wordnet.
And a corpus of similar, correctly spaced, material. If suitable, download the Wikipedia dump. You'll need to clean it up and break into ngrams. 3 grams will probably suit the task. Or save yourself the time and use Google's ngram data. Either the web ngrams (paid) or the book ngrams (free-ish).
Set a max word length cap. Let's say 20chars.
Take the first char of your mystery string and look it up in the dictionary. Then take the first 2 chars and look them up. Keep doing this until you get to 20. Store all matches you get, but the longest one is probably the best. Move the starting point 1 char at a time, through your string.
You'll end up with an array of valid word matches.
Loop through this new array and pair each value up with the following value, comparing it to the original string, so that you identify all possible valid word combinations that don't overlap. You might end up with 1 output string, or several.
If you've got several, break each output string into 3-grams. Then lookup in your new ngram database to see which combinations are most frequent.
There might also be some time-saving techniques like starting with stop words, checking them in a dictionary combined with incremental letter either side, and adding spaces there first.
... or I'm over-thinging the whole issue and there's an awk one liner that someone will humble me with :)
You can do this using edit distance and finding the minimum edit distance substring of the reference. Check out my answer (PHP implementation) to a similar question here:
Longest Common Substring with wrong character tolerance
Using the shortest_edit_substring() function from the above link, you can add this to do the search after stripping out everything but letters (or whatever you want to keep in: letters, numbers, etc.) and then correctly map the result back to the original version.
// map a stripped down substring back to the original version
function map_substring($haystack_letters,$start,$length,$haystack, $regexp)
{
$r_haystack = str_split($haystack);
$r_haystack_letters = $r_haystack;
foreach($r_haystack as $k => $l)
{
if (preg_match($regexp,$l))
{
unset($r_haystack_letters[$k]);
}
}
$key_map = array_keys($r_haystack_letters);
$real_start = $key_map[$start];
$real_end = $key_map[$start+$length-1];
$real_length = $real_end - $real_start + 1;
return array($real_start,$real_length);
}
$haystack = 'Four score and seven years ago our fathers brought forth on this continent a new nation, conceived in liberty, and dedicated to the proposition that all men are created equal. Now we are engaged in a great civil war, testing whether that nation, or any nation, so conceived and so dedicated, can long endure. We are met on a great battle-field of that war. We have come to dedicate a portion of that field, as a final resting place for those who here gave their lives that that nation might live. It is altogether fitting and proper that we should do this. But, in a larger sense, we can not dedicate, we can not consecrate, we can not hallow this ground. The brave men, living and dead, who struggled here, have consecrated it, far above our poor power to add or detract. The world will little note, nor long remember what we say here, but it can never forget what they did here. It is for us the living, rather, to be dedicated here to the unfinished work which they who fought here have thus far so nobly advanced. It is rather for us to be here dedicated to the great task remaining before us—that from these honored dead we take increased devotion to that cause for which they gave the last full measure of devotion—that we here highly resolve that these dead shall not have died in vain—that this nation, under God, shall have a new birth of freedom—and that government of the people, by the people, for the people, shall not perish from the earth.';
$needle = 'ieldasafinalrTstingplaceforwhofoughtheregavetheirliZesthatthatn';
// strip out all non-letters
$regexp_to_strip_out = '/[^A-Za-z]/';
$haystack_letters = preg_replace($regexp_to_strip_out,'',$haystack);
list($start,$length) = shortest_edit_substring($needle,$haystack_letters);
list($real_start,$real_length) = map_substring($haystack_letters,$start,$length,$haystack,$regexp_to_strip_out);
printf("Found |%s| in |%s|, matching |%s|\n",substr($haystack,$real_start,$real_length),$haystack,$needle);
This will do the error correction as well; it's actually easier to do it than to not do it. The minimum edit distance search is pretty straightforward to implement in other languages if you want something faster than PHP.

Resources