UITextField has trailing the whitespace after secureTextEntry toggle - ios

I am trying to toggle the password text field, but I am facing a problem of white space at right side of text field.
//Eye button action.
#IBAction func btnEyePassword(sender: AnyObject)
{
//If password text is secure.
if (self.passrordText.secureTextEntry == true)
{
self.passrordText.secureTextEntry = false
}
else
{
self.passrordText.secureTextEntry = true
}
}

Swift 4 solution
func togglePasswordVisibility() {
password.isSecureTextEntry = !password.isSecureTextEntry
if let textRange = password.textRange(from: password.beginningOfDocument, to: password.endOfDocument) {
password.replace(textRange, withText: password.text!)
}
}
You don't need extra if statement for simple toggle isSecureTextEntry property, but you should replace text this way to force UITextField recalculate text width to avoid extra space.
UPDATE
Swift 4.2
Instead of
password.isSecureTextEntry = !password.isSecureTextEntry
you can do this
password.isSecureTextEntry.toggle()

Dont worry, in both cases when you print your textField.text your data will be same, there would not be white space.
Check with print(self.passrordText.text)
Declare var str : String = ""
#IBAction func btnEyePassword(sender: AnyObject)
{
//If password text is secure.
if (self.passrordText.secureTextEntry == true)
{
self.passrordText.secureTextEntry = false
self.passrordText.text = ""
self.passrordText.text = str
print(self.passrordText.text)
}
else
{
self.passrordText.secureTextEntry = true
str = self.passrordText.text!
print(self.passrordText.text)
}
}
This happens because of space consumption, "W" keeps much space while "i" keep lesser space.

Related

Detect when a button is pressed outside of the IBAction in Swift?

I'm a beginner programmer making my first real app, a calculator in Swift.
It's mostly working, but one issue I'm running into is how it displays numbers after pressing one of the operator buttons. Currently, whenever an operator button is pressed, I have it set the label at the top of the calculator to "0". But on actual calculators, this top display won't change until another number button is pressed.
If I don't reset the display to 0, then any number buttons that are pressed will add to the current text at the top, and mess up the equation that the calculator will have to do (i.e. 2+2 displays 22, and the solution it displays is 22+2=24)
I'm wondering if it's possible to detect when one of the number buttons is pressed (listed in my code as the intButtonPressed IBAction) outside of the intButtonPressed function? That way I can keep the top label the same until the user starts inputting more text, then I can set it to 0 to prevent the calculator from breaking.
Any other possible (better) solutions would be welcome as well
Here's my code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var topLabel: UILabel!
var num1 = Double()
var solution = Double()
var op = String()
#IBAction func intButtonPressed(_ sender: UIButton) {
if topLabel.text == "0" {
topLabel.text = sender.currentTitle
}
else if topLabel.text == String(solution) {
num1 = Double(topLabel.text!)!
solution = 0.00
topLabel.text = sender.currentTitle
// Basically stops user from adding to solution?
// Also stores solution as num1 and clears solution field
}
else {
topLabel.text = topLabel.text! + sender.currentTitle!
// Keep typing
}
if (topLabel.text?.count)! > 12 {
topLabel.text = "Error"
}
else {
return
}
// if its greater than 12 characters, display "error"
}
#IBAction func clearButtonPressed(_ sender: UIButton) {
num1 = 0.00
solution = 0.00
topLabel.text = "0"
}
#IBAction func opButtonPressed(_ sender: UIButton) {
if sender.currentTitle == "=" {
equals()
}
else {
op = sender.currentTitle!
num1 = Double(topLabel.text!)!
topLabel.text = "0"
}
// Need it to display num1 until I press another button, then I need it to only display that text.
}
func equals() {
switch op {
case "+":
add()
case "-":
subtract()
case "×":
multiply()
case "÷":
divide()
default:
print(Error.self)
}
}
func add() {
solution = num1 + Double(topLabel.text!)!
topLabel.text = String(solution)
}
func subtract() {
solution = num1 - Double(topLabel.text!)!
topLabel.text = String(solution)
}
func multiply() {
print("topLabel = ", topLabel.text!)
solution = num1 * Double(topLabel.text!)!
print("solution = ", solution)
topLabel.text = String(solution)
}
func divide() {
solution = num1 / Double(topLabel.text!)!
//answer()
}
}
Update for anyone who finds this in the future: I've solved the issue, and realized that I wasn't very clear with what I wanted it to do. I solved the problem simply by adding a condition to the if/else statement in the inButtonPressed function that detects if the topLabel is 0. By rewriting that to detect if the topLabel is 0 OR the same as num1, and then changing the else statement in the opButtonPressed function to set topLabel to String(num1), the program does exactly what I want. Here's the rewritten code:
#IBAction func intButtonPressed(_ sender: UIButton) {
if topLabel.text == "0" || topLabel.text == "0.0" || topLabel.text == String(num1){
dotPressed = false
// Resets dotPressed whenever the top is 0 or num1 and an int button is pressed
topLabel.text = sender.currentTitle
}
else if topLabel.text == String(solution) {
num1 = Double(topLabel.text!)!
solution = 0.00
topLabel.text = sender.currentTitle
// Basically stops user from adding to solution?
// Also stores solution as num1 and clears solution field
}
else {
topLabel.text = topLabel.text! + sender.currentTitle!
}
}
#IBAction func opButtonPressed(_ sender: UIButton) {
if sender.currentTitle == "=" {
equals()
}
else {
op = sender.currentTitle!
num1 = Double(topLabel.text!)!
topLabel.text = String(num1)
}
// Successfully displays num1 until I press another button, then only displays that text.
I also added a separate function to handle decimals, and I had to update the code in there as well to keep that working.

How to add two numbers in Swift only when both of the text fields are filled in

I'm trying to add two numbers in Swift 5, and I want to add some error checks. I don't want to make it possible for a user to click on the plus button if both of the text fields are not filled in. I tried with the if state below but it did not work.
the whole function:
#IBAction func sum(_ sender: Any) {
let one = input1.text
let oneInt = Int(one!)
let two = input2.text
let twoInt = Int(two!)
let total = oneInt! + twoInt!
label.text = "\(total)"
if(input2.text == nil){
addBtn.isEnabled = false
}
if(input1.text == nil){
addBtn.isEnabled = false
}
}
Try to use guard like this. If your input field does not contain any value that field return blank string and when you try to get integer value from that string it will return nil and your add button will be disable.
#IBAction func sum(_ sender: Any) {
guard let text1 = input1.text, let intValue1 = Int(text1) else {
addBtn.isEnabled = false
return
}
guard let text2 = input2.text, let intValue2 = Int(text2) else {
addBtn.isEnabled = false
return
}
label.text = "\(intValue1 + intValue2)"
}
A nice and simple way is to addTarget to your textFiels. This will enable you to handle the events on the text field. In this scenario we'll use .editingChanged and use a single selector to achieve our goal:
What we'll do : We will listen for when someone types something in the textfield. Whenever a text changed was made, we'll check to see if all the textfields was populated and then if it was we enable the sum button.
A small controller sample :: Make sure to read the comments to understand the code faster
import UIKit
class ViewController: UIViewController {
#IBOutlet var textfield1: UITextField!
#IBOutlet var textfield2: UITextField!
#IBOutlet var sumButton: UIButton!
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
sumButton.isEnabled = false /// Disable the button first thing
[textfield1, textfield2].forEach {
$0.addTarget(self, action: #selector(editingChanged(_:)), for: .editingChanged) /// add targets to handle the events (in your case it listens for the 'editingChanged' event )
}
}
#objc func editingChanged(_ textField: UITextField) {
/// Here we just loop through all our textfields
for each in [textfield1, textfield2] {
if let text = each?.text { /// Just make sure the textfields text is not nil
if text.count < 1 {
// If the textfiels text has no value in, then we keep the button disabled and return
sumButton.isEnabled = false
return
}
} else {
/// Else if the text field's text is nill, then return and keep the button disabled
sumButton.isEnabled = false
return
}
}
sumButton.isEnabled = true /// If the code reaches this point, it means the textfields passed all out checks and the button can be enabled
}
#IBAction func sum(_ sender: Any) {
let one = textfield1.text!
let two = textfield2.text!
guard let oneInt = Int(one), let twoInt = Int(two) else {
print("Whatever was in that text fields, couldn't be converted to an Int")
label.text = "Be sure to add numbers."
return
}
let total = oneInt + twoInt
label.text = "\(total)"
}
}
textfields are not nil but empty strings. so make your comparison like :
if input1.text == "" {
// do your check here
}
Seems like you want to start with addBtn.isEnabled = false then update it whenever the user enters two valid integers into the text fields, i.e. Int(input1.text ?? "") != nil && Int(input2.text ?? "") != nil. You can do this by adding a target to your textfields (input1 and input2) for .editingChanged events. For example, if you're doing this in a UIViewController, you can do this in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
addBtn.isEnabled = false
input1.addTarget(self, action: #selector(textFieldDidEdit(_:)), for: .editingChanged)
input2.addTarget(self, action: #selector(textFieldDidEdit(_:)), for: .editingChanged)
}
Where textFieldDidEdit(_:) action looks like:
#objc func textFieldDidEdit(_ sender: UITextField) {
addBtn.isEnabled = Int(input1.text ?? "") != nil && Int(input2.text ?? "") != nil
}
Finally your sum function becomes:
#IBAction func sum(_ sender: UIButton) {
guard let oneInt = Int(input1.text ?? ""), let twoInt = Int(input2.text ?? "") else {
return
}
let total = oneInt + twoInt
label.text = "\(total)"
}
As all of the number validation has moved to the textFieldDidEdit(_:) function.

Adding multiple lines in UILabel

I am trying to make a times table app with user's input value. Whenever I try to print result on a label, it just prints the last line and not a full table.
How can I append text in UILabel? Here is my code:
if let textBox = textField.text{
let textBoxNum = Int(textBox)
if let number = textBoxNum{
if number > 0{
let i = 1
while i <= 20{
label.text = "\(number) x \(i) = \(number * i)"
}
}
else{
label.text = "Please enter a positive whole number"
}
}
else{
label.text = "Please enter a positive whole number"
}
}
The assignment operator = just reassigns a new value to the text of your label. You need to add on to the existing text using the Compound Assignment Operator call addition assignment operator += which will assign new value by adding it to the existing value.
let a:String = "Hello"
print(a) // Hello
a += " World!"
print(a) // Hello World!
This same terminology will be applied to your case where you will use += on the text property of the label.
if let textBox = textField.text{
let textBoxNum = Int(textBox)
if let number = textBoxNum{
if number > 0{
let i = 1
while i <= 20{
label.text += "\(number) x \(i) = \(number * i)\n"
// Use of compound operator here will append the text so you have
// to use a new line escape character to print the text properly.
}
}
else{
label.text = "Please enter a positive whole number"
}
}
else{
label.text = "Please enter a positive whole number"
}
}
More about operators here:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html
Additional to the other answers (using += and \n for line breaks):
If you use a label, you'll have to set the number of lines to zero, which will allow unlimited number of lines:
label.numberOfLines = 0
But you might better want a UITextField to display multi-line text, which allows the user to scroll up and down if the text gets too long. You would only have to disable the editing / keyboard popping up, something like:
class TheViewController: UIViewController, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.theTextField.delegate = self
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
return false
}
}

Preventing Cursor Move when Validation Fails

I would like to implement validation in my view such that, when the user enters invalid text into a UITextField, all other UITextFields are disabled and the cursor remains in the current place. I am using the SwiftValidator library (https://github.com/jpotts18/SwiftValidator).
The validation of a text field is triggered by UIControlEvent.EditingDidEnd, which typically happens when the user moves to the next field. I have not been able to prevent the cursor moving to the other UITextField nor have I been able to get it back to the view that failed validation. What would be the right approach?
Below are the relevant sections of my code, where validateSingleValue() is the place where I'm trying to implement the logic for this task.
My ViewController acts as a delegate to my custom control groups, handling EditingDidEnd events. The custom controls are typically a UILabel and a UITextField embedded in a UIView.
func textViewEditingDidEnd(editTextPropertyGroup: EditTextPropertyGroup) {
validateField(editTextPropertyGroup.propertyValue)
}
Fields are validated using
func validateField(sender: AnyObject) {
switch (sender as! UITextField) {
case firstNameField:
validateSingleValue(textfield: firstNameField, validationrule: firstNameRule(), errorlabel: firstNameErrorLabel, container: firstNameField.superview)
case lastNameField:
validateSingleValue(textfield: lastNameField, validationrule: lastNameRule(), errorlabel: lastNameErrorLabel, container: lastNameField.superview)
case adressField:
validateSingleValue(textfield: addressField, validationrule: addressRule(), errorlabel: addressErrorLabel, container: addressField.superview)
case emailField:
validateSingleValue(textfield: emailField, validationrule: emailRule(), errorlabel: emailErrorLabel, container: emailField.superview)
default:
print("No fields to validate.")
}
}
func validateSingleValue(textfield textField:UITextField, validationrule validationRule: Rule, errorlabel errorLabel: UILabel, container: UIView?) -> Bool {
if validationRule.validate(textField.text!) {
errorLabel.hidden = true
textField.layer.borderColor = UIColor.grayColor().CGColor
textField.layer.borderWidth = 0.5
enableAllControls()
return true
} else {
errorLabel.hidden = false
textField.layer.borderColor = UIColor.redColor().CGColor
textField.layer.borderWidth = 1.0
if let unwrappedContainer = container {
textField.becomeFirstResponder()
disableAllControls()
unwrappedContainer.userInteractionEnabled = true
unwrappedContainer.alpha = 1.0
}
return false
}
}
and for disabling and enabling the controls:
override func disableAllControls() {
self.FirstNameSection.propertyValue.userInteractionEnabled = false
self.FirstNameSection.alpha = 0.3
self.LastNameSection.userInteractionEnabled = false
self.LastNameSection.alpha = 0.3
self.AddressSection.userInteractionEnabled = false
self.AddressSection.alpha = 0.3
self.EmailSection.userInteractionEnabled = false
self.EmailSection.alpha = 0.3
}
override func enableAllControls() {
self.FirstNameSection.propertyValue.userInteractionEnabled = true
self.FirstNameSection.alpha = 1.0
self.LastNameSection.userInteractionEnabled = true
self.LastNameSection.alpha = 1.0
self.AddressSection.userInteractionEnabled = true
self.AddressSection.alpha = 1.0
self.EmailSection.userInteractionEnabled = true
self.EmailSection.alpha = 1.0
}
Jerry, I did something like this many moons ago; this is a snippet of the code in objective C I fear, sorry.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if ([textField.accessibilityIdentifier isEqualToString:#"ClassSize"]) {
if ([self isNumeric:string]) {
return YES;
} else {
return NO;
}
}
return YES;
}
Basically if the user tried to input incorrect data, the keyboard didn't respond.
I used this technique to guard agains't number that were beyond a certain range and indeed even to restrict said user when entering a hexadecimal number I recall. They could only enter 0-F in the field.
It turns out that all this can be achieved with only a few lines of code. To support validation, the UITextFieldDelegate protocol defines the following methods:
textFieldShouldBeginEditing(_:)
textFieldDidBeginEditing(_:)
textFieldShouldEndEditing(_:)
textFieldDidEndEditing(_:)
(see documentation here)
The code in the question can be reduced to:
var shouldEndEdit: Bool = true
func textFieldShouldEndEditing(textField: UITextField) -> Bool {
validator.validateField(textField) { error in
if error == nil {
self.shouldEndEdit = true
} else {
self.shouldEndEdit = false
}
}
return self.shouldEndEdit
}
plus the validation rules as described here.

String to int Button

I'm working on a math multiple choice buttons based swift app. I want to check if the selected button is the correct answer. i have the following code.
let sum = num1.text!.toInt()! + num2.text!.toInt()!
if btn2.titleLabel! == "\(sum)"
{
check.text = "Right"
}
else
{
check.text = "Wrong"
}
it still doesn't work when I click on a answer choice. It says wrong for all
You are comparing the actual UILabel object to the sum and not the text of the label to the sum, so try:
let sum = num1.text!.toInt()! + num2.text!.toInt()!
if btn2.titleLabel!.text == "\(sum)"
{
check.text = "Right"
}
else {
check.text = "Wrong"
}
You should connect your buttons to an #IBAction function.
Keep the sum as an Int. No need to convert it to a String.
The function could look like this.
#IBAction func theButtons(sender: UIButton) {
let p = sender.currentTitle!.toInt()
if sum==p {
check.text = "right"
}
else{
check.text = "wrong"
}
}

Resources