How do I add a placeholder to my UITextField? (Using Xcode 12) - swift

EDIT: New ErrorCurrently using XCode 12, and I'm trying to add a placeholder. I followed the Swift QuestionBot documentation but it doesn't work (I'm assuming it's because my XCode is much newer). Anyway, appreciate all the help!
EDIT: I added an image of a new error I got.
EDIT 2: Added MyQuestionAnswerer() struct! It's on a different view controller (obvious).
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var responseLabel: UILabel!
#IBOutlet weak var askButton: UIButton!
#IBOutlet weak var questionField: UITextField!
let questionAnswerer = MyQuestionAnswerer()
override func viewDidLoad() {
super.viewDidLoad()
questionField.becomeFirstResponder()
}
func respondToQuestion(_ question: String) {
let answer = questionAnswerer.responseTo(question: question)
displayAnswerTextOnScreen(answer)
questionField.placeholder = "Ask another question..."
questionField.text = nil
askButton.isEnabled = false
}
#IBAction func askButtonTapped(_ sender: AnyObject) {
guard questionField.text != nil else {
return
}
questionField.resignFirstResponder()
}
func displayAnswerTextOnScreen(_ answer: String) {
responseLabel.text = answer
}
}
extension ViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return false
}
func textFieldDidEndEditing(_ textField: UITextField) {
guard let text = textField.text else {
return
}
respondToQuestion(text)
}
#IBAction func editingChanged(_ textField: UITextField) {
guard let text = textField.text else {
askButton.isEnabled = false
return
}
askButton.isEnabled = !text.isEmpty
}
}
struct MyQuestionAnswerer {
func responseTo(question: String) -> String {
let loweredQuestion = question.lowercased()
if loweredQuestion == "What is the current vaccination rate of the Philippines?" {
return "As of August 8, the vaccination rate of the Philippines is 10%!"
} else if loweredQuestion.hasPrefix("Where") {
return "Check the map for nearby vaccination centers."
}
}

The placeholder is not present when the textFiled as it is not set until the respondToQuestion method is called. It should probably be set inside of a view controller life cycle method such as viewDidLoad().
Example:
override func viewDidLoad() {
super.viewDidLoad()
questionField.placeholder = "Ask another question"
questionField.becomeFirstResponder()
}

Related

Two windows are presented when I start my xib only project

I am using Xcode 10.1 two create a simple single window application. The issue is that I see two windows instantiated when I run it instead of the single one I expect. Here is my code
//AppDelegate
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var mainWindowController: WindowController!
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
print("loading")
let mainWindowController = WindowController()
mainWindowController.showWindow(self)
self.mainWindowController = mainWindowController
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
print("closed")
}
}
//WindowController.swift
import Cocoa
class WindowController: NSWindowController, NSSpeechSynthesizerDelegate {
#IBOutlet weak var textField: NSTextField!
#IBOutlet weak var speakButton: NSButton!
#IBOutlet weak var stopButton: NSButton!
override var windowNibName: String {
return "Window"
}
let speechSynth = NSSpeechSynthesizer()
var isStarted: Bool = false {
didSet {
updateButtons()
}
}
override func windowDidLoad() {
super.windowDidLoad()
viewDidLoad()
}
func viewDidLoad() {
// super.viewDidLoad()
updateButtons()
speechSynth.delegate = self
print("view load")
// Do any additional setup after loading the view.
}
#IBAction func speakIt(sender: NSButton) {
let string = textField.stringValue
if string.isEmpty {
print("String is empty")
}
else {
print(string)
speechSynth.startSpeaking(string)
isStarted = true
}
}
#IBAction func stopIt(sender: NSButton) {
speechSynth.stopSpeaking()
isStarted = false
}
func updateButtons() {
if isStarted {
speakButton.isEnabled = false
stopButton.isEnabled = true
} else {
stopButton.isEnabled = false
speakButton.isEnabled = true
}
}
func speechSynthesizer(_ sender: NSSpeechSynthesizer, didFinishSpeaking finishedSpeaking: Bool) {
isStarted = false
}
}
My Xib has the option checked "Visible at launch". If I uncheck it, I see no window.
Basically, I am attempting a program from the book "Cocoa Programming for OSX". However, the book is obsolete. But I am trying to find my way through new Xcode and Swift. Any help?
Also a point worth noting is that only one window is functional, the second window IBOutlets and IBAction are not connected. So nothing happens in the second window.
I can't figure out why is this happening?

How to use file.swift into a swift created app(project)?

I have a task to create an app that checks if a String is a palindrome or not. And also I have instructed to create a file and in that file should be a function. But what I can't figure out is how to make a button run that function that is in the file.
func isPalidrone(phrase: String) -> Bool {
let isPalidrone = true
let reversed = String(phrase.reversed())
if reversed == phrase {
return isPalidrone
} else {
return !isPalidrone
}
}
class ViewController: UIViewController {
#IBOutlet weak var Label: UILabel!
#IBOutlet weak var textFieldOutlet: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttonPressed(_ sender: Any) {
}
}
Like this:
#IBAction func buttonPressed(_ sender: Any) {
let pal = isPalidrone(phrase: self.textFieldOutlet.text!)
// now pal is true or false and you can decide what else to do ...
// for example:
self.Label.text = pal ? "It is a palindrome" : "It is not a palindrome"
}

Material Textfield crash on setting error text

I am using the CosmicMind library for Material design and I am trying to set up basic textfields with some error checking but the documentation is not very good.
I have set up my text fields like the following:
#IBOutlet weak var userNameField: ErrorTextField!
userNameField.placeholder = "Enter Username"
userNameField.delegate = self
userNameField.error = "Text is too long" // App Crashes here
userNameField.errorColor = Color.red.base
App crashes with EXC_BAD_ACCESS
I validate my field like this:
func textField(textField: TextField, didChange text: String?) {
if textField == userNameField {
if validateUsername(text: textField.text!) {
userNameField.isErrorRevealed = true
} else {
userNameField.isErrorRevealed = false
}
}
}
Even if I remove that line, app crashes on userNameField.isErrorRevealed = true too.
I have created following code with same library which are you using, which are working fine. I have create textField programatically.
import UIKit
import Material
class ViewController: UIViewController {
fileprivate var emailField: ErrorTextField!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = Color.grey.lighten5
emailField = ErrorTextField()
emailField.placeholder = "Email"
emailField.error = "Text is too long"
emailField.delegate = self
self.view.layout(emailField).height(40).width(200).centerVertically().centerHorizontally()
}
}
extension ViewController: TextFieldDelegate {
public func textFieldDidEndEditing(_ textField: UITextField) {
(textField as? ErrorTextField)?.isErrorRevealed = false
}
public func textFieldShouldClear(_ textField: UITextField) -> Bool {
(textField as? ErrorTextField)?.isErrorRevealed = false
return true
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
(textField as? ErrorTextField)?.isErrorRevealed = true
return true
}
}
See this sample project: Sample Project
I hope this will help you.

How can I append text to the active UITextField - Swift

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

How do I dismiss keyboard with a touch? touchesBegan not workig

According to everything I've read here, I should override touchesBegan() to dismiss the keyboard (in my case DatePicker). Unfortunately touching the screen does not dismiss the DatePicker. Touching other UI elements like the UISteppers dismisses the keyboard just fine, and it is using the same function of CloseKeyboard()
class RecordWorkoutTableViewController: UITableViewController, UITextFieldDelegate {
#IBOutlet weak var dateTextField: UITextField!
#IBOutlet weak var weightLabel: UILabel!
#IBOutlet weak var setOneLabel: UILabel!
#IBOutlet weak var setTwoLabel: UILabel!
#IBOutlet weak var weightStepper: UIStepper!
#IBOutlet weak var setOneStepper: UIStepper!
#IBOutlet weak var setTwoStepper: UIStepper!
var newDate: NSDate? {
didSet {
dateTextField.text = NSDateToPrettyString(newDate!)
}
}
var newWeight: Int? {
didSet {
weightLabel.text = "\(newWeight!) lbs"
}
}
var newSetOne: Int? {
didSet {
setOneLabel.text = "Set 1: \(newSetOne!) reps"
}
}
var newSetTwo: Int? {
didSet {
setTwoLabel.text = "Set 2: \(newSetTwo!) reps"
}
}
var workout: Workout?
// MARK: UITextFieldDelegate
func textFieldDidBeginEditing(textField: UITextField) {
let datePicker = UIDatePicker()
textField.inputView = datePicker
datePicker.addTarget(self, action: #selector(RecordWorkoutTableViewController.datePickerChanged(_:)), forControlEvents: .ValueChanged)
}
func datePickerChanged(sender: UIDatePicker) {
newDate = sender.date
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
// disable editing of date text. datepicker input only
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
return false
}
// MARK: Helper Functions
func closeKeyboard() {
self.view.endEditing(true)
}
// MARK: Touch Events
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
closeKeyboard()
}
override func viewDidLoad() {
super.viewDidLoad()
dateTextField.delegate = self
newDate = NSDate()
if let lastWorkout = workout {
newWeight = lastWorkout.sets[0].weight
newSetOne = lastWorkout.sets[0].repCount
newSetTwo = lastWorkout.sets[1].repCount
} else {
newWeight = 0
newSetOne = 9
newSetTwo = 8
}
weightStepper.stepValue = 5
weightStepper.maximumValue = 995
weightStepper.value = Double(newWeight!)
setOneStepper.stepValue = 1
setOneStepper.maximumValue = 20
setOneStepper.value = Double(newSetOne!)
setTwoStepper.stepValue = 1
setTwoStepper.maximumValue = 20
setTwoStepper.value = Double(newSetTwo!)
}
#IBAction func weightStepperChanged(sender: UIStepper) {
newWeight = Int(sender.value)
closeKeyboard()
}
#IBAction func setOneStepperChanged(sender: UIStepper) {
newSetOne = Int(sender.value)
closeKeyboard()
}
#IBAction func setTwoStepperChanged(sender: UIStepper) {
newSetTwo = Int(sender.value)
closeKeyboard()
}
}
You said in the comments that you have another class in your app named Set. Since this class is in the same module as your table view controller, Swift is prioritizing it over it's built in class.
You can fix this by either renaming your Set class, or explicitly specifying the Swift module in the function declaration:
override func touchesBegan(touches: Swift.Set<UITouch>, withEvent event: UIEvent?) {
closeKeyboard()
}