I have implemented multiple TextFields in a static TableViewController like this:
import UIKit
class GameViewController: UITableViewController, UITextFieldDelegate {
#IBOutlet weak var inputField11:UITextField?
#IBOutlet weak var inputField12:UITextField?
...
override func viewDidLoad() {
super.viewDidLoad()
inputField11!.delegate = self
inputField12!.delegate = self
...
inputField11?.becomeFirstResponder()
}
}
I can't seem to find a way to implement a way to change the active TextField with an IBAction.
I have an inputFieldDidChange IBAction method implemented that works fine, triggered by "Editing Changed" in the storyboard:
#IBAction func inputFieldDidChange(inputField: UITextField?) {
switch inputField {
case inputField11:
inputField12?.becomeFirstResponder()
case inputField12:
inputField13?.becomeFirstResponder()
...
}
But for some reason the same implementation method doesn't work for my inputFieldSelected method, triggered by "Touch Down":
#IBAction func inputFieldSelected(inputField: UITextField?) {
switch inputField {
case inputField11:
inputField11?.becomeFirstResponder()
case inputField12:
inputField12?.becomeFirstResponder()
...
}
}
I also tried doing this with "Touch Up Inside", "Editing Did Begin" and tap gesture recognizers.
Related
I have an onboarding user flow:
Name -> Age -> Gender
Each of the screens shares the same structure:
Question (top)
Input (middle)
Continue (bottom)
I have a class OnboardingHelper.swift that creates a class to set the question box and continue button:
class UserOnboardingHelper{
var text: String
var questionbox: UIView
var viewController: UIViewController
var continueButton: UIButton
init(text: String, questionbox: UIView, viewController: UIViewController, continueButton: UIButton){
self.text = text
self.questionbox = questionbox
self.viewController = viewController
self.continueButton = continueButton
}
func setQuestionBox(){
//sets question box
}
func setContinueButton(){
//sets continue button
enableContinueButton()
addContinueButtonPath()
}
func enableContinueButton(){
//enables continue button
}
func disableContinueButton(){
//disables continue button
}
func addContinueButtonPath(){
//sets path of continue button based on which view
}
}
In each of the onboarding ViewControllers I am setting the class in ViewDidLoad():
class NamePageViewController: UIViewController, UITextFieldDelagate {
#IBOutlet weak var questionbox: UIView!
#IBOutlet weak var continueButton: UIButton!
#IBOutlet weak var inputLabel: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
let namePageSettings = UserOnboardingHelper(text: "What is your name", questionbox: questionbox, viewController: self, continueButton: continueButton)
namePageSettings.setQuestionBox()
namePageSettings.setContinueButton()
inputLabel.delegate = self
if nameIsFilled {
namePageSettings.enableContinueButton()
} else{
namePageSettings.disableContinueButton()
}
}
}
The issue is that in the ViewController I textFieldDidEndEditing() function which needs to call the namePageSettings class from viewDidLoad()
func textFieldDidEndEditing(_ textField: UITextField){
if (textField.text?.empty)!{
//I want to call disableContinueButton() from UserOnboardingHelper
} else {
//I want to enable enableContinueButton() from UserOnboardingHelper
}
}
Trying to understand if:
The overall approach is correct and if not, what's the best way
If the above approach is in the right direction, how should disableContinueButton() and enableContinueButton() be called?
Thanks in advance! Sorry if the approach is really dumb - I'm still trying to wrap my head around classes.
You can have the view controller have a weak reference to the onboarding helper, so you can still call helper methods without creating a retain cycle.
In NamePageViewController, add a property:
weak var userOnboardingHelper: UserOnboardingHelper?
Then, in UserOnboardingHelper's initializer, add:
self.viewController.userOnboardingHelper = self
You can now call the onboarding helper's methods in the view controller:
userOnboardingHelper.disableContinueButton()
userOnboardingHelper.enableContinueButton()
I have ButtonClass which has buttonTapped method implemented.
The button itself is on the view. I need to add an extra layer when the button is pressed but I don't have access to self in the ButtonClass because self is a button not the view... Basically I need to access self.view found in the viewController but that's another class. If I create an new instance like so viewController() it won't help because it wont' be the same instance.
If you're adding the buttonTapped method from the custom button class then you need to create a delegate, and call the delegate method when the user the buttonTapped method is called.
In your custom button class
protocol YourCustomButtonDelegate {
func changeSomethingInTheView()
}
class YourCustomButtonClass {
var delegate: YourCustomButtonDelegate?
func buttonTapped(_ sender: AnyObject) {
if let delegate = delegate {
delegate.changeSomethingInTheView()
}
}
}
And in the ViewController
class ViewController: UIViewController, CustomCellDelegate {
#IBOutlet weak var button: YourCustomButton!
override func viewDidLoad() {
super.viewDidLoad()
button.delegate = self
}
func changeSomethingInTheView() {
// write your code here...
}
}
This question already has answers here:
What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean?
(16 answers)
Closed 6 years ago.
I'm having a problem, I have a UIButton in a UIViewController class and I want to enable that button after an animation that happens in a UIView class that is in another file.
class MainViewController: UIViewController {
#IBOutlet weak var nextButton: UIButton!
#IBAction func nextButtonPressed(sender: UIButton) {
nextButton.enable = false
}
}
When I try to call the nextButton from the viewController class after the animation is done I get this error:
EXC_BAD_INSTRUCTION(code = EXC_I386_INVOP, subcode = 0x0)
I get the error on the line where I set the nextButton enable to true.
class CustomView: UIView {
var vc = MainViewController()
func animationEnded() {
vc.nextButton = true
}
}
I don't have a clue what I'm missing and I would appreciate some help. Thanks
You encounter an error because in your CustomView you create a new MainViewController, you're not using the one initialized from the storyboard. That new MainViewController doesn't have any of its properties initialized, so nextButton is nil, hence the crash when you try to access it.
What you want to do is notify your controller from the view that the animation has ended so that the controller can update the button (since the controller owns the button). The standard way to do this in Cocoa is to use the delegate pattern like so:
class MainViewController: UIViewController, CustomViewDelegate
{
#IBOutlet weak var nextButton: UIButton!
#IBOutlet weak var customView: CustomView!
#IBAction func nextButtonPressed(sender: UIButton) {
self.nextButton.enabled = false
}
override func awakeFromNib() {
super.awakeFromNib()
self.customView.delegate = self
}
func customViewAnimationDidEnd(customView: CustomView) {
self.nextButton.enabled = true
}
}
protocol CustomViewDelegate : class
{
func customViewAnimationDidEnd(customView: CustomView)
}
class CustomView: UIView
{
weak var delegate: CustomViewDelegate? = nil
func animationEnded() {
self.delegate?.customViewAnimationDidEnd(self)
}
}
In this implementation the controller is the view delegate and gets notified when interesting events happen in the view (like a particular animation ending).
Make a delegate in your UIView that tells when it should hapen
protocol CustomViewDelegate {
func pushThatButton()
}
in CustomView class put this:
weak var delegate: CustomViewDelegate?
then
func animationEnded() {
delegate.pushThatButton()
}
and in UIViewController
class MainViewController: UIViewController, CustomViewDelegate {
and implement delegate ofc
func pushThatButton()
nextButton.sendActionsForControlEvents(.TouchUpInside)
}
almost forget, do an outlet to your view in viewController and setup delegate! in viewDidLoad() or when you will find this best
customViewOutlet.delegate = self
I created a custom input view with a couple of UIButtons inside it. All these buttons invoke one single IBAction.
The IBAction is not called although I'am 100% sure I set them properly in IB. To prove that this isn't causing the problem I added a target to one of the buttons programmatically, but the action still won't get invoked.
import UIKit
protocol CashDeskInputDelegate: class {
func didReceiveInput(inputValue: String)
func didReceiveDoneMessage()
}
class CashDeskInputViewController: UIInputViewController {
// Delegate
weak var delegate: CashDeskInputDelegate?
// Actions
#IBAction func buttonTapped(sender: UIButton) {
print("buttonTapped")
guard let valueOfSender = sender.titleLabel?.text else {
dismissKeyboard()
return
}
valueOfSender == "Done" ? delegate?.didReceiveDoneMessage() : delegate?.didReceiveInput(valueOfSender)
}
// Test outlet
#IBOutlet weak var firstButton: UIButton!
override func viewDidLoad() {
// Add an action to the first button for testing purposes.
firstButton.addTarget(self, action: "buttonTapped:", forControlEvents: .TouchUpInside)
}
}
I didn't implement the delegate properly yet, but that not the the point. The print("buttonTapped") won't even show up. The method just doesn't get called, I tested this using breakpoints.
Does anyone know what is causing this problem
In XCode 6.3.2, I have a UITextField:
#IBOutlet weak var uiswitchControlledTextField: UITextField!
I am now using a UISwitch (named mySwitch) to control its enabled or disabled state in the following way:
#IBOutlet weak var mySwitch: UISwitch!
mySwitch.addTarget(self, action: Selector("stateChanged:"), forControlEvents: UIControlEvents.ValueChanged)
//callback below:
func stateChanged(switchState: UISwitch) {
uiswitchControlledTextField.enabled = switchState.on
}
The above works well, however, I am looking to try if it would be possible to create a UITextFieldDelegate to control the above UITextField in the same way. So far, I have the following by implementing textFieldShouldBeginEditing, in which I wish to return false to disable the UITextField, but I don't know how to let the UISwitch dynamically return true or false from textFieldShouldBeginEditing
import Foundation
import UIKit
class SwitchControlledTextFieldDelegate: NSObject, UITextFieldDelegate {
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
return false; //do not show keyboard or cursor
}
}
In ViewController, I try to set
self.uiswitchControlledTextField.delegate = SwitchControlledTextFieldDelegate()
but it does not work as I wished. Any help would be appreciated.
self.uiswitchControlledTextField.delegate = SwitchControlledTextFieldDelegate()
The problem is that that line merely creates an instance of your SwitchControlledTextFieldDelegate class, which then immediately goes right back out of existence.
You need to use, as your text field delegate, some instance which already exists and which will persist - like, perhaps, your view controller!
(Xcode 7)
Use this:
override func viewDidLoad() {
super.viewDidLoad()
// Setting the delegate
self.textField3.delegate = self
self.editingSwitch.setOn(false, animated: false)
}
// Text Field Delegate Methods
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
return self.editingSwitch.on
}
#IBAction func toggleTheTextEditor(sender: AnyObject) {
if !(sender as! UISwitch).on {
self.textField3.resignFirstResponder()
}
}