Add didSet observer on a weak property in an extension - ios

I am trying to make a UITextField extension which performs additional functions upon the setting of a delegate.
extension UITextField {
override weak public var delegate: UITextFieldDelegate? {
didSet {
print("Do stuff")
}
}
}
This fails with three errors:
'delegate' used within its own type
'weak' cannot be applied to non-class type '<<error type>>'
Property does not override any property from its superclass
What do I need to change for Do stuff to be printed upon the setting of the delegate?

You can't override delegate property using extension, you need to create subclass:
class TextField: UITextField {
override weak var delegate: UITextFieldDelegate? {
didSet {
super.delegate = delegate
print("Do stuff")
}
}
}
But this seems kinda wrong. What are you trying to achieve?

Related

Delegate not executing after call swift

I have a viewController with another containerView insider set up to appear temporarily (added programmatically). The containerView is a sort of operation bar, which allows you to change values of the viewController. The protocol called from an IBAction of a button however, does not call the protocol set up inside the viewController class.
Here is the code from both classes:
class viewController: UIViewController, updateListDelegate {
let dataSource = containerView()
override func viewDidLoad() {
super.viewDidLoad()
dataSource.delegate = self
}
func updateList(sender: containerView) {
print("is called") //is not printed
}
}
The code from the containerView:
protocol updateListDelegate {
func updateList(containerView)
}
class containerView: UIViewController {
var delegate: updateListDelegate?
#IBAction func AddSong(_ sender: UIButton) {
self.delegate?.updateList(sender: self)
}
}
If this method is only to be called from one object, then, in my opinion, I would not define a protocol. If multiple objects are to call this method, then I would define a protocol. This is typically how you would call a method backwards, using a basic delegate.
class ViewController: UIViewController {
let container = ContainerView()
override func viewDidLoad() {
super.viewDidLoad()
container.viewControllerDelegate = self
// push to this instance of container at some point
}
func doSomething() {
print("great success")
}
}
class ContainerView: UIViewController {
weak var viewControllerDelegate: ViewController?
#objc func someAction() {
if let viewControllerDelegate = viewControllerDelegate {
viewControllerDelegate.doSomething()
}
}
}
// prints "great success" when someAction() called
One of the most common mistakes people make is not keeping track of instances. For delegates to work, you must be sure you are using the specific instances that you've instantiated and assigned those delegates to.

Cannot pass immutable value of type 'NSObject' as inout argument

This should work, but I've got no clue why it doesn't. The code is self-explanatory.
class Themer {
class func applyTheme(_ object: inout NSObject) {
//do theming
}
}
And I apply theme to the button like so:
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
Themer.applyTheme(&button)
}
The button object is a variable, yet the compiler throws an error.
Since button is an object, this syntax
Themer.applyTheme(&button)
means that you want to change the reference to that object. But this is not what you want. You want to change the referenced object so you simply need to write
Themer.applyTheme(button)
Finally you also don't need the inout annotation
class Themer {
class func applyTheme(_ object: AnyObject) {
//do theming
}
}
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
Themer.applyTheme(self.button)
}
}
But...
However, what should your applyTheme method do? It receives AnyObject and then what? You could make it a little but more specific and use a UIView as param
class Themer {
class func applyTheme(view: UIView) {
//do theming
}
}
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
Themer.applyTheme(view: button)
}
}
Now you have a chance to write meaningful code inside Themer.applyTheme.
inout is for the case that you want to change the reference, that is replace one object with another object. That's a very, very, very bad thing to do with an IBOutlet. That button is used in a view, connected up to lots of things, and if you change the variable, all hell will break lose.
Apart from that, listen to appzYourLife.

Setting label text on a UIViewController from a Non-UIViewController Class

I have a UIViewController and I'm refactoring it and I've bumped into a situation where I need to update it from another class. I know I need to use the delegate pattern, but I'm having a tough time finding an example that fits my situation (which I think it dirt simple).
ItemViewController has a Timer class that's instantiated. I'm trying to update an ItemViewController label from the Timer class.
On Timer, I've done the following:
weak var delegate: TimerDelegate? // weak to avoid a retain cycle
func updateLabel(timeRemaining: Int) -> String {
return formatTimeInSeconds(timeRemaining) // another method w/in Timer
}
I declare the protocol at the bottom of the Timer class
protocol TimerDelegate: class {
func updateLabel(timeString: String) -> String
}
On ItemViewController I have the following property declared:
#IBOutlet weak var timeValueLabel: UILabel?
I set it as a delegate of Timer in viewDidLoad as follows:
timer.delegate = self
What I'm trying to make happen is when updateLabel is called on Timer, I'd like to update timeValueLabel.text on ItemViewController. This is where I'm stuck...what next?
If your Timer class assign from any where and you need to change the text of label you can use singleton object for that, create one singleton object with your Timer class then use that object to set delegate like this way.
class Timer {
static let sharedInstance = Timer()
weak var delegate: TimerDelegate?
//Now use this delegate to call method.
func updateLabel(timeRemaining: Int) -> String {
delegate?.updateLabel(formatTimeInSeconds(timeRemaining)
return formatTimeInSeconds(timeRemaining)
}
}
Now you need to just set this delegate in your ItemViewController like this.
class ItemViewController: UIViewController, TimerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
Timer.sharedInstance.delegate = self
}
func updateLabel(timeString: String) -> String
self.label.text = timeString
return "" //return string that you want.
}
}
I guess you should do:
func updateLabel(timeRemaining: Int) -> String {
let formattedTime = formatTimeInSeconds(timeRemaining)
delegate.updateLabel(formattedTime)
return formattedTime
}
And in the ItemViewController you should declare that the class follows the delegate and implement the declared method. Something like:
class ItemViewController: TimerDelegate {
...
func updateLabel(timeString: String) -> String {
...
}
}

Swift: Delegation protocol not setting UILabel properly

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

How to distinguish UITextFields in delegate methods?

I have the following code:
class ViewController: UIViewController, UITextFieldDelegate {
// MARK: Properties
#IBOutlet weak var layersTextField: UITextField!
#IBOutlet weak var innerShapeTextField: UITextField!
#IBOutlet weak var outerShapeTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
layersTextField.delegate = self
innerShapeTextField.delegate = self
outerShapeTextField.delegate = self
}
// MARK: UITextFieldDelegate
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func textFieldDidEndEditing(textField: UITextField) {
// do something
}
}
Now in textFieldDidEndEditing(_:) I would like to do something, dependent on which UITextField called this method.
Is there any way to distinguish, which UITextField did this? Is there some kind of ID or identifier I can set on the UITextFields?
You can make this determination using one of two approaches: outlets or tags. For the outlet approach, declare an outlet instance variable (using the IBOutlet keyword) and then make an outlet connection. In your delegation method, test whether the passed-in text object is the same object referenced by the outlet, using pointer comparison.
For example, say you declare and connect an outlet named SSN. Your code might look something like Listing 3-1:
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
if (textField == SSN) {
// ...
return NO;
}
return YES;
}
// Translated to Swift:
func textFieldShouldEndEditing(textField: UITextField) -> Bool {
if textField === SSN {
// ...
return false
}
return true
}
You can check by the name of the next filed. if textField == layersTextField { //do what you want } and you can do that for any text field you need a specific action for.
You can create an IBAction instead:

Resources