MonoTouch event handler in ViewDidLoad not working sometimes? - ipad

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.

Related

How to use custom delegate and MvvmCross Binding on Xamarin.iOS?

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.

Where is the correct place to populate UITableView?

I have a UITableView in my ViewController. To populate it, I have to make an async request that may take up to a second to complete. Where should I put it?
When I tried to make ViewDidLoad async and make a call from there, ViewWillLayoutSubviews throws an error, because it tries to position some elements that weren't assigned yet - due to the fact that not all code from ViewDidLoad was executed yet (I think).
Before awaiting anything in ViewDidLoad you need to setup all your view logic. Otherwise your view initialization will not be finished when ViewDidLoad method returns. That could be a potential cause for ViewWillLayoutSubviews to fail. If it still fails, use a try/catch to make sure your service is working:
public override async void ViewDidLoad()
{
base.ViewDidLoad();
// setup all the view elements here (before the async call)
try
{
var results = await MakeYourAsyncRequest();
InvokeOnMainThread(() =>
{
_tableView.Source = ...; // do something with the results
_tableView.ReloadData();
});
}
catch(Exception ex)
{
// do something with the exception
}
}
Try putting the tableView.ReloadData(); method inside
dispatch_async(dispatch_get_main_queue(), ^{} this might solve your issue.
-:As a general rule, you should try to make sure that all of your UI interaction happens on the main thread. And your data fetching task will work in background. It looked like you were calling reload Data from your
background thread, which seems risky.
Depending on the data I would put the call in the AppDelegate. When the app launches the data should be fetched and saved.
When your UITableview appears it will already have the data ready or maybe an error message since you already know the result of the fetch.
The data may change thats why I would also put the fetch call in viewWillAppear() of your ViewController with the UITableview.
ViewDidLoad() is a method that gets called only once. Also it is called as the first method of the ViewController lifecycle.
It would be good if you read a bit about it VC lifecycle.
You can help yourself by trying it in code with printf("method name").

No ItemSelected call in my UICollectionView?

So I'm trying out Xamarin Forms, and I made a control that has a renderer that outputs a UICollectionView. The collection view in question was ported from an Objective C version. In the Objective C version, I implemented collectionView:didSelectItemAtIndexPath: and when I clicked on the item that method was called. But in the Xamarin version, this does not seem to be the case and the ItemSelected method is not called. I've tried using both the Delegate and WeakDelegate versions to no avail. I made sure AllowsSelection is true.
I tried adding a UIButton to the cell, and was able to get a log entry from its TouchUpInside handler, so I don't think it's a matter of another view on top stealing the touches. Pans and such work. And in Simulator I had it highlight the drawn layers and didn't notice anything fishy.
Anyone have any ideas of stuff to try? Thanks.
I don't know if this can help you, but i get the same problem using the GridView control of the Xamarin.Forms.Labs.
I see that in delegate, the method "ItemSelected" is not called, but is call the "ItemHighlighted" method. So I used this to make the GridView selectitem works.
I hope this can be helpfull. :)
Double check that you have assigned the UICollectionViewSource object to ColelctionView.Source and NOT ColelctionView.DataSource by mistake
I did eventually fix this. What was happening is that Xamarin.Forms adds a gesture recognizer to the root of the page which cancels touches in the page. This works well in general, but does not work well in the case of UICollectionViews where it interferes with the UICollectionView calling ItemSelected.
What I ended up doing was to create this custom renderer:
public class IosTapFixPageRenderer : PageRenderer {
public override void ViewDidLoad()
{
base.ViewDidLoad();
foreach (var g in View.GestureRecognizers) {
g.CancelsTouchesInView = false;
}
}
}
Then I assigned this as the renderer for problem pages containing UICollectionViews using the usual attribute method:
[assembly: ExportRenderer(typeof(CalendarPage), typeof(IosTapFixPageRenderer))]

Weak Event Pattern in MonoTouch

I used to develop iOS apps using the Objective-C language, and relied on the dealloc method to perform some cleanup/unregister tasks in my application. Now on the MonoTouch (garbage collected) it is not an option anymore.
Suppose I have a UIViewController that adds as a subview of it's View property an instance of MyView (UIView subclass). MyView in turn registers itself to receive some events from another manager/global object so that it knows how to update itself accordingly (e.g.: onlineProfilesManager.Refreshed += () => <update UI with the new state>;).
As long as MyView is on screen, everything is fine. However I must know when it's removed from the screen so that I can unregister MyView from the event handler.
In Obj-C this could be simply done in the dealloc method because when the screen changes the UIViewController is deallocated --> MyView is removed from it's superview and then MyView dealloc method is called.
In Monotouch I don't have this 'deterministic' flow anymore. I tried to put some print statements in the UIViewController and MyView destructors but they are never called (the reason is because the MyView is still registered for the event handler, since I don't know when/how to unregister it, it will never be deallocated).
Does anyone know what is the 'pattern' to handle such situations in MonoTouch? I think I'm missing a fundamental concept and getting into trouble developing my apps.
Thanks in advance.
EDIT
I'm editing my question because looks like the solution for my problem is using the Weak Event Pattern but I didn't find an implementation for the MonoTouch platform.
Does anyone know how can I use the Weak Event Pattern in MonoTouch ?
The best way to handle events is to unregister them in ViewWillDisappear and register them in ViewWillAppear. This means that you can't use anonymous methods though as you don't have a reference to the method to unregister it.
If that doesn't suit what you need, you can do something similar to this http://sgmunn.com/blog/2012/05/non-gcd-event-handlers/
Cheers.
If you are looking for weak events, you can try my "Messenger" implementation here.
It is inspired by what is available in TinyIoC, but I re-implemented it so it used less reflection, etc.

What's the best way to call an IBAction from with-in the code?

Say for instance I have an IBAction that is hooked up to a UIButton in interface builder.
- (IBAction)functionToBeCalled:(id)sender
{
// do something here
}
With-in my code, say for instance in another method, what is the best way to call that IBAction?
If I try to call it like this, I receive an error:
[self functionToBeCalled:];
But, if I try to call it like this (cheating a bit, I think), it works fine:
[self functionToBeCalled:0];
What is the proper way to call it properly?
The proper way is either:
- [self functionToBeCalled:nil]
To pass a nil sender, indicating that it wasn't called through the usual framework.
OR
- [self functionToBeCalled:self]
To pass yourself as the sender, which is also correct.
Which one to chose depends on what exactly the function does, and what it expects the sender to be.
Semantically speaking, calling an IBAction should be triggered by UI events only (e.g. a button tap). If you need to run the same code from multiple places, then you can extract that code from the IBAction into a dedicated method, and call that method from both places:
- (IBAction)onButtonTap:(id)sender {
[self doSomething];
}
This allows you to do extra logic based on the sender (perhaps you might assign the same action to multiple buttons and decide what to do based on the sender parameter). And also reduces the amount of code that you need to write in the IBAction (which keeps your controller implementation clean).

Resources