I'm developing note app, when the text view is empty the done button should be disabled so user could not be able to save empty notes into data base, else the button should be enabled.
here's my code below, my attempts have failed; how I can solve this problem?
#IBOutlet weak var textView: UITextView!
#IBOutlet weak var done: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
title = note?.text
if (self.textView.text.isEmpty){
done.enabled = false
}
if let noteContent = note
{
textView.text = noteContent.text
}
self.navigationController!.toolbarHidden = false;
}
func textViewShouldBeginEditing(textView: UITextView) -> Bool{
done.enabled = true
return done.enabled
}
Make your view controller conform to UITextViewDelegate protocol
In Interface Builder, connect the delegate on the text view to your view controller.
Add the following function to your view controller:
func textViewDidChange(textView: UITextView) {
if textView == self.textView {
self.doneButton.enabled = !textView.text.isEmpty
}
}
Try to use another delegate method for you're purpose. This is example :
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
textView.delegate = self
if (textView.text.isEmpty) {
button.enabled = false
}
}
}
extension ViewController: UITextViewDelegate {
func textView(textView: UITextView, range: NSRange, replacementText text: String) -> Bool
{
if (!textView.text.isEmpty) {
button.enabled = true
} else {
button.enabled = false
}
return true
}
}
Try this in textViewDidChange method:
yourBarButtonItem.isEnabled = !(yourTextField.text?.isEmpty ?? false)
Related
I have this swift file where I'm trying to create a custom class to handle placeholders for multiple text views on one page. I prefer a separate class because I think its cleaner to handle multiple views. Problem is, the functions textViewDidEndEditing and textViewDidBeginEditing are not being called when those events occur.
I've had it successfully work by setting the PostPage to the delegate, but I don't see how I could do that with multiple UITextViews.
This is my code:
import UIKit
class PostPage: UIViewController, UITextViewDelegate {
#IBOutlet weak var eventDescription: UITextView!
#IBOutlet weak var eventTitle: UITextView!
var placeholderShowing = true
override func viewDidLoad() {
super.viewDidLoad()
// Add a placeholder for the text
let ph_descr = PlaceHolderHandler(placeholderText: "Description", textBox: eventDescription)
eventDescription.delegate = ph_descr
let ph_title = PlaceHolderHandler(placeholderText: "Title", textBox: eventTitle)
eventTitle.delegate = ph_title
}
}
class PlaceHolderHandler: NSObject, UITextViewDelegate {
var placeholderShowing = true
var placeholderText = ""
required init(placeholderText: String, textBox: UITextView) {
super.init()
self.placeholderText = placeholderText
textBox.text = placeholderText
textBox.textColor = UIColor.lightGray
}
// MARK:- UITextViewDelegates
public func textViewDidBeginEditing(_ textView: UITextView) {
if self.placeholderShowing {
textView.text = ""
textView.textColor = UIColor.black
self.placeholderShowing = false
}
}
public func textViewDidEndEditing(_ textView: UITextView) {
if textView.text == "" {
textView.text = "Description"
textView.textColor = UIColor.lightGray
self.placeholderShowing = true
}
}
}
When I select either of the UITextViews, I'd like the corresponding function to be called, and have the text adjust accordingly.
You need to retain phDescr and phTitle as in your code they are local variables so get de-allocated end of viewDidLoad
import UIKit
class PostPage: UIViewController, UITextViewDelegate {
#IBOutlet weak var eventDescription: UITextView!
#IBOutlet weak var eventTitle: UITextView!
var placeholderShowing = true
var phDescr, phTitle:PlaceHolderHandler!
override func viewDidLoad() {
super.viewDidLoad()
// Add a placeholder for the text
phDescr = PlaceHolderHandler(placeholderText: "Description", textBox: eventDescription)
eventDescription.delegate = phDescr
phTitle = PlaceHolderHandler(placeholderText: "Title", textBox: eventTitle)
eventTitle.delegate = phTitle
}
}
also use camelCase to name variables
I've had it successfully work by setting the PostPage to the delegate, but I don't see how I could do that with multiple UITextView
Off course you can set the delegate for both to PostPage and to know which 1 is intact use
func textViewDidBeginEditing(_ textView: UITextView) {
if textView == eventDescription {
}
else { // eventTitle
}
}
I'm doing a test of a custom keyboard. This is what I need:
It has to have two UITextFields. Cannot be labels.
The keyboard is an embedded UIView.
The default keyboard should be disabled.
It cannot be a keyboard extension.
Not sure why the app is crashing. PS: Not all the keys are on the code yet. Here is an image of what I'm trying to do and the two View Controllers.
Edit: The error is: Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
First ViewController:
import UIKit
class HomeVC: UIViewController, ButtonTapDelegate {
#IBOutlet var textField1: UITextField!
#IBOutlet var textField2: UITextField!
#IBOutlet var keyboardView: UIView!
var buttonPressed = [String]()
override func viewDidLoad() {
addKeyboard(view: keyboardView)
buttonPressed = [String]()
textField1.inputView = UIView()
textField2.inputView = UIView()
}
func addKeyboard(view: UIView) {
let keyboard = KeyboardVC(nibName: "KeyboardVC", bundle: nil)
view.addSubview(keyboard.view)
addChild(keyboard)
}
func didTapButton(sender: UIButton) {
if sender.tag == 5 {
textField1.text?.append(contentsOf: " ")
} else if sender.tag == 6 {
textField1.text?.removeAll()
buttonPressed = [String]()
} else {
let val = sender.titleLabel?.text
textField1.text?.append(contentsOf: val!)
}
self.textField1.text = buttonPressed.joined(separator: "")
}
}
Here is the second View Controller:
import UIKit
protocol ButtonTapDelegate {
func didTapButton(sender: UIButton)
}
class KeyboardVC: UIViewController {
var delegate: ButtonTapDelegate!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttons(_ sender: UIButton) {
delegate.didTapButton(sender: sender)
print(sender)
}
}
var delegate: ButtonTapDelegate!
An implicitly unwrapped optional is essentially a promise that you're definitely going to give the variable a value before you try to access it. The problem in this case is that you haven't done that. Most likely, you want to do this in your first view controller:
func addKeyboard(view: UIView) {
let keyboard = KeyboardVC(nibName: "KeyboardVC", bundle: nil)
keyboard.delegate = self // Now "delegate" will have a value before the function gets called
view.addSubview(keyboard.view)
addChild(keyboard)
}
I'm trying to make a custom keyboard. But I cannot input info on the active textField. I'm not sure what I'm doing wrong.
PS: The keyboard is in another ViewController and is passing the sender.tag well.
Here is my code:
import UIKit
class HomeVC: UIViewController, ButtonTapDelegate, UITextFieldDelegate {
#IBOutlet var textField1: UITextField!
#IBOutlet var textField2: UITextField!
#IBOutlet var keyboardView: UIView!
var activeField: UITextField?
var delegate: ButtonTapDelegate!
override func viewDidLoad() {
addKeyboard(view: keyboardView)
textField1.inputView = UIView()
textField2.inputView = UIView()
textField1.becomeFirstResponder()
activeField?.delegate = self
}
func textFieldDidBeginEditing(_ textField: UITextField) {
activeField = textField
}
func addKeyboard(view: UIView) {
let keyboard = KeyboardVC(nibName: "KeyboardVC", bundle: nil)
keyboard.delegate = self
view.addSubview(keyboard.view)
addChild(keyboard)
}
func didTapButton(sender: UIButton) {
if sender.tag == 8 {
activeField?.text?.append(contentsOf: " ")
} else if sender.tag == 9 {
activeField?.text?.removeAll()
} else {
let val = sender.titleLabel?.text
activeField?.text?.append(contentsOf: val!)
}
}
}
There is a slight problem in your code that is causing your issue. In the comments, you mentioned that didTapButton() is called beforetextFieldDidBeginEditing. This means that actionField is not assigned a value and therefore is nil. Your code in didTapButton() safely unwraps the optional value so that no error is produced, but of course, you cannot append text to a non-existent UITextField.
I was able to fix it after some research with changes to the textFieldDidBeginEditing and didTapButton functions: Here is the full code if anybody wants to choose one textField at a time with a custom keyboard:
import UIKit
class HomeVC: UIViewController, ButtonTapDelegate, UITextFieldDelegate {
#IBOutlet var textField1: UITextField!
#IBOutlet var textField2: UITextField!
#IBOutlet var keyboardView: UIView!
var activeField: UITextField?
var delegate: ButtonTapDelegate!
override func viewDidLoad() {
addKeyboard(view: keyboardView)
textField1.inputView = UIView()
textField2.inputView = UIView()
textField1.becomeFirstResponder()
activeField?.delegate = self
}
func textFieldDidBeginEditing(_ textField: UITextField) {
self.activeField = textField
}
func addKeyboard(view: UIView) {
let keyboard = KeyboardVC(nibName: "KeyboardVC", bundle: nil)
keyboard.delegate = self
view.addSubview(keyboard.view)
addChild(keyboard)
}
func didTapButton(sender: UIButton) {
if textField1 == self.activeField {
if sender.tag == 8 {
textField1.text?.append(contentsOf: " ")
} else if sender.tag == 9 {
textField1.text?.removeAll()
} else {
let val = sender.titleLabel?.text?
textField1.text?.append(contentsOf: val!)
}
return;
}
if textField2 == self.activeField {
if sender.tag == 8 {
textField2.text?.append(contentsOf: " ")
} else if sender.tag == 9 {
textField2.text?.removeAll()
} else {
let val = sender.titleLabel?.text?
textField2.text?.append(contentsOf: val!)
}
return;
}
}
}
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.
}
I have a button that is disabled in my view controller. I have IBActions for when two text fields are edited. I am trying to enable the button when two text fields both contain integers. I have tried to do this, but whenever I run the ios simulator, the button stays disabled even when I put integers into each text field. Why is it staying disabled? I am new to swift, so please help me out. Here is the code for my entire project:
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var calculatorButton: UIButton!
#IBOutlet weak var inspirationLabel: UILabel!
#IBOutlet weak var beginningLabel: UILabel!
#IBOutlet weak var calculatorContainer: UIView!
#IBOutlet weak var answer1Label: UILabel!
#IBOutlet weak var doneButton: UIButton!
#IBOutlet weak var yourWeightTextField: UITextField!
#IBOutlet weak var calorieNumberTextField: UITextField!
#IBOutlet weak var menuExampleButton: UIButton!
#IBOutlet weak var aboutButton: UIButton!
#IBOutlet weak var calculateButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib
yourWeightTextField.delegate = self
calorieNumberTextField.delegate = self
calculateButton.enabled = false
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func calculatorButtonTapped(sender: AnyObject) {
calculatorContainer.hidden = false
inspirationLabel.hidden = true
beginningLabel.hidden = true
menuExampleButton.hidden = true
aboutButton.hidden = true
}
#IBAction func yourWeightEditingDidEnd(sender: AnyObject) {
yourWeightTextField.resignFirstResponder()
}
#IBAction func calorieNumberEditingDidEnd(sender: AnyObject) {
calorieNumberTextField.resignFirstResponder()
}
var yourWeightFilled = false
var calorieNumberFilled = false
func yourWeightTextFieldValueValidInt(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
// Find out what the text field will be after adding the current edit
let text = (yourWeightTextField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
if let intVal = text.toInt() {
self.yourWeightFilled = true
} else {
self.yourWeightFilled = false
}
return true
}
func calorieNumberTextFieldValueValidInt(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
// Find out what the text field will be after adding the current edit
let text = (calorieNumberTextField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
if let intVal = text.toInt() {
self.calorieNumberFilled = true
} else {
self.calorieNumberFilled = false
}
return true
}
#IBAction func yourWeightTextFieldEdited(sender: AnyObject) {
if self.yourWeightFilled && self.calorieNumberFilled {
self.calculateButton.enabled = true
}
}
#IBAction func calorieNumberTextFieldEdited(sender: AnyObject) {
if self.yourWeightFilled && self.calorieNumberFilled {
self.calculateButton.enabled = true
}
}
}
Your delegate methods are a bit mixed up -- they have to be named exactly what the caller expects, or they won't be found, so yourWeightTextFieldValueValidInt() and calorieNumberTextFieldValueValidInt() aren't being called at all. Instead you'll need to handle the edits to both text fields in a single method:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
// Find out what the text field will be after adding the current edit
let text = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
if textField == yourWeightTextField {
yourWeightFilled = text.toInt() != nil
} else if textField == calorieNumberTextField {
calorieNumberFilled = text.toInt() != nil
}
return true
}