I want to delegate some tasks to the AcceptController, but my delegate property inside of 'SendController' always returns nil, so no delegation will ever be executed. I just can't figure out why my delegate property 'übergabeDelegate' always returns nil.
protocol ÜbergabeDelegate {
func übergebeText(text: String)
}
class SendController: UIViewController {
#IBOutlet weak var textField: UITextField!
var übergabeDelegate: ÜbergabeDelegate?
#IBAction func save(_ sender: UIButton) {
if let text = textField.text {
if übergabeDelegate != nil {
übergabeDelegate!.übergebeText(text: text)
} else {
print("\nübergabeDelegate is nil\n")
}
}
}
}
class AcceptController: UIViewController {
#IBOutlet weak var label: UILabel!
let sendController = SendController()
override func viewDidLoad() {
super.viewDidLoad()
sendController.übergabeDelegate = self
//print("Delegate gesetzt")
}
}
extension AcceptController: ÜbergabeDelegate {
func übergebeText(text: String) {
label.text = "\(text)"
}
}
I expect the label to present the input I gave on my SendController but the text of the label never actually changes.
inside viewDidLoad let sendController = SendController() creates a local variable. the sendController will then be released after viewDidLoad returns. set the sendController as a property of the AcceptController and the delegate will persist when you assign it because the SendController object will not be released after viewDidLoad returns.
Related
How do I find out if the keyboard is of type numeric, Twitter, email, etc...?
edit: Is there a way to detect keyboard type without using an outlet?
Consider that you have tow textFields in the ViewController, You will need to implement textFieldShouldBeginEditing method from UITextFieldDelegate protocol, as follows:
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var tfEmail: UITextField!
#IBOutlet weak var tfPassword: UITextField!
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField.keyboardType == .emailAddress {
// this is the tfEmail!
}
if textField.isSecureTextEntry {
// this is tfPassword!
}
}
}
Make sure their delegates are connected to the ViewController, programmatically:
tfEmail.delegate = self
tfPassword.delegate = self
or from the Interface Builder.
Note that you can recognize the keyboard type for the current textField by checking its keyboardType property, which is an instance of UIKeyboardType enum:
The type of keyboard to display for a given text-based view. Used with
the keyboardType property.
What about UITextView?
The same exact functionality should be applied when working with UITextViews, but you need to implement textViewDidBeginEditing(_:) method from UITextViewDelegate protocol instead of implementing textFieldShouldBeginEditing. Again, make sure the delegate of the textView is connected to the ViewController.
Also,
If your main purpose of checking the keyboard type is just for recognizing what is the current responded textField/textView, I suggest to do a direct check:
class ViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate {
#IBOutlet weak var tfEmail: UITextField!
#IBOutlet weak var tfPassword: UITextField!
#IBOutlet weak var textViewDescription: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
tfEmail.delegate = self
tfPassword.delegate = self
textViewDescription.delegate = self
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField === tfEmail {
// this is the tfEmail!
}
if textField === tfPassword {
// this is tfPassword!
}
}
func textViewDidBeginEditing(_ textView: UITextView) {
if textView === textViewDescription {
// this is description textview
}
}
}
For more information about === operator you might want to check this question/answers.
Hope this helped.
In addition to Ahmad F 's great answer, this is my approach of getting the current keyboard type, at any time:
Step 1: Delegate UITextField
class File: UIViewController, UITextFieldDelegate{//...}
Update viewDidLoad() to this:
#IBOutlet weak var normalTextField: UITextField!
#IBOutlet weak var numberTextField: UITextField!
#IBOutlet weak var emailTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
numberTextField.keyboardType = .numberPad
normalTextField.keyboardType = .default
emailTextField.keyboardType = .emailAddress
numberTextField.delegate = self
normalTextField.delegate = self
emailTextField.delegate = self
}
Step 2: Working with UITextField's methods:
Add a variable called keyboardType, as below:
var keyboardType: UIKeyboardType? = nil
Then, change it whenever a new textField begins editing:
func textFieldDidBeginEditing(_ textField: UITextField) {
keyboardType = textField.keyboardType
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
keyboardType = nil
return true
}
Step 3: Create and call a function like below:
func getCurrentKeyboard() -> String{
if keyboardType == nil{
return "no current keyboard"
}
else if keyboardType == .numberPad{
return "number"
}
else if keyboardType == .emailAddress{
return "email"
}
else{
return "default"
}
}
#IBAction func displayCurrentKeyboard(_ sender: UIButton) {
print(self.getCurrentKeyboard())
}
And this outputs: email / number / no current keyboard / default, depending on the case.
If you want to check which type of keyboard it is with if-else statements, you can change your displayCurrentKeyboard() method to this:
#IBAction func displayCurrentKeyboard(_ sender: UIButton) {
let keyboardString = self.getCurrentKeyboard()
if keyboardString == "number"{
//...
}
else if keyboardString == "email"{
//...
}
else{
//...
}
}
And that's it! You can call this wherever you want in your code with this usage:
let keyboardString = self.getCurrentKeyboard()
NOTE: This method also handles the case of no keyboard visible on the screen, returning no current keyboard, in this case.
Let me know if this helps!
I know there are a lot of unexpected nil value questions here, but I've tried other approaches and they're not working for me. The function at the bottom is called from a button on another view controller. Basically the issue is that this code works as expected, the form element has the DatePicker as the input view, and it updates startDatePickerField.text when the value is changed, so I know that startDatePickerField.text is not empty, as it is displayed on the screen. However, when calling the testForValidity() function is called from another view controller containing the one we are working in, I get the "unexpectedly found nil while unwrapping option value error". All of the outlets are connected properly so I know it isn't that.
class popoverTableViewController: UITableViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet var startDatePickerField: UITextField!
override func viewDidLoad() {
let startDatePicker:UIDatePicker = UIDatePicker()
startDatePicker.datePickerMode = UIDatePickerMode.dateAndTime
startDatePickerField.inputView = startDatePicker
startDatePicker.minuteInterval = 5
startDatePicker.addTarget(self, action: #selector(popoverTableViewController.startDatePickerValueChanged(_:)), for: UIControlEvents.valueChanged)
}
#IBAction func startDateDidBegin(_ sender: AnyObject) {
}
func startDatePickerValueChanged(_ sender: UIDatePicker) {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = DateFormatter.Style.long
dateFormatter.timeStyle = DateFormatter.Style.short
startDatePickerField.text = dateFormatter.string(from: sender.date)
finishDateTime = sender.date
}
And the function that errors:
func testForValidity() {
print(startDatePickerField.text)
}
Issue:
You're trying to access IBOutlet property from another class via instance method.
The reason behind the nil value is IBOutlets are initialized when the nib is loaded.
Hence in your case when you are in another view controller you can't access an Outlet variable else you will be always getting nil.
Solution:
If I assume correctly, you want to use a value from a view controller after having a value in it.
The best way is to follow 'Design patterns'. i.e., You have to store your required value in a model class and access the value via model class instance.
Apple has very good documentation about it. Please go through.
https://developer.apple.com/library/ios/referencelibrary/GettingStarted/DevelopiOSAppsSwift/Lesson6.html#//apple_ref/doc/uid/TP40015214-CH20-SW1
Sample Code in Swift:
class TextFieldViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var outputLabel : UILabel!
#IBOutlet weak var textField : UITextField!
override func viewDidLoad() { super.viewDidLoad(); setupTextField() }
func testValidity() -> (textFieldValue: UITextField?, modelValue: String?) {
return (textField, TextFieldModel.sharedModel.textFieldData)
}
//MARK: - Text Field Delegates
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
outputLabel.text = textField.text
TextFieldModel.sharedModel.textFieldData = textField.text
return true
}
}
class TextFieldModel {
static let sharedModel = TextFieldModel()
var textFieldData: String?
}
class DemoController: UIViewController {
#IBOutlet weak var textFieldOutput: UILabel!
#IBOutlet weak var modelOutput: UILabel!
override func viewDidLoad() { super.viewDidLoad(); testValues() }
func testValues() {
let tvc = TextFieldViewController()
textFieldOutput.text = "Value of Text field is " + (tvc.testValidity().textFieldValue?.text ?? "nil")
modelOutput.text = "Value accessed from Model Class is \(tvc.testValidity().modelValue)"
}
}
extension TextFieldViewController {
func setupTextField() {
textField.placeholder = ""
textField.delegate = self
textField.becomeFirstResponder()
textField.keyboardType = UIKeyboardType.Default
textField.returnKeyType = UIReturnKeyType.Done
}
}
Output:
At the top, add UITextFieldDelegate so it becomes:
class popoverTableViewController: UITableViewController, UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate {
Then in viewDidLoad() add this line :
self.startDatePickerField.delegate = self
I want a stepper and label to reset to zero after my variable in another class is also reset. The variables reset but the stepper and label do not even after using a delegate.
View Controller:
class ViewController: UIViewController, CircleViewDelegate {
var colors = CircleView()
#IBOutlet weak var circleView1: CircleView!
#IBOutlet weak var redStepper: UIStepper!
#IBOutlet weak var redValue: UILabel!
#IBAction func stepperChange(sender: UIStepper)
{
circleView1.redd1 = Int(redStepper.value);
redValue.text = Int(sender.value).description;
}
func updateRedStepperValue(value: Double) {
redStepper.value = value
redValue.text = Int(colors.redd1.value).description;
}
override func viewDidLoad() {
super.viewDidLoad()
colors.delegate = self
}
}
CircleView:
protocol CircleViewDelegate
{
func updateRedStepperValue(value: Double)
func updateGreenStepperValue(value: Double)
func updateBlueStepperValue(value: Double)
}
class CircleView: UIView
{
var delegate: CircleViewDelegate?
var redd1 = 0
func updateValues()
{
if(redd1==Int(red1))
{
redd1=0;
delegate?.updateRedStepperValue(0.0)//
}
}
}
The problem is that your making a brand new instance of your CircleView.
let cycle = CircleView()
You need to set your delegate to your current working instance.
To do so, you should replace your assignment in your viewDidLoad with the following:
override func viewDidLoad() {
super.viewDidLoad()
let app = UIApplication.sharedApplication().delegate! as! AppDelegate
if let viewControllers = app.window?.rootViewController?.childViewControllers {
viewControllers.forEach { vc in
if let cont = vc as? CircleView {
cont.delegate = self
}
}
}
}
Here's an article with project files.
I have the following Protocol:
protocol SoundEventDelegate{
func eventStarted(text:String)
}
which I call in this class:
class SoundEvent {
var text:String
var duration:Double
init(text: String, duration: Double){
self.text = text
self.duration = duration
}
var delegate : SoundEventDelegate?
func startEvent(){
delegate?.eventStarted(self.text)
}
func getDuration() -> Double{
return self.duration //TODO is this common practice?
}
}
Which I have my ViewController conform to:
class ViewController: UIViewController, SoundEventDelegate {
//MARK:Properties
#IBOutlet weak var beginButton: UIButton!
#IBOutlet weak var kleinGrossLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//DELEGATE method
func eventStarted(text:String){
kleinGrossLabel.text = text
}
//MARK: actions
#IBAction func startImprovisation(sender: UIButton) {
var s1:Sentence = Sentence(type: "S3")
var s2:Sentence = Sentence(type: "S1")
var newModel = SentenceMarkov(Ult: s1, Penult: s2)
s1.start()
beginButton.hidden = true
}
}
But when I run the app kleinGrossLabel.text does not change. Am I referring to the label in the wrong way? Or is it the way that I do delegation that is incorrect?
Here are links to the complete Class definitions of Sentence and SentenceMarkov
https://gist.github.com/anonymous/9757d0ff00a4df7a29cb - Sentence
https://gist.github.com/anonymous/91d5d6a59b0c69cba915 - SentenceMarkov
You never set the delegate property. It's nil. It will never be called.
First off it's not common practice to have a setter in swift. if you want to have a readonly property you can use private(set) var propertyName
in other cases simply access the property like mentioned in the comment
Also i don't see a reason why you eventArray in sentence is of type [SoundEvent?] not [SoundEvent] as SoundEventdoes not seem to have a failable initialiser
Like mentioned before you need to not only implement the SoundEventDelegate protocol but also set the delegate
the problem is that you can't really access the SoundEventDelegate from the viewcontroller because you instantiate the SoundEvents inside Sentence
var soundEventDelegate: SoundEventDelegate?
the easiest way to do this would be adding a soundEventDelegate property for sentence and setting it like this:
let s1:Sentence = Sentence(type: "S3")
let s2:Sentence = Sentence(type: "S1")
s1.soundEventDelegate = self
s2.soundEventDelegate = self
and inside sound you would need the set the delegate for every event to the soundEventDelegate of Sentence
you could do it like this:
var soundEventDelegate: SoundEventDelegate? = nil {
didSet {
eventArray.forEach({$0.delegate = soundEventDelegate})
}
}
or write another initialiser that takes the delegate
hope this helps
p.s: you shouldn't inherit form NSObject in swift excepts it's really necessary
I'm trying to make a kind of conditional segue through the "shouldPerformSegueWithIdentifier" override function, and it reports the error "Method does not override any method from its superclass" and tells me to remove the "override" word. When I do so, it reports the next error:
"Method 'shouldPerformSegueWithIdentifier(:sender:)' with Objective-C selector 'shouldPerformSegueWithIdentifier:sender:' conflicts with method 'shouldPerformSegueWithIdentifier(:sender:)' from superclass 'UIViewController' with the same Objective-C selector".
This is my code now:
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
//MARK: Propierties
#IBOutlet weak var whateverLabel: UILabel!
#IBOutlet weak var errorMessageLabel: UILabel!
#IBOutlet weak var textFieldRandomWord: UITextField!
//MARK: Actions
#IBAction func printWhatever(sender: UIButton) {whateverLabel.text = "Whatever"
}
#IBOutlet weak var goOnButton: UIButton!
override func shouldPerformSegueWithIdentifier(identifier: String, sender: UIButton?) -> Bool {
if identifier == "firstsegue" && sender == goOnButton { // you define it in the storyboard (click on the segue, then Attributes' inspector > Identifier
if textFieldRandomWord.text == "Whatever" {
errorMessageLabel.textColor = UIColor .redColor()
errorMessageLabel.text = "*** NOPE, segue wont occur"
return false
}
else {
errorMessageLabel.text = "*** YEP, segue will occur"
}
}
// by default, transition
return true
}
override func viewDidLoad() {
super.viewDidLoad()
textFieldRandomWord.delegate = self //ViewController is textFieldRandomWord's delegate, so "self" reffers to itself (ViewController)//
}
//MARK: UITextFieldDelegate
func textFieldShouldReturn(textField: UITextField) -> Bool {
//Hide the keyboard.
textFieldRandomWord.resignFirstResponder()
return true}
func textFieldDidEndEditing(textField: UITextField){
whateverLabel.text = textFieldRandomWord.text}
//Above, the textFieldShouldReturn function makes the text field inactive when return (enter) is pressed. The last function gets activated automatically when this happens.//
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
sender should be AnyObject?. You can't change type in methods declaration to fit your current need like that. You have to check inside the function if your sender is UIButton.