I have custom UITextView:
public partial class EditableDescriptionTextView : UITextView, IUITextViewDelegate
{
ctors()...//Invokes Initialize();
void Initialize()
{
Delegate = this;
}
//Override methods I need to handle
}
In my View I have a binding to the ViewModel like so:
bindingSet.Bind(EditableDescriptionTextView)
.To(vm => vm.Description);
The problem is that firstly works that string: Delegate = this . It Ok, but when works bindingSet.Bind(...).To(...) seems that delegate changes and application crashes with exception like :
Event registration is overwriting existing delegate. Either just use events or your own delegate
The same error happens when I directly try to assign delegate after bindings like:
// CustomDelegate extends UIViewTextDelegate
EditableDescriptionTextView.Delegate = new CustomDelegate();
In other words, app fails each time when I reassign the delegate. Is there any possibility to use my own delegate with MvvmCross or what is the best workaround of this situation?
P.S.: Originally I need to implement functional like here
ShouldChangeTextInRange is not called for UITextView , but I also need mvvxcross binding.
This doesn't seem like a MvvmCross problem. MvvmCross does not attach any delegates to UITextFields. But since you are implementing your own Delegate, eventhandlers that MvvmCross uses to listen to changes in such views for TwoWay bindings, will probably not work after you have attached your custom delegate.
So I would simply stick to the events that UITextField provides you.
Related
I am new in iOS development.
I learnt about Delegate (Single Observer) and also NSNotificationCenter to listen to event.
Now I want to pop up an alert view when an event in a manager class happens. May I know if there's a way to pop it up regardless which view controller is showing? From my understanding, NSNotificationCenter allows me to show a alert but I have to implement the show up method on each of the view controller class. Is it a better way to do it?
Thank you!
If you just want to show up a native alert view, you can use AppDelegate class as a listener to NSNotoficationCenter, because native alert view will be added to top window directly.
If you are worry about the fact to implement your alert in every controller, you shouldn't.
They are many ways to solve that issue, you could for instance :
Subclass UIViewController, and implement a method inside that will display your alert. Just keep in mind that everytime you'll use a UIViewController, use your subclassed controller instead.
You could use protocols (objective C) or extensions (swift), to add a function that will display your alert view. In that case you don't even need to subclass anything :
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Extensions.html
Also don't forget that in swift, any properties can be listened natively. For instance, when you set your property you can do this :
var myProperty : String {
willSet {
doSomething()
}
didSet {
presentMyAlertController()
}
}
I understand how to use delegation with iOS objects that already exist. For example, if I create an instance of a UITableView, and my view controller conforms to the UITableView delegate, I can implement the various methods of the UITableView delegate. My newly create table can receive notifications, for example, when didSelectRowAtIndexPath is called.
My question is why did my table get this particular delegate callback? My understanding is that the delegate is just a list of methods with no implementation. It seems to me there must be a lot more going on. What is really going on "behind the scenes"?
Image if I were to rename all the delegate methods to the following:
- mysteryMethod1
- mysteryMethod2
- mysteryMethod3... Etc
One of these methods is responsible for setting the height of a row at a particular index. Another one these methods will be responsible for editing a particular row.
Everything I read about delegation says the delegator makes a contract with the delegate. The delegate promises to implement the methods. When it does, somehow everything is wired up correctly and everything magically works. What is the magic that I'm not seeing?
I think that in order to know how delegates actually work you should create your own custom delegate first, that way you will see that there is no magic under the hood, you probably can't see the implementation of the actual apple build in delegate methods but I assure you that there is a lot of logic implemented in those but it's just not available for privacy reasons I assume.
When you create your custom delegate let's say for example...
You have Class A and in this class, you start by creating a protocol
protocol ClassADelegate: class {
func changeBackgroundColor(_ color: UIColor?)
}
In this class you have a delegate property like this.
weak var delegate: ClassADelegate?
Let's say that this class is a Viewcontroller and you have an IBACtion on it like a UIbutton, and your goal is that when you tap that button another ViewController in your app change its background color to blue. Inside this action in Class A you do this...
func someAction() {
delegate?.changeBackgroundColor(.blue)
}
,
Let's say that the "magic" happens here in class A, by the way if you are thinking in delegates using UITableview think that UItableView is class A.
ok so now you have Class B that is where you want to change the color right?
Well now class B needs to conform to the protocol like this, just like you also conform to the protocol UITableViewDelegate etc.
class ClassB: UIViewController, ClassADelegate {
}
Now think of the word delegate for a second and think what that means, you are just delegating responsibility to somebody else, right? and yes, in this case, ClassB is going to be the delegated, for that we need to have an instance of Class A in class B just to have access to it's delegate property.
let classa = ClassA()
classa.delegate = self
the final step is just to call the method of the protocol like this..
func changeBackgroundColor(_ color: UIColor?) {
view.backgroundColor = color
}
To conclude if yo see this method in your class but you don't have access to the implementation of the protocol, you will ask yourself "where does this magic color coming from??" but as you saw it just comes from another class where a protocol belongs to, hope this helps.
I have tried to make a location autocomplete text view class by subclassing UITextField and use Google Place Autocomplete API. This works great, but I have a design error due to the implementation. To observe when the user types text, I set the UITextFieldDelegate to self in the custom subclass and track changes to the typed text in textView:shouldChangeTextInRange:replacementText:. This works, but here is the design error: If someone now wants to check what is typed into the custom subclass by setting the delegate to something new, the delegate of my class is not set to the object of the class itself anymore. Now the custom class is useless. Is there any way to either get the text as it is typed without the delegate, prevent the delegate from being changed, or in any other way fix my problem?
A few options I have though about that could work, but in a bad way:
Check regularly what the text property is: Should be obvious why busy waiting is a stupid idea
Override the delegate property and set it to private: Not sure if this will even work, but if it did, the class is no longer a proper subclass of UITextField and all delegate methods are unavailable when implementing my subclass.
Provide a new delegate for further use of the delegate: Allows someone to get the same things as the UITextFieldDelegate provides, but it still messes up the documentation and proper implementation of UITextField
Delegates in UIKit I normally one to one connections. Which can cause the problem you have described.
If you want multiple delegates of a UITextField I would derive a class from UITextField for example MYTextField and add a method to addDelegate and removeDelegate that maintains a list of delegates. The sent the MYTextField's delegate to itself and broadcast any delegate method to all listeners in the delegate array.
this post shows example code on how do maintain a list of multiple delegates.
Delegation to multiple objects
Say I write a UITextField subclass and want to have control over the text written into it by the user. I would set the input field's delegate to be myself and implement -textField:shouldChangeCharactersInRange:replacementString:.
However, I would still want to allow whatever part of code uses me as a text field to implement the usual delegate methods. An approach for that would be to store a second delegate reference and map them like so:
- (id)init {
self = [super init];
super.delegate = self;
return self;
}
- (void)setDelegate:(id)delegate {
self.nextDelegate = delegate;
}
- (id)delegate {
return self.nextDelegate;
}
I would then proceed to implement all UITextFieldDelegate methods and forward them to the next delegate as I wish. Obviously, I may want to modify some parameters before passing them on to the next delegate, like in -textField:shouldChangeCharactersInRange:replacementString:.
Another problem I'm thinking of is when the user's sets nextDelegate to the text field itself (for whatever reason), resulting in an infinite loop.
Is there a more elegant way to hijack delegate callbacks like in the example code I posted?
The problem with your approach is the overridden delegate accessor: There's no guarantee that Apple's code always uses the delegate ivar directly and does not use the getter to access the delegate. In that case it would just call through to the nextDelegate, bypassing your sneaked in self delegate.
You might have checked that your approach works in the current implementation but this could also change in future UIKit versions.
Is there a more elegant way to hijack delegate callbacks like in the example code I posted?
No, I'm not aware of any elegant solutions. You could not override the delegate accessor and instead set up secondary delegate (to which you have to manually pass all delegate messages).
To solve the actual problem of filtering text input it might be worthwhile looking into
- (void)replaceRange:(UITextRange *)range withText:(NSString *)text;
This method is implemented by UITextField (as it adopts UITextInput) and could be overridden to filter the text argument.
I think you're thinking about this correctly, and the approach you outlined will work fine (I've done it).
There's no circularity issue because you shouldn't expose nextDelegate in the subclass's public interface, so no caller will have the chance to setup a cycle. (You could also test in the setter that delegate != self.
It would be better, though, if you could avoid this altogether. For example, if you just want to tweak the text field text as it changes, you can get the control event:
[self addTarget:self action:#selector(didChange:) forControlEvents:UIControlEventEditingChanged];
Then,
- (void)textFieldDidChange:(id)sender {
self.text = [self alteredText];
}
- (NSString *)alteredText {
// do whatever transform to user input you wish, like change user input 'a' to 'x'
return [self.text stringByReplacingOccurrencesOfString:#"a" withString:#"x"];
}
This will work as well, but with the odd side effect that the delegate won't see the alteredText in shouldChangeCharactersInRange:. That's fixable by making alteredText public and having the class customers call it instead of the standard getter.
All of the problems with subclassing can be avoided by using a different approach of intercepting delegate messages: A "delegate proxy".
The idea is to use an intermediate object (derived from NSProxy) that either responds to a delegate message or passes it along to the next delegate. It's basically what you did by subclassing the UITextField but instead of using the text field object we'll use a custom object that handles only the interception of some delegate messages.
These customized delegate proxys form a set of reusable building blocks which are simply plugged into each other to customize the behavior of any object that uses delegation.
Here's an example (code on github) of a chain of delegates:
UITextField -> TextFilterDelegate -> SomeViewController
The UITextField passes delegate messages to TextFilterDelegate which responds to textField:shouldChangeCharactersInRange:replacementString: and passes other delegate messages on to its own delegate (the view controller).
There are several ways to handle event in MonoTouch. It looks to me that mapping the event in IB is the most reliable way to do. What I don't understand is why sometimes the event mapped in ViewDidLoad doesn't work. For example, I have a UITextField (called tfCode). If it's mapped in IB for EditingDidEnd to tfCodeChanged, it works:
partial void tfCodeChanged(NSObject sender)
{
...
}
However, in ViewDidLoad, if I put in the following code, it doesn't get hit:
tfCode.EditingDidEnd += delegate {
...
};
But in general I'm doing a lot of event handling in ViewDidLoad and they mostly work.
So, I'm confused. Can anybody explain why?
The events are triggered as long as you do not override the internal handlers by assigning to either the Delegate or WeakDelegate properties.