My save button is hidden with the function saveBtnHidden() in the code below. However, the save button won't reappear when text is typed into the text field. I have tried multiple solutions similar to this. Every time that I type into the text field, the save button just won't show up.
import UIKit
class TableViewController: UITableViewController, UITextFieldDelegate {
#IBOutlet weak var saveBtn: UIButton!
#IBOutlet var nicknameField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
saveBtnHidden()
}
func saveBtnHidden() {
if (nicknameField.text?.isEmpty ?? true) {
// is empty
saveBtn.isHidden = true
} else {
saveBtn.isHidden = false
}
}
#IBAction func saveBtnPressed(_ sender: Any) {
performSegue(withIdentifier: "nextPage", sender: nil)
}
}
You are getting this error because your function saveBtnHidden() is only called once in viewDidLoad(). It does not get called again when the text in your text field changes. To detect when text changes, you will need to add a target to your text field that calls a function when it changes (.editingChanged) like this:
nicknameField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
Then in the textFieldDidChange call your saveBtnHidden() function:
func textFieldDidChange(_ textField: UITextField) {
saveBtnHidden()
}
Code adapted from: How do I check when a UITextField changes?
Use delegate to be notify of any change. Delegates is a key part of iOS development and Apple's framework.
class TableViewController: UITableViewController, UITextFieldDelegate {
#IBOutlet weak var saveBtn: UIButton!
#IBOutlet var nicknameField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
saveBtnHidden()
nicknameField.delegate = self
}
func textFieldDidChange(_ textField: UITextField) {
saveBtnHidden()
}
// More of your source code below...
Related
I have a custom cell class with two buttons and one label defined in its own class. Am using protocol-delegates to update the value of the label when the button is pressed. But am not able to figure out how to update the UITableView.
protocol customCellDelegate: class {
func didTapDecrementBtn(_ sender: AllCountersTableViewCell)
func didTapIncrementBtn(_ sender: AllCountersTableViewCell)
func updateTableViewCell(_ sender: AllCountersTableViewCell)
}
class AllCountersTableViewCell: UITableViewCell {
#IBOutlet weak var counterValueLbl: UILabel!
#IBOutlet weak var counterNameLbl: UILabel!
#IBOutlet weak var counterResetDateLbl: UILabel!
#IBOutlet weak var counterDecrementBtn: UIButton!
#IBOutlet weak var counterIncrementBtn: UIButton!
weak var delegate: customCellDelegate?
#IBAction func decrementBtnPressed(_ sender: UIButton) {
delegate?.didTapDecrementBtn(self)
delegate?.updateTableViewCell(self)
}
#IBAction func incrementBtnPressed(_ sender: UIButton) {
delegate?.didTapIncrementBtn(self)
delegate?.updateTableViewCell(self)
}
In my ViewController, I have provided the delegate. But reloadData() is not working, so although sender.counterLbl.value is changing its not reflecting on the view.
extension AllCountersVC: customCellDelegate {
func didTapIncrementBtn(_ sender: AllCountersTableViewCell) {
guard let tappedCellIndexPath = tableView.indexPath(for: sender) else {return}
let rowNoOfCustomCell = tappedCellIndexPath[1]
let newValue = String(allCounter[rowNoOfCustomCell].incrementCounterValue(by: allCounter[rowNoOfCustomCell].counterIncrementValue))
sender.counterValueLbl.text = newValue
}
func updateTableViewCell(_ sender: AllCountersTableViewCell) {
allCountersTableView.reloadData()
}
In cellForRow you must set cell.delegate = self for this logic to start working. Its not enough just to make you controller confroms to your custom cell delegate protocol. From your post I assume delegate is always nil in the cell, that is why it does not work.
How to start a new line in UITextView with a little space, called paragraph I believe?
What you need to do is make the controller confront to UITextFieldDelegate
for example:
class ViewController: UIViewController,UITextFieldDelegate
{
//link the textfield
#IBOutlet weak var textField: UITextField!
override func viewDidLoad()
{
super.viewDidLoad()
// link the delegate to the textfield
textField.delegate = self
}
//this function detects the return button when it's pressed
func textFieldShouldReturn(_ textField: UITextField) -> Bool
{
//since we always want to start a new line with space we override the default function
textField.text += "\n "
return false
}
I'm making a simple swift application. Right now I have an input text field that takes a user input and outputs it to the output user field when a button is clicked. As shown in the code below:
import UIKit
import Foundation
class ViewController: UIViewController {
#IBOutlet weak var userNameField: UITextField!
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
userNameField.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func enterTapped(_ sender: Any) {
textView.text = "\(userNameField.text!)"
}
}
extension ViewController : UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}
I wanted to perform some word processing before the text is displayed to the user but, every time I try coding I get a bunch of errors. Where in the code should my word processing code go. When I refer to word processing I'm referring to some string commands such as: split, combine...
It should be here
#IBAction func enterTapped(_ sender: Any) {
let str = userNameField.text!
// do what you want with str
let addComma = str + ","
textView.text = addComma
}
Where in the code should my word processing code go.
you want to do some processing and put the processed text in the out-filed. As this process needs to take place on button action. I understand the processing should go in following method:
#IBAction func enterTapped(_ sender: Any) {
// this is the correct place for processing your string.
let inputStr = userNameField.text!
//Process 'inputStr" as needed.
textView.text = inputStr
}
I'm very new to Swift MacOS programming and have been learning by writing small test applications.
The aim of this application is to enable the pushbutton when the 2nd textfield has the focus, and disable it when it is not focused.
I have found that by subclassing the NSTextField I can override becomeFirstResponder() however don't know how to set the button to be disabled from the subclass.
ViewController:
class ViewController: NSViewController {
#IBOutlet public weak var pushButton: NSButton!
#IBOutlet weak var textField3: NSTextField!
#IBOutlet weak var textField2: GSTextField!
#IBOutlet weak var textField1: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
textField2.delegate = self
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
func chgButton(onoff: Bool){
pushButton.isEnabled = onoff
}
}
// When the field completes editing make the pushbutton disabled.
extension ViewController: NSTextFieldDelegate {
override func controlTextDidEndEditing(_ obj: Notification) {
print("did end")
chgButton(onoff: false)
}
}
GSTextField.Swift
class GSTextField: NSTextField {
override func becomeFirstResponder() -> Bool {
print("GSTextField Firstresponder")
////*** I need to set the button to be enabled here
return super.becomeFirstResponder()
}
}
Your NSTextField subclass needs to be able to communicate with the pushButton. The easiest way to do this is to pass a reference to the pushButton to your text field and then update the push button from there.
Update your ViewController like this:
override func viewDidLoad() {
super.viewDidLoad()
textField2.delegate = self
textField2.pushButton = pushButton
// Do any additional setup after loading the view.
}
And your GSTextField like this:
class GSTextField: NSTextField {
weak var pushButton: NSButton?
override func becomeFirstResponder() -> Bool {
print("GSTextField Firstresponder")
pushButton?.isEnabled = true
return super.becomeFirstResponder()
}
override func resignFirstResponder() -> Bool {
pushButton?.isEnabled = false
return super.resignFirstResponder()
}
}
It should be noted that while this works fine in this toy example, this is a sub-optimal solution to this problem because it tightly couples the pushButton and the GSTextField. A better solution would be to use delegation to communicate the focus changes to the ViewController, and let the ViewController handle the updates.
Your GSTextField would look like this:
protocol FocusObservable: class {
func didGainFocus(sender: Any)
func didLoseFocus(sender: Any)
}
class GSTextField: NSTextField {
weak var focusDelegate: FocusObservable?
override func becomeFirstResponder() -> Bool {
print("GSTextField Firstresponder")
focusDelegate?.didGainFocus(sender: self)
return super.becomeFirstResponder()
}
override func resignFirstResponder() -> Bool {
focusDelegate?.didLoseFocus(sender: self)
return super.resignFirstResponder()
}
}
And then you would add protocol conformance to the ViewController:
extension ViewController: FocusObservable {
func didGainFocus(sender: Any) {
pushButton.isEnabled = true
}
func didLoseFocus(sender: Any) {
pushButton.isEnabled = false
}
}
and set the focusDelegate of the text field:
override func viewDidLoad() {
super.viewDidLoad()
textField2.delegate = self
textField2.focusDelegate = self
// Do any additional setup after loading the view.
}
This is driving me crazy. The function updateTextView() is being called, verified by the print statements, but it is not setting the label in my view controller, and the print statements for the label are returning nil even though it has a default value set which is visible when the app is loaded. Whats more perplexing is that I set up a test button to call this function separately, and when I call it with test(), then the label updates properly.
class GoalDetailViewController: UIViewController, TextDelegate {
#IBAction func test(sender: AnyObject) {
updateTextView()
}
func updateTextView() {
print(goalSummaryTextBox?.text)
print("delegate called")
self.goalSummaryTextBox?.text = GoalsData.summaryText
print(goalSummaryTextBox?.text)
}
#IBOutlet weak var goalTitle: UILabel?
#IBOutlet weak var goalCreationDate: UILabel?
#IBOutlet weak var goalSummaryTextBox: UITextView?
override func viewDidLoad() {
super.viewDidLoad()
goalSummaryTextBox?.text = GoalsData.summaryText
}
}
updateTextView() is being called through a delegate method after I pop a different view controller, as can be seen below:
class TextEditViewController: UIViewController {
var textDelegate: TextDelegate?
#IBOutlet weak var textView: UITextView?
func configureView() {
navigationItem.title = "Edit Description"
navigationItem.setRightBarButtonItem((UIBarButtonItem(barButtonSystemItem: .Done, target: self, action: "segue")), animated: true)
}
func segue() {
textDelegate = GoalDetailViewController()
if let text = textView?.text {
GoalsData.summaryText = text
}
textDelegate?.updateTextView()
self.navigationController?.popViewControllerAnimated(true)
}
}
The line causing the issue is in the TextEditViewController below:
textDelegate = GoalDetailViewController()
What this line does is creates a new instance of GoalDetailViewController, and sets it as the delegate to the TextEditViewController. But, what you want is the original instance of GoalDetailViewController to be the delegate. This is why you were seeing the logs when popping TextEditViewController, since it was executing the other instance (which wasn't part of the view hierarchy). It also explains why all your IBOutlets are nil when stepping through updateTextView() on the delegate call, and that the button you added updates the text properly.
The solution is to make the delegate connection in the prepareForSegue method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let destination = segue.destinationViewController as? TextEditViewController {
destination.textDelegate = self
}
}
Add the above code to your GoalDetailViewController.
EDIT:
The below code will ensure that this problem does not happen in the future. Change the delegate's definition to:
weak var textDelegate: TextDelegate?
and change your protocol to:
protocol TextDelegate: class {
func updateTextView()
}