How can I change the Voiceover value of a UIButton programmatically? - ios

I've created a UISwitch in InterfaceBuilder to toggle audio on and off. The switch works fine, but I want to change the value that Voiceover reads out from the current '0' and '1' to 'Off' and 'On'. My code is as follows:
import UIKit
class AudioStreamTableViewCell: UITableViewCell {
weak var controller: EventDetailsViewController!
weak var audioInterface: EventAudioInterface? { didSet { if self.audioInterface !== oldValue { self.updateUI() }}}
func updateUI() {}
override func awakeFromNib() {
super.awakeFromNib()
self.updateUI()
}
}
class MuteStreamTableViewCell: AudioStreamTableViewCell {
static let identifier = "MuteStreamTableViewCell"
#IBOutlet var muteSwitch: UISwitch!
override func updateUI() {
self.muteSwitch.isOn = self.audioInterface?.muted ?? false
}
#IBAction func switchChanged(_ muteSwitch: UISwitch) {
self.controller.setMuted(muteSwitch.isOn, on: self.audioInterface)
if muteSwitch.isOn {
self.muteSwitch.accessibilityValue = "on"
} else {
self.muteSwitch.accessibilityValue = "off"
}
}
}
Voiceover still speaks '0' and '1'. What am I doing wrong?
Thanks!!

Swift 4.2
I had to subclass UISwitch and override accessibilityValue.
class AccessibilityUiSwitch: UISwitch {
override var accessibilityValue: String? {
get {
return isOn ? "on" : "off"
}
set {
self.accessibilityValue = newValue
}
}
}

Related

How to check if UITextField became empty again?

I'm creating a simple log in VC. let's ignore the input validation for both username & password. I just want to enable the UIButton when both username's and password's UITextField is not empty. And whenever any one of them becomes empty, I want the button to be disabled.
#IBAction func typingUserName(_ sender: Any) {
usernameTxtfield.layer.shadowPath = UIBezierPath(rect: usernameTxtfield.bounds).cgPath
usernameTxtfield.layer.shadowRadius = 2
usernameTxtfield.layer.shadowOffset = .zero
usernameTxtfield.layer.shadowOpacity = 0.5
signInIcon.isEnabled = false
}
#IBAction func typingPassword(_ sender: Any) {
passwordTxtfield.layer.shadowPath = UIBezierPath(rect: passwordTxtfield.bounds).cgPath
passwordTxtfield.layer.shadowRadius = 2
passwordTxtfield.layer.shadowOffset = .zero
passwordTxtfield.layer.shadowOpacity = 0.5
signInIcon.isEnabled = false
}
#IBAction func usernameTxtFieldEditingChnged(_ sender: Any) {
usernameTxtfield.layer.shadowRadius = 0
usernameTxtfield.layer.shadowOffset = .zero
usernameTxtfield.layer.shadowOpacity = 0
}
#IBAction func passwordEditingChaned(_ sender: Any) {
passwordTxtfield.layer.shadowRadius = 0
passwordTxtfield.layer.shadowOffset = .zero
passwordTxtfield.layer.shadowOpacity = 0
signInIcon.isEnabled = true
}
#IBAction func signInClicked(_ sender: Any) {
performSegue(withIdentifier: "welcomeVC", sender: signInIcon)
}
As you can see, I'm enabling the button only after the password textfield EditingChanged has been triggered.
You can observe event .editingChanged.
passwordTxtfield.addTarget(self, action: #selector(passwordEditingChaned), for: .editingChanged)
usernameTxtfield.addTarget(self, action: #selector(usernameTxtFieldEditingChnged), for: .editingChanged)
And then add check in both methods:
signInIcon.isEnabled = passwordTxtfield.text?.isEmpty == false && usernameTxtfield.text?.isEmpty == false
You have to implement something like this:
if usernameTxtfield.text != "" && passwordTxtfield.text != "" {
signInIcon.isEnabled = true
} else {
signInIcon.isEnabled = false
}
You add this piece of code in the action of each UITextField and your are good to go
Connect outlets of textFields and Button to ViewController Class.
#IBOutlet private weak var usernameTxtfield: UITextField!
#IBOutlet private weak var passwordTxtfield: UITextField!
#IBOutlet private weak var signInButton: UIButton!
then write your setup function
private func setupTextFields() {
// write your TextFields custom setup ...
// also add delegate lines
usernameTxtfield.delegate = self
passwordTxtfield.delegate = self
}
your setupTextFields function to viewDidLoad of VC and also set your button isEnable = false
override func viewDidLoad() {
super.viewDidLoad()
setupTextFileds()
setSignInButton(isEnable: false)
}
and also write func to get textFields.
private func getTextFields() -> [UITextField] {
return [usernameTxtfield, passwordTxtfield]
}
also we need to check if these are valid.
private func isInputsValid() -> Bool {
var isValid: Bool = true
let inputs: [UITextField] = getTextFields()
if let input = inputs.first(where: { $0.text?.count == 0 }) {
debugPrint("\(input) is not valid.")
isValid = false
}
return isValid
}
also add func to set Button
private func setSignInButton(isEnable: Bool) {
signInButton.isEnabled = isEnable
}
then write TextField delegate func to understand inputs are changing and change condition of button.
extension ViewController: UITextFieldDelegate {
func textFieldDidChangeSelection(_ textField: UITextField) {
setSignInButton(isEnable: isInputsValid())
}
}
I hope it was helpful :)

Is it possible to make the borderColor of the textField inside the tableView change as the text color of the textField changes?

I want to change borderColor from lightGray to black when textField is firstResponder. And if the textField resigns again, I want to change it to gray. TextField's textColor changes color automatically without tableView's reload, but borderColor doesn't. Is there a way to change the color by observing the values ​​without reloading the tableView?
class ChangePasswordViewController: UITableViewController {
#IBOutlet weak var newPasswordTextField: RoundTextField!
#IBOutlet weak var checkingNewPasswordTextField: RoundTextField!
#IBOutlet weak var explainLabel: UILabel!
#IBOutlet weak var changePwButton: FillRoundButton!
let viewModel = ChangePasswordViewModel()
override func viewDidLoad() {
super.viewDidLoad()
hideKeyboardWhenTappedAround()
newPasswordTextField.tag = 0
checkingNewPasswordTextField.tag = 1
configureNotificationObservers()
}
func configureNotificationObservers() {
newPasswordTextField.addTarget(self, action: #selector(textDidChange(sender:)), for: .editingChanged)
checkingNewPasswordTextField.addTarget(self, action: #selector(textDidChange(sender:)), for: .editingChanged)
}
// MARK: - Actions
#objc func textDidChange(sender: RoundTextField) {
if sender == newPasswordTextField {
viewModel.pwValue = sender.text
} else {
viewModel.confirmPwValue = sender.text
}
updateForm()
}
}
extension ChangePasswordViewController: UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
if textField.tag == 0 {
viewModel.pwValue = textField.text ?? ""
} else {
viewModel.confirmPwValue = textField.text ?? ""
}
}
}
extension ChangePasswordViewController: FormViewModel {
func updateForm() {
changePwButton.isEnabled = viewModel.pwFormIsValid
newPasswordTextField.layer.borderColor = UIColor.yellow.cgColor
newPasswordTextField.textColor = .yellow
}
}
enter image description here

Swift Protocol of Custom View Always nil

I have a custom view CustomSegmentedControl in my app which have its protocol with changed index function as below,
protocol SegmentControllerDelegate:class {
func indexChanged(index : Int)
}
class CustomSegmentedControl: UIView {
weak var delegate : SegmentControllerDelegate?
#objc func buttonAction(sender:UIButton) {
for (buttonIndex, btn) in buttons.enumerated() {
btn.setTitleColor(textColor, for: .normal)
if btn == sender {
let selectorPosition = frame.width/CGFloat(buttonTitles.count) * CGFloat(buttonIndex)
selectedIndex = buttonIndex
//delegate?.changeToIndex(index: buttonIndex)
if(delegate != nil){
delegate?.indexChanged(index: buttonIndex)
}else{
print(buttonIndex)
}
UIView.animate(withDuration: 0.3) {
self.selectorView.frame.origin.x = selectorPosition
}
btn.setTitleColor(selectorTextColor, for: .normal)
}
}
}
I added a view to my ViewController Mail and set the class to my custom view and make outlet for it and use the protocol as well like below,
import UIKit
class Mail: UIViewController, SegmentControllerDelegate {
func indexChanged(index: Int) {
print(index)
switch index {
case 0:
container.bringSubviewToFront(inbox)
break
case 1:
container.bringSubviewToFront(outbox)
break
default:
break
}
}
#IBOutlet weak var segment: CustomSegmentedControl!{
didSet{
segment.setButtonTitles(buttonTitles: ["First","Second"])
segment.selectorTextColor = .orange
segment.selectorViewColor = .orange
}
}
#IBOutlet weak var container: UIView!
var inbox : UIView!
var outbox : UIView!
override func viewDidLoad() {
super.viewDidLoad()
segment.delegate = self
inbox = Inbox().view
outbox = GPS().view
container.addSubview(inbox)
container.addSubview(outbox)
}
But the protocol is always nil and the function never be called !?, What Im missing here ?
Any help will be much appreciated

Disable / Enable a button in Xcode

Hi i'm new with Swift programming.
What im trying to do is Disable my button (signIn) in viewDidLoad and only enable when the textfields have text in them. Here's what i've achieved so far. (not much though!)
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
signIn.isEnabled = false
// Do any additional setup after loading the view, typically from a nib.
}
#IBOutlet weak var emailtxt: UITextField!
#IBOutlet weak var passwordtxt: UITextField!
#IBOutlet weak var signIn: UIButton!
I need help to create a function in signIn that keeps button disabled until text fields (emailtxt & passwordtxt) have text in them and then proceed.
Glad if anyone can sort me.
Thanks in advance!
First add these for all of your textFields in viewDidLoad():
emailtxt.addTarget(self, action: #selector(textFieldDidChange(_:)),
for: UIControlEvents.editingChanged)
passwordtxt.addTarget(self, action: #selector(textFieldDidChange(_:)),
for: UIControlEvents.editingChanged)
Then use this:
#objc func textFieldDidChange(_ textField: UITextField) {
self.buttonIsEnabled()
}
func buttonIsEnabled() {
var buttonIsEnabled = true
defer {
self.signIn.isEnabled = buttonIsEnabled
}
guard let emailtxt = self.emailtxt.text, !emailtxt.isEmpty else {
addButtonIsEnabled = false
return
}
guard let passwordtxt = self. passwordtxt.text, ! passwordtxt.isEmpty else {
addButtonIsEnabled = false
return
}
}
I use this way in my codes and it works well.
Even you can add more methods for additional checking to buttonIsEnabled, like:
self.checkEmailIsValid(for: emailtxt)
Of course you should handle this method before:
func checkEmailIsValid(for: String) {
//...
}
Set ViewController as delegate for emailtxt and passwordtxt like this,
override func viewDidLoad() {
super.viewDidLoad()
signIn.isEnabled = false
emailtxt.delegate = self
passwordtxt.delegate = self
// Do any additional setup after loading the view, typically from a nib.
}
Conform your ViewController to UITextFieldDelegate and enable/disable as the text input is finished,
extension ViewController: UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
if emailtxt.text?.isEmpty == false && passwordtxt.text?.isEmpty == false {
signIn.isEnabled = true
} else {
signIn.isEnabled = false
}
}
}
Here is the fix for your code you shared.
import UIKit
extension UIViewController {
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
}
extension SignInVC: UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
if emailtxt.text?.isEmpty == false && passwordtxt.text?.isEmpty == false {
signIn.isEnabled = true
} else {
signIn.isEnabled = false
}
}
}
class SignInVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
signIn.isEnabled = false
emailtxt.delegate = self
passwordtxt.delegate = self
self.hideKeyboardWhenTappedAround()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBOutlet weak var emailtxt: UITextField!
#IBOutlet weak var passwordtxt: UITextField!
#IBOutlet weak var signIn: UIButton!
}
What I would do is create an IBAction from one of your text fields, and set the event to Editing Changed:
The code should look like this:
#IBAction func textFieldEditingDidChange(_ sender: UITextField) {
}
You can then connect that same outlet to both of your text fields by dragging from the outlet to your remaining field. If you've connected both correctly, clicking on the circle to the left of your IBAction should show two text fields:
The action will now be fired every time text changes in either of your fields.
Then, at the top of the file, I'd create a computed property that returns false unless there is something in both fields:
var shouldEnableButton: Bool {
guard let text1 = textField1.text, let text2 = textField2.text else {
return false
}
return text1.isEmpty && text2.isEmpty ? false : true
}
Finally, we add shouldEnableButton to our IBAction:
#IBAction func textFieldEditingDidChange(_ sender: UITextField) {
button.isEnabled = shouldEnableButton
}
Important
When you connect your second text field to the outlet, it will incorrectly assign Editing Did End as its event:
Delete this event and click and drag from Editing Changed to your IBAction:
Use SwiftValidator
https://github.com/SwiftValidatorCommunity/SwiftValidator
by this, you will set validation of email & password like below
import SwiftValidator
let validator = Validator()
validator.registerField(emailTextField, errorLabel: emailErrorLabel, rules: [RequiredRule(), EmailRule(message: "Invalid email")])
// MARK: - ValidationDelegate
extension ViewController: ValidationDelegate {
func validationSuccessful() {
self.loginUser()
}
func validationFailed(_ errors:[(Validatable ,ValidationError)]) {
for (field, error) in errors {
//Handle as per need - show extra label - shake view etc
/*
if let field = field as? UITextField {
Utilities.shakeTheView(shakeView: field)
}
error.errorLabel?.text = error.errorMessage
error.errorLabel?.isHidden = false
*/
}
}
}

UITextField without get property swift 4

I have custom control which contains textField. Sample code looks like this:
#IBDesignable
class MyTextField: UIView {
private var _text: String?
private var textField: UITextField?
#IBInspectable var text: String? {
get {
return _text
} set (newValue) {
guard let txt = newValue else { return }
self._text = txt
//updateView()
}
}
override func awakeFromNib() {
super.awakeFromNib()
updateView()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
updateView()
}
func updateView() {
textField = UITextField(frame: CGRect(x: 0, y: 0, width: 97, height: 30))
self.addSubview(textField!)
textField!.text = self._text
}}
When I create an outlet to my custom control in code I can set text property, but I can't get it. What am I doing wrong ? I'm new in this so I don't understand...
EDIT:
Add MyTextField to Main.storyboard and standard textField
Create an outlets ​#IBOutlet var myTextField: MyTextField! and
standardTextField
Set Text property of my control in Object Inspector
My control on storyboard updates correctly and "test text" is visible
Add a button and create an action outlet
case 1: works fine
#IBAction func click(_ sender: UIButton) {
myTextField.text = standardTextField.text
}
case 2: doesn't work
#IBAction func click(_ sender: UIButton) {
standardTextField.text = myTextField.text
}
After case 2, in standardTextField is only value which i set from object inspector. If i change value of myTextField.text, standardTextField still shows a orginal value from object inspector
I replicated your setup in Xcode. Case 2 does work — the standardTextField displays MyTextField's text when the button is clicked.
Note: in MyTextField I assumed that you want to create textField once, instead of recreating it each time the text updates. Case 2 was working without this change, though.
import UIKit
class ViewController: UIViewController {
#IBOutlet var myTextField: MyTextField!
#IBOutlet var standardTextField: UITextField!
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
button.addTarget(self, action: #selector(click(_:)), for: .touchUpInside)
}
#IBAction func click(_ sender: UIButton) {
// myTextField.text = standardTextField.text // Case 1 works
standardTextField.text = myTextField.text // Case 2 works
}
}
MyTextField control:
import UIKit
#IBDesignable
class MyTextField: UIView {
private var _text: String?
private var textField = UITextField(frame: CGRect(x: 0, y: 0, width: 97, height: 30))
#IBInspectable var text: String? {
get {
return _text
} set (newValue) {
guard let txt = newValue else { return }
self._text = txt
updateView()
}
}
override func awakeFromNib() {
super.awakeFromNib()
self.addSubview(textField)
updateView()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
self.addSubview(textField)
updateView()
}
func updateView() {
textField.text = self._text
}
}
A simple solution for not working getter is change text property
from:
#IBInspectable var text: String? {
get {
return _text
} set (newValue) {
guard let txt = newValue else { return }
self._text = txt
updateView()
}
}
to
#IBInspectable var text: String? {
get {
return textField!.text
} set (newValue) {
guard let txt = newValue else { return }
self._text = txt
updateView()
}
}
But I don't know if this is correct...
Does it make any sense ?

Resources