Make UITextField with VoiceOver read numbers as digits - uitextfield

Our App has a few UITextField where users enter alphanumeric input codes such as "AA149873A".
We'd like voice over to read these as "A A 1 4 9 8 7 2 A", but it instead reads the digits as a number: "One hundred and forty nine thousand, eight hundred and seventy three".
Is there some way to configure UITextField so that it knows its content shouldn't be thought of as numbers, but individual digits?
Thanks.

We'd like voice over to read these as "A A 1 4 9 8 7 2 A", but it instead reads the digits as a number: "One hundred and forty nine thousand, eight hundred and seventy three".
The fastest wy to reach your goal is to add spaces between each character in the accessibilityValue of the UITextField as follows: 🤓
class AccessibilityTextField: UITextField {
var _str: String?
override var text: String? {
get { return _str}
set {
_str = newValue!
accessibilityValue = _str!.map{String($0)}.joined(separator: " ")
}
}
convenience init() { self.init() }
required init?(coder: NSCoder) { super.init(coder: coder) }
}
I didn't implement all the text field delegate stuff to test it ⟹ I created a blank project only with an UITextField adding a "BB0024FG" plain text and changed the text in the viewDidAppear of my view controller:
myTextField.text = "AA14987A"
In the end, VoiceOver spells out each character after another without reading out the initial plain text. 👍
Following this rationale, you have a way that let VoiceOver know that the UITextField content must be thought as individual digits. 😉

As of iOS 13 it is possible to add a string attribute to direct voice-over vocalisation to spell strings out as individual letters and digits.
I've not found a way to direct UITextField to add this attribute to its content. However, a UITextField subclass can override it's accessibilityValue to achieve this.
The subclass given here adds a property to enable or disable this behaviour.
final class AccessibilityTextField: UITextField {
var isAlphanumeric: Bool = false
override public var accessibilityAttributedValue: NSAttributedString? {
get {
guard let text = text, !text.isEmpty else {
return nil
}
return NSAttributedString(string: text, attributes: valueAttributes)
}
set {
// Ignore these values.
_ = newValue
}
}
private var valueAttributes: [NSAttributedString.Key: Any] {
guard #available(iOS 13.0, *), isAlphanumeric else {
return [:]
}
return [.accessibilitySpeechSpellOut: true]
}
}
An alternative approach is given in another answer here that doesn't use the iOS 13 feature . accessibilitySpeechSpellOut. However, I've seen it suggested that this is not ideal for brail output systems as they also use accessibilityLabel. Perhaps this is a good fallback on pre iOS 13 systems.

Related

Set Limit for TextField characters in RxSwift [duplicate]

This question already has an answer here:
RxSwift/RxCocoa: prevent UITextField from having more than ... characters
(1 answer)
Closed 4 years ago.
I want to set a limit for characters in a textFiled using RxSwift,
may be i can benefit from Scan operator but i don't know how to use it in this case ,
I want my resulting code to behave as same as implementing it in shouldChangeCharacterInRage method:
func textField(_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String)
-> Bool {
if textField == baseTextFiled{
let enteredString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
if enteredString.count > limit{ // limit defined previously
return false
}
}
return true
}
Any help?
ReactiveX pushes data, so isn't really compatible with that particular delegate method. That said, you can still do it because .rx.text is a control property...
baseTextField.rx.text.orEmpty // pushes text
.map { String($0.prefix(limit)) } // pushes first X characters in text
.distinctUntilChanged() // only pushes if text is different than previously
.bind(to: baseTextField.rx.text) // pushes text into text field
.disposed(by: bag)

NumberFormatter, NSLocale, Xcode, iOS, Swift 3.0

Problem: A textField with a "number" containing a decimal separator (a comma in my case), how can I change this to a decimal separator that Xcode will understand (.), and the other way around - display a result with the local decimal separator?
Ok...half way there...
let label = Input.text
let formatter = NumberFormatter()
let maybeNumber = formatter.number(from: label!)
if maybeNumber != nil {
Output.text = String(describing: maybeNumber!)
}
I'm not sure if this is what you mean, but you can change the comma in the textField.text to a (.) by using this piece of code:
textField.text = textField.text.replacingOccurrences(of: ",", with: ".", options: NSString.CompareOptions.literal, range: nil)
This finds the commas in your textField and replaces them with (.)

Looping zero-padded numbers in swift - Xcode

Under the SKSpriteNode class, you can do the following to animate an object
private func animate(){
var playerTextures:[SKTexture] = []
for i in 1...3 {
playerTextures.append(SKTexture(imageNamed: "image00\(i)"))
}
let playerAnimation = SKAction.repeatActionForever(
SKAction.animateWithTextures(playerTextures, timePerFrame: 0.1))
self.runAction(playerAnimation)
}
The above code can animate an object using the sequence of images. Here the image files would be image001.png image002.png image003.png
Here come's my question, how can I animate images if the file names are
image001.png
image002.png
...
image009.png
image010.png
image011.png
image012.png
The key point here is the problem with the zero paddings. Any ideas?
Assuming you will only have three digits number you could add your pictures that way:
for i in 1...3 {
let imageNumber = String(format: "%03d", i)
playerTextures.append(SKTexture(imageNamed: "image\(imageNumber)"))
}
This will give you image001, image002 and image003
This requires that you import Foundation at the beginning of your swift file

UITextField - Limit text lenght not by character count

is there a way to limit a textfield but not by character count? I would like to stop the ability to type when the end of the field is reached. But as different characters have different dimensions stopping at a certain count ist not really a solution. Something like "stop at the end of the line" would be perfect.
this doesn't work for me
Max length UITextField
What you want to do is instead of calculating the maximum number of characters of the new string, calculate it's width in points and compare it to the width you need.
[Swift]
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let combinedString = textField.attributedText!.mutableCopy() as NSMutableAttributedString
combinedString.replaceCharactersInRange(range, withString: string)
return combinedString.size().width > textField.bounds.size.width
}
You might need to edit the attributes to get it to render on a single line.
Subscribe to controlTextDidChange and calculate the size of the text via
let s = NSString(string: <yourtext>)
let size = s.boundingRectWithSize(<#size: NSSize#>, options: <#NSStringDrawingOptions#>, attributes: <#[NSObject : AnyObject]?#>)
Now you can check if the text is too large.

Can't setup bindings in Swift Storyboard?

I've got some code that runs a fairly complex algorithm. I want to put together a fairly simple UI that will allow me to monitor the values of the various variables in the algorithm in some graphical ways -- think of it like a dashboard of sorts.
So, for simplicity's sake, let's say I have an algorithm like what follows. It searches a vector of values for the two values that most closely sum to a target value:
import Foundation
class algorithm {
var numbers = [Double]()
let numberOfRandoms = 1000
dynamic var a: String
dynamic var b: String
init () {
// Load initial vector with some random numbers between 0 and 1
for _ in 1...numberOfRandoms {
numbers.append(Double(arc4random()) / Double(UINT32_MAX))
}
a = " "
b = " "
}
func findTheTwoNumbersThatAddUpTheClosestToTarget(target: Double) {
//Initializing this to a very large value
var currentBestSum = 1000.0
//Begin brute force search for the optimal solution
for i in 0...numbers.count-2 {
for j in i+1...numbers.count-1 {
//Check to see if the current candidate exceeds the best solution
if abs(numbers[i] + numbers[j] - target) < currentBestSum {
//If it does, store the new champion
a = String(i)
b = String(j)
//And reset the current top score to match
currentBestSum = abs(numbers[i] + numbers[j]-target)
}
}
}
}
}
Now, this is just a simple (and silly) example, but it suits these purposes. I basically want to create a simple UI that displays the important values in the process as it runs (dynamically).
In this example, let's say that I just want to display two labels that contain the index values of the two leaders as the algorithm executes.
I created the labels in the storyboard.
Then, I created IBOutlets in the ViewController (Actually, storyboards did it for me when I Ctrl-dragged):
class ViewController: NSViewController {
#IBOutlet weak var a: NSTextField!
#IBOutlet weak var b: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
}
Then, I ctrl-dragged the labels to the a and b in the algorithm class to create the bindings.
Finally, I create an class variable in the view controller and instantiate it in the viewDidLoad method. This doesn't seem like the right thing to do -- maybe it is. Seems like you would want to keep separate your interface and data...
The labels do, in fact, show up -- but they never show any values of a and b. They just show the default text.
Not sure what I'm doing wrong.
Help!?
P.S., in response to Anthony Kong, I do recognize that I could manually synchronize all the view elements in the code, but I thought the whole point of using bindings was to avoid having to do this manual synchronization. I just can't figure out how to set it up.
Without commenting on your specific code I think I have experienced (and solved) the problem you describe. I was able to write an app that had two targets, one NIB-based and one Storyboard-based. As much as I was able I duplicated the code in each and shared the common data instance that I was trying to display in a TableView. The NIB-based app worked using the stock Cocoa Bindings that I set in IB. But the Storyboard-based app did not, the array controller did not see the data.
My solution was simply to add the binding for contentArray programmatically in viewDidLoad. The one line that fixed it for me is:
ac.bind("contentArray", toObject: cd, withKeyPath: "people", options: nil)
ac is the IBOutlet for the ArrayController in the Storyboard. cd is the class instance that contains the people array.
This is using XCode 6.2 (6C107a) which is Beta 3 I think.
This was the only binding that I had to set myself, the TableView to ArrayController (arrangedObjects) and TableViewCell to TableView (objectValue) didn't need any tweaking.
There are several problems with your code
1) In your code,
func applicationDidFinishLaunching(aNotification: NSNotification?) {
// Insert code here to initialize your application
var test = algorithm()
test.findTheTwoNumbersThatAddUpTheClosestToTarget(0.5)
}
The variable test goes out of scope when the function exits. Based on the wording of your question, you are expecting it to be a long running process. So this will not do what you want.
You should
a) Add test as class variable to the ViewController class
b) instantiate the variable in viewDidLoad method.
2) In your algorithm it does not actually provide any feedback to the labels. Maybe you think because the class has the ivar a and b so they are hooked to the IBOutlet by the same names. But of course it is not the case. And you do not need the keyword dynamic too.
What you should do is:
a) provide a method in the View Controller class to update the labels. It will serve as a callback function to be used by algorithm class to feedback the calculation result.
It may look like this:
func update_value_callback(vala: String, valb: String) {
a.text = vala; // updating the label here
b.text = valb;
}
b) make the algorithm class calls the callback function e.g.
func findTheTwoNumbersThatAddUpTheClosestToTarget(target: Double, viewController: ViewController) {
// do your stuff
...
// execute the callback
viewController.update_value_callback(a, b)
}

Resources