UITextField without get property swift 4 - ios

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 ?

Related

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

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

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
}
}
}

How do I add a custom action to the text selection edit menu in iOS?

I need to add a custom action to the edit menu that pops up when a user selects some text in a UITextView in iOS.
How do I do this?
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
addCustomMenu()
}
func addCustomMenu() {
let printToConsole = UIMenuItem(title: "Print To Console", action: #selector(printToConsole))
UIMenuController.shared().menuItems = [printToConsole]
}
func printToConsole() {
if let range = textView.selectedTextRange, let selectedText = textView.text(in: range) {
print(selectedText)
}
}
}
This is an example of text selection menu item that changes the text in a UITextView to red. changeToRedFunc can perform any action you want.
Note: This is in Swift 3
(ask if you want it in Swift 2.3)
Hope this helps! If you have any questions feel free to ask! :D
SWIFT 5
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
addCustomMenu()
}
func addCustomMenu() {
//Xcode doesn't like printToConsole being a var and a function call
let printToConsole = UIMenuItem(title: "Print To Console", action: #selector(printToConsole2))
UIMenuController.shared.menuItems = [printToConsole]
}
#objc func printToConsole2() {
if let range = textView.selectedTextRange, let selectedText = textView.text(in: range) {
print(selectedText)
}
}
}
Here is how you create a custom edit menu in swift 5:
import UIKit
class ViewController: UIViewController {
#IBOutlet var textfield: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let changeBackground = UIMenuItem(title: "Change Background Colour", action: #selector(changeBackgroundColour))
UIMenuController.shared.menuItems = [changeBackground] //will add it to everything that has
}
#objc func changeBackgroundColour()
{
self.view.backgroundColor = .cyan //just makes the background colour cyan
}
}
I have also made a youtube video explaining this here

Switching between Text fields on pressing return key in Swift

I'm designing an iOS app and I want that when the return key is pressed in my iPhone it directs me to the next following text field.
I have found a couple of similar questions, with excellent answers around but they all just happen to be in Objective-C and I'm looking for Swift code, now this is what I have up until now:
func textFieldShouldReturn(emaillabel: UITextField) -> Bool{
return true
}
It's placed in the file that's connected and controller to the UIView that contains the text fields, but I'm not sure if thats the right place.
Okay, so I tried this out and got this error:
//could not find an overload for '!=' that accepts the supplied arguments
func textFieldShouldReturn(textField: UITextField) -> Bool {
let nextTag: NSInteger = textField.tag + 1
// Try to find next responder
let nextResponder: UIResponder = textField.superview!.viewWithTag(nextTag)!
if (nextResponder != nil) {
// could not find an overload for '!=' that accepts the supplied arguments
// Found next responder, so set it.
nextResponder.becomeFirstResponder()
} else {
// Not found, so remove keyboard.
textField.resignFirstResponder()
}
return false // We do not want UITextField to insert line-breaks.
}
Make sure your UITextField delegates are set and the tags are incremented properly. This can also be done through the Interface Builder.
Here's a link to an Obj-C post I found: How to navigate through textfields (Next / Done Buttons)
class ViewController: UIViewController,UITextFieldDelegate {
// Link each UITextField (Not necessary if delegate and tag are set in Interface Builder)
#IBOutlet weak var someTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do the next two lines for each UITextField here or in the Interface Builder
someTextField.delegate = self
someTextField.tag = 0 //Increment accordingly
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
// Try to find next responder
if let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField {
nextField.becomeFirstResponder()
} else {
// Not found, so remove keyboard.
textField.resignFirstResponder()
}
// Do not add a line break
return false
}
}
Swift 5
You can easily switch to another TextField when clicking return key in keyboard.
First, Your view controller conforms to UITextFieldDelegate and add the textFieldShouldReturn(_:) delegate method in ViewController
Drag from TextField to ViewController in Interface Builder. Then select the delegate option. Note : Do this for all TextField
Create an IBOutlet for all TextFields
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var txtFieldName: UITextField!
#IBOutlet weak var txtFieldEmail: UITextField!
#IBOutlet weak var txtFieldPassword: UITextField!
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField == txtFieldName {
textField.resignFirstResponder()
txtFieldEmail.becomeFirstResponder()
} else if textField == txtFieldEmail {
textField.resignFirstResponder()
txtFieldPassword.becomeFirstResponder()
} else if textField == txtFieldPassword {
textField.resignFirstResponder()
}
return true
}
}
I suggest that you should use switch statement in textFieldShouldReturn(_:).
// MARK: UITextFieldDelegate
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
switch textField {
case nameTextField:
phoneTextField.becomeFirstResponder()
case phoneTextField:
emailTextField.becomeFirstResponder()
case emailTextField:
descriptionTextField.becomeFirstResponder()
default:
textField.resignFirstResponder()
}
return false
}
This approach needs some changes in table views and collection views, but it's okay for simple forms I guess.
Connect your textFields to one IBOutletCollection, sort it by its y coordinate and in textFieldShouldReturn(_:) just jump to the next textfield until you reach the end:
#IBOutlet var textFields: [UITextField]!
...
textFields.sortInPlace { $0.frame.origin.y < $1.frame.origin.y }
...
func textFieldShouldReturn(textField: UITextField) -> Bool {
if let currentIndex = textFields.indexOf(textField) where currentIndex < textFields.count-1 {
textFields[currentIndex+1].becomeFirstResponder()
} else {
textField.resignFirstResponder()
}
return true
}
Or just look at sample project (xcode 7 beta 4)
Swift & Programmatically
class MyViewController: UIViewController, UITextFieldDelegate {
let textFieldA = UITextField()
let textFieldB = UITextField()
let textFieldC = UITextField()
let textFieldD = UITextField()
var textFields: [UITextField] {
return [textFieldA, textFieldB, textFieldC, textFieldD]
}
override func viewDidLoad() {
// layout textfields somewhere
// then set delegate
textFields.forEach { $0.delegate = self }
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if let selectedTextFieldIndex = textFields.firstIndex(of: textField), selectedTextFieldIndex < textFields.count - 1 {
textFields[selectedTextFieldIndex + 1].becomeFirstResponder()
} else {
textField.resignFirstResponder() // last textfield, dismiss keyboard directly
}
return true
}
}
Caleb's version in Swift 4.0
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if let nextField = self.view.viewWithTag(textField.tag + 1) as? UITextField {
nextField.becomeFirstResponder()
} else {
textField.resignFirstResponder()
}
return false
}
P.S. textField.superview? not working for me
I have tried many codes and finally this worked for me in Swift 3.0 Latest [March 2017]
The "ViewController" class should inherited the "UITextFieldDelegate" for making this code working.
class ViewController: UIViewController,UITextFieldDelegate
Add the Text field with the Proper Tag nuber and this tag number is used to take the control to appropriate text field based on incremental tag number assigned to it.
override func viewDidLoad() {
userNameTextField.delegate = self
userNameTextField.tag = 0
userNameTextField.returnKeyType = UIReturnKeyType.next
passwordTextField.delegate = self
passwordTextField.tag = 1
passwordTextField.returnKeyType = UIReturnKeyType.go
}
In the above code, the "returnKeyType = UIReturnKeyType.next" where will make the Key pad return key to display as "Next" you also have other options as "Join/Go" etc, based on your application change the values.
This "textFieldShouldReturn" is a method of UITextFieldDelegate controlled and here we have next field selection based on the Tag value incrementation
func textFieldShouldReturn(_ textField: UITextField) -> Bool
{
if let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField {
nextField.becomeFirstResponder()
} else {
textField.resignFirstResponder()
return true;
}
return false
}
the easiest way to change to next text Field is this no need for long code
override func viewDidLoad() {
super.viewDidLoad()
emailTextField.delegate = self
passwordTextField.delegate = self
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField == emailTextField {
passwordTextField.becomeFirstResponder()
}else {
passwordTextField.resignFirstResponder()
}
return true
}
I have a good solution for your question.
STEP:
1 - Set your return key from the storyboard.
2 - In your swift file.
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField.returnKeyType == .next {
Email.resignFirstResponder()
Password.becomeFirstResponder()
} else if textField.returnKeyType == .go {
Password.resignFirstResponder()
self.Login_Action()
}
return true
}
3 - Don't forget to set the delegate of the Textfield.
Thank you :)
Just use becomeFirstResponder() method of UIResponder class in your textFieldShouldReturn method. Every UIView objects are UIResponder's subclasses.
if self.emaillabel.isEqual(self.anotherTextField)
{
self.anotherTextField.becomeFirstResponder()
}
You can find more information about becomeFirstResponder() method at Apple Doc's in here.
Swift 4.2
This is a More Generic and Easiest Solution, you can use this code with any amount of TextFields.
Just inherit UITextFieldDelegate and update the Textfield Tag according to the order and copy this function
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
let txtTag:Int = textField.tag
if let textFieldNxt = self.view.viewWithTag(txtTag+1) as? UITextField {
textFieldNxt.becomeFirstResponder()
}else{
textField.resignFirstResponder()
}
return true
}
An alternative method for purists who don't like using tags, and wants the UITextField delegate to be the cell to keep the components separated or uni-directional...
Create a new protocol to link the Cell's and the TableViewController.
protocol CellResponder {
func setNextResponder(_ fromCell: UITableViewCell)
}
Add the protocol to your cell, where your TextField Delegate is also the cell (I do this in the Storyboard).
class MyTableViewCell: UITableViewCell, UITextFieldDelegate {
var responder: CellResponder?
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
responder?.setNextResponder(self)
return true
}
}
Make your TableViewController conform to the CellResponder protocol (i.e. class MyTableViewController: UITableViewController, CellResponder) and implement the method as you wish. I.e. if you have different cell types then you could do this, likewise you could pass in the IndexPath, use a tag, etc.. Don't forget to set cell.responder = self in cellForRow..
func setNextResponder(_ fromCell: UITableViewCell) {
if fromCell is MyTableViewCell, let nextCell = tableView.cellForRow(at: IndexPath(row: 1, section: 0)) as? MySecondTableViewCell {
nextCell.aTextField?.becomeFirstResponder()
} ....
}
No any special, here is my currently using to change the textFiled. So the code in ViewController looks good :). #Swift4
final class SomeTextFiled: UITextField {
public var actionKeyboardReturn: (() -> ())?
override init(frame: CGRect) {
super.init(frame: frame)
super.delegate = self
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fatalError("init(coder:) has not been implemented")
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.resignFirstResponder()
actionKeyboardReturn?()
return true
}
}
extension SomeTextFiled: UITextFieldDelegate {}
class MyViewController : UIViewController {
var tfName: SomeTextFiled!
var tfEmail: SomeTextFiled!
var tfPassword: SomeTextFiled!
override func viewDidLoad() {
super.viewDidLoad()
tfName = SomeTextFiled(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
tfName.actionKeyboardReturn = { [weak self] in
self?.tfEmail.becomeFirstResponder()
}
tfEmail = SomeTextFiled(frame: CGRect(x: 100, y: 0, width: 100, height: 100))
tfEmail.actionKeyboardReturn = { [weak self] in
self?.tfPassword.becomeFirstResponder()
}
tfPassword = SomeTextFiled(frame: CGRect(x: 200, y: 0, width: 100, height: 100))
tfPassword.actionKeyboardReturn = {
/// Do some further code
}
}
}
If you have a lot of textfield components my be it could be better to use an outlet collection, linking textfields and setting Return Key from interface builder
#IBOutlet var formTextFields: [UITextField]!
override func viewDidLoad() {
for textField in formTextFields {
textField.delegate = self
}
}
extension RegisterViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if let componentIndex = formTextFields.firstIndex(of: textField) {
if textField.returnKeyType == .next,
componentIndex < (formTextFields.count - 1) {
formTextFields[componentIndex + 1].becomeFirstResponder()
} else {
textField.resignFirstResponder()
}
}
return true
}
}
Swift 4+
This piece of code will help you.
class YOURClass: UITextFieldDelegate {
override func viewDidLoad() {
//delegate your textfield in here
choosenTextField1.delegate = self
choosenTextField2.delegate = self
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
switch textField.tag {
case 1:
choosenTextField1.becomeFirstResponder()
case 2:
choosenTextField2.becomeFirstResponder()
default:
break
}
return true
}
}
You can go with field tags. I think that's easier than other.
First of all you have enter code hereto give tag to your field.
On my code usernameField tag is 0 and passwordField tag is 1. And I check my tag. Then doing proccess.
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField.tag == 0 {
passwordField.becomeFirstResponder()
} else if textField.tag == 1 {
self.view.endEditing(true)
loginFunc()
} else {
print("Hata var")
}
return false
}
If click return on username field, go password.
Or If you click return when password field, run login function to login.
The viewWithTag is a bad solution because the superview may have views with tags set. This is better:
public extension Collection where Element: Equatable {
func element(after element: Element) -> Element? {
guard let index = firstIndex(of: element) else { return nil }
let nextIndex = self.index(after: index)
return nextIndex < endIndex ? self[nextIndex] : nil
}
}
class Controller: UIViewController {
#IBOutlet weak var firstNameTextField: UITextField!
#IBOutlet weak var lastNameTextField: UITextField!
#IBOutlet weak var companyTextField: UITextField!
private lazy var primaryTextFields: [UITextField] = {
[firstNameTextField, lastNameTextField, companyTextField]
}()
override func viewDidLoad() {
super.viewDidLoad()
primaryTextFields.forEach { $0.delegate = self }
}
}
extension Controller: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if let next = primaryTextFields.element(after: textField) {
next.becomeFirstResponder()
} else if primaryTextFields.contains(textField) {
textField.resignFirstResponder()
}
return true
}
}

Implementing Protocol in Swift to Change UIView's Attribute

I thought I wrote this correctly. I'm trying to get a value set in another viewcontroller (then stored in a singleton) to change the height of the rectangle in my BarGraphView, but my BarGraphView doesn't redraw when the value I'm passing in is changed...can anyone spot what I'm missing?
BarGraphViewController:
import UIKit
class BarGraphViewController: UIViewController, BarGraphViewDataSource {
#IBOutlet weak var barGraphView: BarGraphView! {
didSet {
barGraphView.datasource = self
}
}
#IBOutlet weak var testLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
var firstBarHeight: Int = 30 {
didSet {
firstBarHeight = SharkTankSingleton.instance.firstInvestorTotalasInt / 250
updateUI()
}
}
private func updateUI() {
barGraphView.setNeedsDisplay()
}
func firstBarHeightForBarGraphView(sender: BarGraphView) -> CGFloat? {
return CGFloat(firstBarHeight)
}
BarGraphView:
import UIKit
protocol BarGraphViewDataSource: class {
func firstBarHeightForBarGraphView(sender: BarGraphView) -> CGFloat?
}
#IBDesignable
class BarGraphView: UIView {
#IBInspectable var firstBarColor: UIColor = UIColor.blueColor()
weak var datasource: BarGraphViewDataSource?
override func drawRect(rect: CGRect) {
let firstBarHeight = datasource?.firstBarHeightForBarGraphView(self) ?? 0
let firstBar = CGRect(x: 200, y: 400, width: 30, height: firstBarHeight)
// firstBarColor.setFill()
var firstBarPath = UIBezierPath(rect: firstBar)
UIColor.redColor().setFill()
firstBarPath.fill()

Resources