How to link text fields to functions - swift

I have to create an app, with a label, a text box and a button, that will take the text that the user inputs and test if it is a palindrome and true a yes or no. It requires that I write the palindrome function in another file and call it when the button is press. The return from the function will also have to be displayed in the label field.
I have written and tested out the function but I don't know how connect it with the 3 objects on my app. They didn't teach how to do this yet in the course and googling the terms "linking text field to function in swift" only confused me more.
How should I go about learning how to do this? What terms should I search for to learn about this? I also included my palindrome funciton as reference.
func isPalindrome(word: String) -> Bool{
let word2 = word
let reversedWord = String(word.reversed())
if word2 == reversedWord {
return true
} else {
return false
}
}
isPalindrome(word: "racecar")

I presume that you know how to connect elements from storyboard to your view controller.
Link your textfield to your ViewController
#IBOutlet weak var textfield: UITextField!
then your button.
let text = textfield.text
#IBAction func checkBtn(_ sender: Any) {
isPalindrome(word: text!)
}
func isPalindrome(word: String) -> Bool{
let word2 = word
let reversedWord = String(word.reversed())
if word2 == reversedWord {
print("Is Palindrome")
return true
} else {
print("Isn't Palindrome")
return false
}
}
Also I don't think that you need to return something, except you want to use it.
for example:
if isPalindrome(word: text!){
//do something
} else{
//do something
}

If you are using storyboard, check how the #IBAction and IBOutlet works. its a visual way to link objects from storyboard into your code. If you created these label, textfield, button from code then checkout the addTarget method of UIButton for how to listen for click events.

Related

How to Pass the data given in UITextField to another variable on another class

So i have a UITextField named apiTextField and a function of the saveButton :
func saveButton(_ sender: Any) {
}
I want when the user writes to the UITextField and press the saveButton that text the user wrote be passed to a variable which is called var baseURL:String? in another class.
I didn't find anything related to UITextField so i decided to make this question, another similar one is 10 years old!.
var anotherClass = AnotherClass()
func saveButton(_ sender: Any) {
guard let text = apiTextField.text, !text.isEmpty else { return }
anotherClass.baseURL = text
}
Is that what you are looking for?

NSTextField keep focus/first responder after NSPopover

The object of this app is to ensure the user has entered a certain text in an NSTextField. If that text is not in the field, they should not be allowed to leave the field.
Given a macOS app with a subclass text field, a button and another generic NSTextField. When the button is clicked, an NSPopover is shown which is 'attached' to the field which is controlled by an NSViewController called myPopoverVC.
For example, the user enters 3 in the top field and then clicks the Show Popover button which displays the popover and provides a hint: 'What does 1 + 1 equal'.
Note this popover has a field labelled 1st resp so when the popover shows, that field becomes the first responder. Nothing will be entered at this time - it's just for this question.
The user would click the Close button, which closes the popover. At that point what should happen if the user clicks or tabs away from the field with the '3' in it, the app should not permit that movement - perhaps emitting a Beep or some other message. But what happens when the popover closes and the user presses Tab
Even though that field with the '3' in it had a focus ring, which should indicate the first responder again in that window, the user can click or tab away from it as the textShouldEndEditing function is not called. In this case, I clicked the close button in the popover, the '3' field had a focus ring and I hit tab, which then went to the next field.
This is the function in the subclassed text field that works correctly after the text has been entered into the field. In this case, if the user types a 3 and then hits Tab, the cursor stays in that field.
override func textShouldEndEditing(_ textObject: NSText) -> Bool {
if self.aboutToShowPopover == true {
return true
}
if let editor = self.currentEditor() { //or use the textObject
let s = editor.string
if s == "2" {
return true
}
return false
}
The showPopover button code sets the aboutToShowPopover flag to true which will allow the subclass to show the popover. (set to false when the popover closes)
So the question is when the popover closes how to return the firstResponder status to the original text field? It appears to have first responder status, and it thinks it has that status although textShouldEndEditing is not called. If you type another char into the field, then everything works as it should. It's as if the window's field editor and the field with the '3' in it are disconnected so the field editor is not passing calls up to that field.
The button calls a function which contains this:
let contentSize = myPopoverVC.view.frame
theTextField.aboutToShowPopover = true
parentVC.present(myPopoverVC, asPopoverRelativeTo: contentSize, of: theTextField, preferredEdge: NSRectEdge.maxY, behavior: NSPopover.Behavior.applicationDefined)
NSApplication.shared.activate(ignoringOtherApps: true)
the NSPopover close is
parentVC.dismiss(myPopoverVC)
One other piece of information. I added this bit of code to the subclassed NSTextField control.
override func becomeFirstResponder() -> Bool {
let e = self.currentEditor()
print(e)
return super.becomeFirstResponder()
}
When the popover closes and the textField becomes the windows first responder, that code executes but prints nil. Which indicates that while it is the first responder it has no connection to the window fieldEditor and will not receive events. Why?
If anything is unclear, please ask.
Here's my attempt with help from How can one programatically begin a text editing session in a NSTextField? and How can I make my NSTextField NOT highlight its text when the application starts?:
The selected range is saved in textShouldEndEditing and restored in becomeFirstResponder. insertText(_:replacementRange:) starts an editing session.
var savedSelectedRanges: [NSValue]?
override func becomeFirstResponder() -> Bool {
if super.becomeFirstResponder() {
if self.aboutToShowPopover {
if let ranges = self.savedSelectedRanges {
if let fieldEditor = self.currentEditor() as? NSTextView {
fieldEditor.insertText("", replacementRange: NSRange(location: 0, length:0))
fieldEditor.selectedRanges = ranges
}
}
}
return true
}
return false
}
override func textShouldEndEditing(_ textObject: NSText) -> Bool {
if super.textShouldEndEditing(textObject) {
if self.aboutToShowPopover {
let fieldEditor = textObject as! NSTextView
self.savedSelectedRanges = fieldEditor.selectedRanges
return true
}
let s = textObject.string
if s == "2" {
return true
}
}
return false
}
Maybe rename aboutToShowPopover.
If you subclass each of your NSTextField, you could override the method becomeFirstResponder and make it send self to a delegate class you will create, that will keep a reference of the current first responder:
NSTextField superclass:
override func becomeFirstResponder() -> Bool {
self.myRespondersDelegate.setCurrentResponder(self)
return super.becomeFirstResponder()
}
(myRespondersDelegate: would optionally be your NSViewController)
Note: do not use the same superclass for your alerts TextFields and ViewController TextFields. Use this superclass with added functionality only for TextFields you would want to return to firstResponder after an alert is closed.
NSTextField delegate:
class MyViewController: NSViewController, MyFirstResponderDelegate {
var currentFirstResponderTextField: NSTextField?
func setCurrentResponder(textField: NSTextField) {
self.currentFirstResponderTextField = textField
}
}
Now, after your pop is dismissed, you could in viewWillAppear or create a delegate function that will be called on a pop up dismiss didDismisss (Depends how your pop up is implemented, I will show the delegate option)
Check If a TextField has existed, and re-make it, the firstResponder.
Pop up delegate:
class MyViewController: NSViewController, MyFirstResponderDelegate, MyPopUpDismissDelegate {
var currentFirstResponderTextField: NSTextField?
func setCurrentResponder(textField: NSTextField) {
self.currentFirstResponderTextField = textField
}
func didDismisssPopUp() {
guard let isLastTextField = self.currentFirstResponderTextField else {
return
}
self.isLastTextField?.window?.makeFirstResponder(self.isLastTextField)
}
}
Hope it works.
Huge thanks to Willeke for the help and an answer that lead to a pretty simple solution.
The big picture issue here was that when the popover closed, the 'focused' field was the original field. However, it appears (for some reason) that the windows field editor delegate disconnected from that field so functions such as control:textShouldEndEditing were not being passed to the subclassed field in the question.
Executing this line when the field becomes the first reponder seems to re-connect the windows field editor with this field so it will receive delegate messages
fieldEditor.insertText("", replacementRange: range)
So the final solution was a combination of the following two functions.
override func textShouldEndEditing(_ textObject: NSText) -> Bool {
if self.aboutToShowPopover == true {
return true
}
let s = textObject.string
if s == "2" {
return true
}
return false
}
override func becomeFirstResponder() -> Bool {
if super.becomeFirstResponder() == true {
if let myEditor = self.currentEditor() as? NSTextView {
let range = NSMakeRange(0, 0)
myEditor.insertText("", replacementRange: range)
}
return true
}
return false
}

Swift 4 - Creating a common function for multiple buttons

I'm wondering if there is a more efficient way to code an action that is the same with the exception of which button has been pressed and which item in a struct it relates to. Basically, I have a struct of 10 variables all of which are a boolean type and I have 10 buttons. When the user presses the button, I want to check whether it has already been pressed (using the struct) and then change the background of the button depending on the state and reverse the state. I've copied my current code for one of the buttons but thought I should be able to avoid doing this 10 times!
#IBAction func architectureButtonPressed(_ sender: Any) {
if myInterests.architecture {
myInterests.architecture = false
architectureButton.setBackgroundImage(imageUncheckedNarrow, for: .normal)
} else {
myInterests.architecture = true
architectureButton.setBackgroundImage(imageCheckedNarrow, for: .normal)
}
}
Well one simple way is to have each UIButton point to the same architectureButtonPressed IBAction method. Since the button that's pressed is passed into the method (sender) you can consult it's tag property to know the index of which field in your struct should be updated. (And then you might want to change your struct to just store an array of 10 bools, but up to you).
Then for each UIButton, whether programmatically in storyboard or nib, you'd assign the appropriate index value to the button's tag field.
Create yours IBOutlet for each button.
Create a array and store all buttons like : var arrayButtons : [UIButton] = []
arrayButtons.append[button1]
arrayButtons.append[button2]
...
Create a array of booleans to store true/false: var arrayBools : [Bool] = [] and initialize if some value.
Note that the indexes of the arrayButtons and arrayBools must be same related.
Create selector function to listen touch buttons.
button.addTarget(self, action: #selector(my_func), for: .touchUpInside)
#objc func my_func(_ sender : UIButton) {
for i in 0...arrayButtons.size-1 {
if arrayButtons[i] == sender {
if arrayBooleans[i] {
arrayBooleans[i] = false
arrayButtons[i].setImage()
} else {
arrayBooleans[i] = true
arrayButtons[i].setImage()
}
}
}
}
My suggestion is to manage the images in Interface Builder via State Config (Default/Selected)
Then assign an unique tag starting from 100 to each button and set the isSelected value in the IBAction to the corresponding struct member in a switch statement:
#IBAction func buttonPressed(_ sender: UIButton) {
switch sender.tag {
case 100: myInterests.architecture = sender.isSelected
case 101: myInterests.art = sender.isSelected
...
default: break
}
}
Alternatively use Swift's native KVC with WriteableKeypath
let keypaths : [WritableKeyPath<Interests,Bool>] = [\.architecture, \.art, \.fashion, \.history, \.localCulture, \.music, \.nature, \.shopping, \.sport, \.anything]
#IBAction func buttonPressed(_ sender: UIButton) {
let index = sender.tag - 100
let keypath = keypaths[index]
myInterests[keyPath: keypath] = sender.isSelected
}

How to delete a character from a UI Label in Swift?

I am new to Swift and I am having issues with deleting a character from a UI Label that I have created. I am trying to make a simple phone dailer app, and I am trying ti implement a backspace button. My UI Label is called DailerLabel, and I know I'm supposed to use the dropLast() function but I keep running into issues about mismatching types or unwrappers. I am not really sure what I am supposed to do here. I tried the thing in the commented code which didn't work, and then I tried what I listed below which doesn't either. Could anyone help me?
#IBAction func backspaceButtonPressed(_ sender: UIButton) {
if (!((DailerLabel.text?.isEmpty)!)) {
// DailerLabel.text?.substring(to: (DailerLabel.text?.index(before: (DailerLabel.text?.endIndex)!))!)
let temp = DailerLabel.text
temp?.dropLast()
DailerLabel.text = temp
}
You can try this one, replace label with your own UILabel
var name: String = label.text! //shauket , for example
name.remove(at: name.index(before: name.endIndex))
print(name) //shauke
label.text = name
print(label.text!)
You are very close. dropLast actually returns the string without the last character and you haven't stored that to anything so there is no change. You also have to conver back to String from Substring.
#IBAction func backspaceButtonPressed(_ sender: UIButton) {
if (!((DailerLabel.text?.isEmpty)!)) {
let temp = DailerLabel.text ?? ""
DailerLabel.text = String(temp.dropLast())
}
}
Here's a better version
#IBAction func backspaceButtonPressed(_ sender: UIButton) {
guard
let text = dialerLabel.text,
!text.isEmpty
else {
return
}
dialerLabel.text = String(text.dropLast())
}

Xcode v7.2 - New Radio Buttons: How to find selected

I am attempting to detect which Radio Button is currently selected using Xcode 7's new Radio Button template (NSButton).
I have created a simple action that will print to log the title of the sender when a radio button is selected. This does work:
#IBAction func radioButtonClicked(sender: AnyObject)
{
print(sender.selectedCell()!.title);
}
But what I am really looking for is the ability to know which radio button is selected elsewhere in my codes (specifically on an IBAction for a button click). I have tried:
#IBAction func uploadButtonPressed(sender: AnyObject)
{
print (radioButton.selectedCell()!.title);
}
This does compile and execute, the problem is it always gives me the title of the first radio button not the one that is actually selected.
Any ideas?
The "closest" I can get (which is not very clean, but works "kinda") is to see if radioButton.cell?state = 1. This tells me that the first radio button is selected. But this is a very poor way to code this and only allows for 2 radio button options.
if (radioButton.cell?.state == 1)
{
print ("Radio 1 Selected");
}
else
{
print ("Radio 2 Selected");
}
Use the identifier or tag property in Interface Builder to distinguish between radio buttons like:
#IBAction func selectOption(sender: AnyObject) {
let id:String = sender.identifier!!
case "first identifier":
// do some stuff
// ...
default:
// do default stuff
}
Another option would be a switch-case statement and defining outlets for each radio button like:
#IBOutlet weak var firstOption: NSButton!
#IBOutlet weak var secondOption: NSButton!
#IBOutlet weak var thirdOption: NSButton!
#IBAction func selectOption(sender: AnyObject) {
switch sender as! NSButton {
case firstOption:
print("firstOption")
case secondOption:
print("secondOption")
case thirdOption:
print("thirdOption")
default:
print("won't be called when exhaustive but must always be implemented")
}
}
I ended up finding a method for this.
First I setup a variable to store which item was checked (and pre-set the value to the value I want for the first radio button)
var databaseUploadMethod = "trickle-add";
Then I compare to see the title value of the selected item:
#IBAction func radioButtonClicked(sender: AnyObject)
{
if ((sender.selectedCell()!.title) == "Trickle Add")
{
databaseUploadMethod = "trickle-add";
}
else if ((sender.selectedCell()!.title) == "Bulk Add All Now")
{
databaseUploadMethod = "bulk-add";
}
}
For reference: I have two radio buttons, the first has a title of "Trickle Add" and the second is "Bulk Add All Now". Those are the values I am comparing in my if statement.