My colleagues and I prefer different approaches to solving one small task. We would like to know opinion of the community.
We have to process UITextField's text during editing. ( Text should be displayed in the view's title )
Two simplest approaches are:
1.Register for notifications:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(textFieldDidChangeNotification:)
name:UITextFieldTextDidChangeNotification
object:_titleTextField];
2.Use UITextFieldDelegate method
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSMutableString *mutableString = [textField.text mutableCopy];
[mutableString replaceCharactersInRange:range withString:string];
// use mutableString for father processing
return YES;
}
Which approach is better and why? Is there any reason why some approach should be avoided in this situation?
UPDATE: ( Some clarifications )
We do not need any additional flexibility that is provided by delegate ( like possibility to disallow some editing) or notifications ( possibility to add several observers ). Our main question is "Is it a good practice to use NSNotifications if we can easily solve an issue with delegate?"
UPDATE2:
Thanks everyone who answered the question!
It seems that community's answer for question "is it a good practice to use NSNotifications if we can easily solve an issue with delegate?" is yes. But for our original issue we have found another (third) variant which is better than delegates and NSNotification (see accepted answer).
I have coded a lot of both and I disagree with Adam. Notifications prove much harder to debug and trace backward most of the time. I also suspect they add more performance overhead, but that's anecdotal for me and I have not benchmarked it.
I lean toward notifications when there may be more than one observer that needs to act on an event, and/or when I want "loose coupling" where one part of the code shouldn't have specific knowledge of API's in another part.
In your case, the text field already comes with the logic to have a delegate and make calls to the delegate on certain events so it's a no-brainer. If one or more delegate methods get called at the time you need with the data you need. that's the simpler, more appropriate path.
The main difference between delegate and notification is that notification will notify all classes, in case they have subscribed to it. While delegate is one to one.
Considering that there won't be any further changes in application work flow. This approaches a pretty much equal. But I still prefer to use delegate because of following reasons:
it's more common approach, so it will make your code more readable and maintainable;
using notification you are have to subscribe to it, so it's extra lines. Not so many but ViewControllers usually are the largest classes in ios applications.
notifications use event DID while delegate use SHOLUD, so if you need to prevent your textfield from entering smth SHOULD fits better
Methods of a UITextFieldDelegate will return YES or NO depending on whether you want the user to be allowed to type certain things.
With this in mind, I'd use a notification. You don't care about the logic for changing the content of the UITextField, all you want to do is, whenever the content is changed, change the title of your view.
This gives you the benefit of allowing any delegate to still be able to return YES or NO if the content needs to be restricted and the notification will only be sent if the delegate has returned YES (or if there is no delegate).
EDIT
In case I misunderstood the question, process the text in the UITextFieldDelegate method and use the notification to update the view's title. Unless the UITextFieldDelegate is the view, in which case I'd just update the view's title directly from the UITextFieldDelegate method.
EDIT #2
Based on your further edit and requirements I would hands down use the delegate method. NSNotification comes with issues that delegates don't - debugging and performance (as RobP mentioned) is one. Another is it's much easier to leave an NSNotification hanging around after you have dealloc'd whatever it was attached to, resulting in a crash (good look debugging, see RobP's answer).
In your specific circumstances, delegate wins.
Since UITextField inherits from UIControl we also can use addTarget: action: forControlEvents:
[_textField addTarget:self action:#selector(textDidChange) forControlEvents:UIControlEventEditingChanged];
Related
I am working with keyboard resign features in iPhone app development. I would like to know why
self.textField.delegate = self
needs to be included into the viewDidLoad of a viewController. I have tried to find reasons of this but no explanation has been clear so far.
A few points
The reason you need to set the delegate is because without it the view doesn't know about the view controller. So it wouldn't know about your method textFieldDidEndEditing and it would never be called.
That is the basic premise of delegate, you are telling that object, "here is an object that I want you to call methods on"
It doesn't have to be set in viewDidLoad - but it's often the most convient place to set up delegates for views.
The delegate doesn't have to be the view controller (self), in your case it's the simplest way, but with a UITableView its common to have another class be the delegate so that all the logic isn't in one place and so it can be changed.
The UITextFieldDelegate protocol defines methods that you use to manage the editing and validation of text in a UITextField object. All of the methods of this protocol are optional.
A text field calls the methods of its delegate in response to important changes. You use these methods to validate text that was typed by the user, to respond to specific interactions with the keyboard, and to control the overall editing process. Editing begins shortly before the text field becomes the first responder and displays the keyboard (or its assigned input view).
From more info. check apple doc.
Its not necessary to use self.textField.delegate = self if you don't want to manage the editing and validation of text in a UITextField object as all the methods of UITextFieldDelegate is optional.
For your other questions like what does .delegate = self do??
When you "set the delegate," what you are doing is saying where you want the messages to go.
Hence,
blah.delegate = amazingPlace will send the messages to "amazingPlace".
blah.delegate = somewhereElse will send the messages to "somewhereElse".
blah.delegate = self will send the messages to you.
... check this source link for details
Delegates are key concepts in iOS development so I'd suggest taking a good look at the documentation for them. It can be particularly useful to create your own custom delegates in certain situations too so understanding and using them in the right places can really help improve the structure of your projects.
There are a couple of key reasons for using them. Firstly, they allow safe communication between classes. In your example, the textField object that you're using is communicating back to your view controller. This is why you need to set your view controller as its delegate. Otherwise the text field doesn't have a delegate object (your view controller) to communicate with. The text field fires certain methods at certain times, such as textFieldDidBeginEditing, and calls these on its delegate object if it has one. When you register your view controller as the text view's delegate you can tap into these callbacks.
The other benefit is that delegates allow you to separate concerns and encapsulate or abstract responsibilities. It might be that the main concern for the text view is how to handle text in its view but not necessarily what to do when has been entered, or when the return button in the keyboard is pressed, or how to validate text that has been input. It's better that these tasks are handed over to something else, such as a delegate (in Obj-C parlance), and that is why in your example you have to register one class as the delegate for another.
As stated before, UITextfield delegation allows you to control events on your textfield.
You ll have the ability to edit functions like
textFieldShoulEndEditing
or
textFieldDidEndEditing
in order to add custom rules, for example : text validation.
Take a look at Apple doc.
If you don't need it, you can delete this line and UITextfieldDelegate on your class declaration.
You need to either set the delegate of a UITextField in code with self.textField.delegate = self
or make your viewcontroller (or any other class) a delegate with class MyViewController: UITextFieldDelegate and set the delegate of the UITextField in the storyboard by control dragging from the textfield to the viewController.
I have UITextField that I have formatting code attached on textFieldDidEndEditing:. This works fine, but when I first load the text field with a text value (someTextField.text = #"...") the formatting doesn't happen. Of course, I can add another formatter there, but it seems kind of repetitive.
Is there a way to make all changes to a UITextField, programmatic or user originated, have formatting applied automatically?
Just run the same method from both places...
- (void)setTextFieldWithText:(NSString *)text {
//do formatting here...
self.textField.text = formattedText;
}
You can either add UITextFieldDidChangeNotification or just add a observer(using the addObserver method) to observe changes to the textfield. Choose which suits your approach. Both of them does cut the repetitive code. KVO - concept related to your question.
you might get some idea if you take a look at the following links
UITextFieldDidChangeNotification - How do i notify changes in UITextField?
addObserver method - detecting the change of content of UITextField when the change is not made by the keyboard
Note: Although the classes of the UIKit framework generally do not support KVO, you can still implement it in the custom objects of your application, including custom views.
I has many UITableViewController subclasses in my app.
Now i just needed to modify them all to add +1 row in all cases, and one simple equal row in all.
I do not want to modify all of them by hand, better way seem's to replace UITableViewDataSource method to modify values in way like:
+(void)load {
[[self class] jr_swizzleMethod:#selector(tableView:numberOfRowsInSection:) withMethod:#selector(swizzledTableView:numberOfRowsInSection:) error:nil];
}
- (NSInteger)swizzledTableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self swizzledTableView:tableView numberOfRowsInSection:section] + 1;
}
But it replaces superclass function, that does not called in subclasses, so this is not working. Is there method to do that what i want, without modifying all subclasses?
You'd need to swizzle every subclass specifically. You can find them by introspecting the class hierarchy at runtime with objc_getClassList, but I can't begin to describe how dangerous and fragile this approach is. You're trying to apply this to every tableview in the system, which you hope is just the tableviews you mean it to be (i.e. your tableviews). But what about tableviews that might be used by the system or from third-party libraries? You're modifying them, too. And when you try to understand the crash this causes, the stack trace will be unintelligible because of the swizzle.
In order for this to work, tableView:cellForRowAtIndexPath: also needs to correctly handle this extra row, so it's hard to see how every table view controller in the system is going to be implemented correctly without knowing about this +1.
Either subclass your table view controller (and have them call super), or use a separate object that all of them call to add the extra row if it's needed. This other object (or superclass) is also where you should handle the cell for this extra row.
I have little experience in swizzling. But I have two possible solutions to your problem.
First:
Create a subclass: YouBaseTableView: UITableView, and add a row in YouBaseTableView. And inherit all your table view classes from YouBaseTableView.
Second:
Create an extension for UITableView, and write your row in this extension.
I'm probably late for the train...
But for future reference, a solution for the problem would be to swizzle setDataSource of UITableView and replace it with an NSProxy instance.
Usually nobody overrides the setDelegate / setDataSource methods, and that would allow you to swizzle those and intercept all calls to these delegates and exchange the implementation.
Check this out for more info: https://developer.apple.com/documentation/foundation/nsproxy
You are going into two different areas that need full understanding to be used correctly and are highly dangerous: Performing code in the +load method, and using method swizzling. I would never dare doing anything in +load. +initialize is ok if you know what you are doing, but +load is something you mustn't even think of touching if you ask questions here.
Now ask yourself first: What is "self" in a class method, and what is "[self class]"? Do you think this has even a chance of working?
I'd also recommend that you google for "swizzle" and pick up some other code for method swizzling. It looks quite dubious to me. And writing it as a category instead of a plain C function feels just horrible.
I am going to have several uiimageview interactive sulasses that need a delegate. when each one of them is touched, the view controller must respond by changing something.
Is it good common practice to have one method , that is the delegate method get called by all of the uiimageview subclass instances?
The delegate will be the view controller.
Overall, is that good practice?
One method is better. This one will be a good option - (void)imageViewTouched:(UIImageView *)sender
You should follow DRY (Do not Repeat Yourself) principle and eliminate code duplcication in your app. So you should have separate methods if they have separate logic. You should have one method if the same type of logic executed for various senders.
Absolutely. Pass in the sender so you can make decisions based on who sent you the message (like tableView:cellForRowAtIndexPath:), and you have a good implementation of the delegation pattern.
I have a viewController, in which I have a view in which I draw like in Paint by tapping. In that controller I have a method to clear screen -
- (void) clearS
{
[(TouchTrackerView *)self.view clear];
}
Now, as I don't want to occupy my screen with buttons, I have button in another screen that resets application to starting position, and I want it to clear screen. That button belongs to different view controller, and among other things I want it to call my drawing view controller and ask it to wipe screen clear. So I have setup notifications, like this in draw view contoller:
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(clearS) name:#"clearScreen" object: nil];
And like this in my view controller from where I click a button:
[[NSNotificationCenter defaultCenter] postNotificationName:#"clearScreen" object: nil];
As I understand, when the button gets clicked, notification gets posted, and method is called. And it crashes.
[(TouchTrackerView *)self.view clear]
As I understand the problem is that at the moment of method invocation the "self" variable is my non-drawing view controller, and it tries to perform clear method on view of itself, which it lacks and crashes.
How do I send the method to correct view controller?
Is there a flaw in my thinking? Maybe this can be approached in a better way?
EDIT:
I have found the problem,
[(TouchTrackerView *)self.view clear]
calls view and it is UIScrollView, and it does not support clear method. So I have made a property containing the correct view, and changed it to
[(TouchTrackerView *)self.correctView clear]
and it works like a charm.
I have chosen notification because it is only two lines of code, and I am beginner and it is hard for me to wrap my head around delegates, so I will leave this as it is, especially that it works.
At the philosophical level, while I'm sympathetic to the observations from others that you could replace the notification with a delegate pattern (or, better, delegate protocol pattern), it strikes me that this is not an appropriate example of having two controllers communicating directly to each other at all. You probably should be employing a MVC model, where the editing view controller, A, is designed for the editing of a drawing (and thus updates the model and coordinates the view) and the reset view controller, B, should just update/reset the model. If A needs to be informed of model changes, then apply a delegate-protocol pattern there, between the model and controller A. But I don't think B should be communicating with A at all.
At a practical level, there's absolutely no reason why notifications shouldn't work just fine. Unfortunately, you haven't shared enough for us to answer the question. You should share the specifics of the error message and we might be able to help you more.
I think that notifications, which are in nature a one-to-many method of communication are not good to use here. A better approach would be to hook one as the delegate of the other -> once the button is pressed and the corresponding IBAction is invoked, the delegate (in your case, the view controller you use for drawing) should get a message and perform whatever it is it needs to do.
I would avoid notifications unless you are trying to broadcast out some info that multiple objects may be interested in.
A better approach might be to create a delegate protocol for the painting view controller --
So the PaintingViewControllerDelegate Protocol may have methods like so
(void) paintingViewControllerWillClear:(PaintingViewController*)paintingViewController;
(void) paintingViewControllerDidClear:(PaintingViewController*)paintingViewController;
Now the controller with the buttons becomes the delegate to the PaintingViewController and that object provides the methods of the PaintingViewControllerDelegate protocol as needed.