How can I append text to the active UITextField - Swift - 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;
}
}
}

Related

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

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()
}

Force unwrapping nil optional for UIImageView when transitioning to view controller

I'm running into an error when transitioning to view controllers by overriding the built-in prepare() function in Swift. I have a UIImageView for backgrounds on my screens. Here is the code for two of the view controllers in question.
import UIKit
import FirebaseAuth
class HomeVC: UIViewController {
#IBOutlet weak var signOutButton: UIButton!
#IBOutlet weak var backgroundImageView: UIImageView!
#IBOutlet weak var friendsNavButton: UIButton!
#IBOutlet weak var homeNavButton: UIButton!
#IBOutlet weak var profileNavButton: UIButton!
#IBOutlet weak var bumpButton: UIButton!
#IBOutlet weak var welcomeLabel: UILabel!
#IBOutlet weak var doNotDisturbLabel: UILabel!
#IBOutlet weak var doNotDisturbButton: UIButton!
var userName = ""
var dndIsOn: Bool = false
#IBAction func dndToggled(_ sender: Any) {
dndIsOn = !dndIsOn
User.current.available = !dndIsOn
FirestoreService.db.collection(Constants.Firestore.Collections.users).document(User.current.uid).updateData([Constants.Firestore.Keys.available : !dndIsOn])
if dndIsOn {
print("DND is on!")
setupDNDUI()
} else if !dndIsOn {
print("DND is off!")
setupActiveUI()
}
}
#IBAction func signOutTapped(_ sender: Any) {
let firAuth = Auth.auth()
do {
try firAuth.signOut()
} catch let signOutError as NSError {
print ("Error signing out: %#", signOutError)
}
print("Successfully signed out")
}
#IBAction func bumpTapped(_ sender: Any) {
self.performSegue(withIdentifier: Constants.Segues.toCall, sender: self)
}
#IBAction func friendsNavTapped(_ sender: Any) {
self.performSegue(withIdentifier: Constants.Segues.toFriends, sender: self)
}
#IBAction func profileNavTapped(_ sender: Any) {
let nav = self.navigationController //grab an instance of the current navigationController
DispatchQueue.main.async { //make sure all UI updates are on the main thread.
nav?.view.layer.add(CATransition().segueFromLeft(), forKey: nil)
nav?.pushViewController(ProfileVC(), animated: false)
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.setNavigationBarHidden(true, animated: true)
self.backgroundImageView.contentMode = UIView.ContentMode.scaleAspectFill
doNotDisturbLabel.isHidden = true
if !userName.isEmpty {
welcomeLabel.text = "Welcome Back, " + userName + "!"
} else {
welcomeLabel.text = ""
}
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .darkContent
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let friendsVC = segue.destination as? FriendsVC else {
return
}
FirestoreService.db.collection(Constants.Firestore.Collections.users).document(User.current.uid).getDocument { (snapshot, err) in
if let err = err {
print(err.localizedDescription)
} else {
let data = snapshot!.data()!
let requests = data[Constants.Firestore.Keys.requests] as? [String]
if let requests = requests {
friendsVC.requests = requests
}
}
}
}
class FriendsVC: UIViewController {
//var friends: [Friend] = User.current.friends
var friends: [User] = []
var requests: [String]?
#IBOutlet weak var requestsNumberLabel: UILabel!
#IBOutlet weak var backgroundImageView: UIImageView!
#IBOutlet weak var friendRequestsButton: UIButton!
#IBOutlet weak var homeNavButton: UIButton!
#IBOutlet weak var friendsTitle: UILabel!
#IBOutlet weak var friendTableView: UITableView!
#IBOutlet weak var addFriendButton: UIButton!
#IBOutlet weak var tableViewTopConstraint: NSLayoutConstraint!
#IBAction func friendRequestsTapped(_ sender: Any) {
self.performSegue(withIdentifier: Constants.Segues.toRequests, sender: self)
}
#IBAction func homeNavTapped(_ sender: Any) {
let nav = self.navigationController //grab an instance of the current navigationController
DispatchQueue.main.async { //make sure all UI updates are on the main thread.
nav?.view.layer.add(CATransition().segueFromLeft(), forKey: nil)
nav?.pushViewController(HomeVC(), animated: false)
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.setNavigationBarHidden(true, animated: true)
backgroundImageView.contentMode = UIView.ContentMode.scaleAspectFill
friendTableView.backgroundView?.backgroundColor = .white
friendsTitle.isHidden = false
UserService.getUserArray(uids: User.current.friendUids, completion: { (users) in
guard let users = users else {
print("User has no friends")
return
}
self.friends = users
self.friendTableView.reloadData()
})
guard let requests = self.requests else {
friendRequestsButton.isHidden = true
requestsNumberLabel.isHidden = true
self.tableViewTopConstraint.constant = 0
return
}
requestsNumberLabel.text = requests.count.description
// Do any additional setup after loading the view.
friendTableView.delegate = self
friendTableView.dataSource = self
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let homeVC = segue.destination as? HomeVC {
homeVC.userName = User.current.firstName
} else if let requestsVC = segue.destination as? RequestsVC {
UserService.getUserArray(uids: self.requests!) { (requesters) in
if let requesters = requesters {
requestsVC.requesters = requesters
}
}
}
}
}
When my app loads into the home screen, there is no problem, and when a button is tapped to transition to FriendsVC, there is no problem. However, when I try to initiate the transition from HomeVC to ProfileVC or from FriendVC to HomeVC, I get the error: "Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value" at the self.backgroundImageView.contentMode = UIView.ContentMode.scaleAspectFill lines in my viewDidLoad methods. These segues have something in common in that these are the ones where I override the prepare() function, but I'm not sure what I'm doing wrong

Custom UITextfield with discontinuous bottom borders

I am now working on a UITextfield. I hope to know how to add four discontinuous bottom border to a UITextfield, and how to make the space between input digits larger to make them fit exactly on the four lines respectively. Moreover, if possible, how to make the line become black (while other lines remain grey) when users are inputing digit on that line? Thank you so much!
Use following subclass of UITextField and create textfield for each digit either in storyboard or programatically.
Note that each textfield has to set a tag, such as
1st Digit: textField1.tag=1
2nd Digit: textField1.tag=2
3rd Digit: textField1.tag=3
4th Digit: textField1.tag=4
class CustomTextField: UITextField {
private let normalStateColor = UIColor.lightGray.cgColor
private let focusStateColor = UIColor.black.cgColor
private let border = CALayer()
private let borderHeight: CGFloat = 4.0
// MARK:- Init
required init?(coder aDecoder: NSCoder) {
super.init(coder:aDecoder)
setup()
}
override init(frame:CGRect) {
super.init(frame:frame)
setup()
}
override func awakeFromNib() {
super.awakeFromNib()
setup()
}
// MARK:- Overrides
override func layoutSubviews() {
super.layoutSubviews()
let size = self.frame.size
self.border.frame = CGRect(x: 0, y: size.height - borderHeight, width: size.width, height: borderHeight)
}
override func willMove(toSuperview newSuperview: UIView!) {
guard newSuperview != nil else {
NotificationCenter.default.removeObserver(self)
return
}
NotificationCenter.default.addObserver(self, selector: #selector(beginEdit),
name: UITextField.textDidBeginEditingNotification, object: self)
NotificationCenter.default.addObserver(self, selector: #selector(endEdit),
name: UITextField.textDidEndEditingNotification, object: self)
}
#objc func beginEdit() {
border.backgroundColor = self.focusStateColor
}
#objc func endEdit() {
border.backgroundColor = self.normalStateColor
}
private func setup() {
border.backgroundColor = self.normalStateColor
textAlignment = .center
borderStyle = .none
layer.addSublayer(border)
delegate = self
}
}
extension CustomTextField: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField.text!.count < 1 && string.count > 0 {
textField.text = string
textField.superview?.viewWithTag(textField.tag + 1)?.becomeFirstResponder()
return false
} else if textField.text!.count >= 1 && string.count == 0 {
textField.text = ""
textField.superview?.viewWithTag(textField.tag - 1)?.becomeFirstResponder()
return false
}
return true
}
}
That yields
check this..
ViewController.swift
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var txtOne: UITextField!
#IBOutlet weak var txtTwo: UITextField!
#IBOutlet weak var txtThree: UITextField!
#IBOutlet weak var txtFour: UITextField!
#IBOutlet weak var vwFour: UIView!
#IBOutlet weak var vwThree: UIView!
#IBOutlet weak var vwTwo: UIView!
#IBOutlet weak var vwOne: UIView!
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == txtOne {
vwOne.backgroundColor = .black
vwTwo.backgroundColor = .lightGray
vwThree.backgroundColor = .lightGray
vwFour.backgroundColor = .lightGray
} else if textField == txtTwo {
vwTwo.backgroundColor = .black
vwOne.backgroundColor = .lightGray
vwThree.backgroundColor = .lightGray
vwFour.backgroundColor = .lightGray
} else if textField == txtThree {
vwThree.backgroundColor = .black
vwTwo.backgroundColor = .lightGray
vwOne.backgroundColor = .lightGray
vwFour.backgroundColor = .lightGray
} else {
vwFour.backgroundColor = .black
vwTwo.backgroundColor = .lightGray
vwThree.backgroundColor = .lightGray
vwOne.backgroundColor = .lightGray
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
}

Custom keyboard is crashing the app - Swift

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

the button is disabled when text view is empty else is enabled

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)