I didin't completely understand the delegate mechanism in monotouch. Can anyone help me to understand this concept?
The question is simple. I'll try to map what I've done in Objective C in Monotouch.
For example, suppose I've creating a UIPopoverController in Objective C inside MyController. In Objective C the code is the following:
#interface MyController : UIViewController <UIPopoverControllerDelegate> {
// ...
}
// ...
#end
Inside MyController I can istantiate a UIPopoverController like the following:
UIPopoverController *popover = // ...
popover.delegate = self;
and finally methods used in the delegate.
So, what about Monotouch?
Through this code I can istantiate the UIPopoverController inside MyController class that extends UIViewController inside a specific TouchUpInside event handler:
popover = new UIPopoverController(new CustomController());
popover.PopoverContentSize = new SizeF(200f, 200f);
popover.PresentFromRect(button.Frame, containerForButtonView, UIPopoverArrowDirection.Left, true);
P.S. An important aspect is to put popover reference as a member class and not as a local variable inside the handler because the monotouch GC works well!!!
Thank you in advance.
This really has more to do with C# than MonoTouch itself. In MonoTouch, UIPopoverControllerDelegate is a class, and C# doesn't allow multiple inheritance, so you can't translate code one to one with Obj-C. There's an easier way out though (code below compiles, but obviously doesn't work):
public class MyController: UIViewController {
public void mymethod(){
var popover = new UIPopoverController();
popover.DidDismiss += HandlePopoverDidDismiss;
popover.PopoverContentSize = new SizeF(200f, 200f);
popover.PresentFromRect(button.Frame, containerForButtonView, UIPopoverArrowDirection.Left, true);
}
void HandlePopoverDidDismiss (object sender, EventArgs e)
{
Console.WriteLine("Working!");
}
}
}
As you can see, you can add an event handler to to the DidDismiss event in the popover, which will do what you want. In general, all events that in Obj-C are handled by the delegate in all controls can be used this way. You can also write the method inline, like this:
popover.DidDismiss += delegate {
//dosomething
};
Hope this is what you're looking for.
This doesn't answer your question specific to your UIPopovercontroller I think you will find this link from the Monotouch Docs useful. It explains the differences between Objective-C delegates and C# delegates with relation to Monotouch. With regards to your specific problem, I havent got time to whip up a quick test case to understand it fully but figured I'd post that link so you've got something to read in the mean time!
Related
I recently started developing using Xamarin, so I'm by no means an expert and have been stuck on this problem for a day or so now.
First of all, I am not using storyboards. I am creating my own custom views (xib) and loading them from code
I'm building a new Xamarin.iOS app and am attempting to load a view controller from within another view controller. Initially, I am loading the first controller from the AppDelegate like so:
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
window = new UIWindow(UIScreen.MainScreen.Bounds);
appStartUpController = new AppStartUpController();
window.RootViewController = appStartUpController;
window.MakeKeyAndVisible();
return true;
}
This loads my AppStartUpController fine which is basically just a loading screen with a background image and loading animation while I make an API call in the background. Once the API call has completed, I want to load another view controller.
After the API call has completed, I attempt to load the next Controller like so:
var controller = new CityPickerViewController();
this.NavigationController.PushViewController(controller, false);
And here is my CityPickerViewController:
public partial class CityPickerViewController : UIViewController
{
CityPicker_View v;
public CityPickerViewController(IntPtr handle) : base(handle)
{
}
public CityPickerViewController ()
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
v = CityPicker_View.Create();
this.View = v;
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(false);
UIImage i = UIImage.FromFile("citypickbackground.jpg");
i = i.Scale(this.View.Frame.Size);
this.View.BackgroundColor = UIColor.FromPatternImage(i);
}
}
I'm probably missing something obvious here, but the CityPickerViewController will not load. If I put a break point within the code, the viewDidLoad / ViewWillAppear overrides never get hit.
I'm a rookie programmer and would definitely appreciate any tips on this. Thanks in advance!
Welcome to SO!
Try base.NavigationController.PushViewController(controller, true); instead since you don't have a local navigation controller.
There could also be an issue in your CityPickerViewController, so try a different ViewController if that doesn't work.
There are lots of related answers about using menuWillOpen. They all explain that one needs to set the menu's delegate first.
This is easy when I have just one target, like a Preferences window or the main application.
But what if I have a document based app, and I need to have the active document handle menuWillOpen? Then the delegate isn't a constant any more.
What's the proper way to handle this? Do I have to set the delegate to a single object (like the AppDelegate) and then forward the call to the active view controller (but how is that done correctly)? Or is there some other elegant way?
I came up with this code which appears to work:
// This is in my AppDelegate class, and the NSMenu's delegate points to it:
- (void)menuWillOpen:(NSMenu *)menu {
// Forward to active document controller
NSWindow *mainWindow = [NSApplication sharedApplication].mainWindow;
NSResponder *r = mainWindow.firstResponder;
while (r) {
if ([r respondsToSelector:_cmd]) {
[(id<NSMenuDelegate>)r menuWillOpen:menu];
return;
}
r = r.nextResponder;
}
}
It assumes that a controller down the responder chain implements menuWillOpen:
let recognizer: UIGestureRecognizer
for recognizer in self.gestureRecognizers! {
if recognizer is DollarPGestureRecognizer {
recognizer.recognize()
self.clearAll()
}
}
how is if recognizer is DollarPGestureRecognizer means?
and also , recognize() function is declared in DollarPGesturRecognizer.h like
#interface DollarPGestureRecognizer : UIGestureRecognizer {
DollarP *dollarP;
NSMutableDictionary *currentTouches;
NSMutableArray *currentPoints;
NSMutableArray *points;
int strokeId;
}
- (void)recognize;
#end
how can i successfully do the recognize function?
Little unclear as to what you are asking but here goes:
First Question Answer) recognizer is DollarPGestureRecognizer returns true if recognizer is an instance of the DollarPGestureRecognizer class.
Second Question Answer) In order to use the DollarPGestureRecognizer you need to instantiate an instance of one and then add is as a gesture recognizer to the view where you want it to be active. So you could do something like this (as a rough example):
let dollarRecognizer = DollarPGestureRecognizer(target:self, action:"handleDollarGesture:"
myView.addGestureRecognizer(dollarRecognizer)
then define the handler in that target class (in this case self)
func handleDollarGesture(recognizer: DollarPGestureRecognizer) {
//Do Something
}
Side Note) If you want some help implementing your own custom UIGestureRecognizer you should look at the following article:
http://www.raywenderlich.com/104744/uigesturerecognizer-tutorial-creating-custom-recognizers
In a test Swift project, I am subclassing NSWindowController. My NSWindowController subclass is designed to work with a particular Nib file. It is desirable, then, that when my window controller is initialized, the nib file is automatically loaded by the window controller instance. In Objective-C, this was achieved by doing:
#implementation MyWindowController
- (id)init {
self = [super initWithWindowNibName:"MyWindowNib"]
if (self) {
// whatever
}
return self
}
#end
Now, in Swift this is not possible: init() cannot call super.init(windowNibName:), because the later is declared not as a designated initializer, but as a convenience one by NSWindowController.
How can this be done in Swift? I don't see a strightforward way of doing it.
P.S.: I have seen other questions regarding this topic, but, as long as I've been able to understand, the solutions all point to initialize the Window Controller by calling init(windowNibName:). Please note that this is not the desired beheaviour. The Window Controller should be initialized with init(), and it should be the Window Controller itself who "picks up" its Nib file and loads it.
If you use the init() just to call super.init(windowNibName:), you could instead just override the windowNibName variable.
override var windowNibName: String {
get {
return "MyWindowNib"
}
}
Then there should be no need to mess with the initializers.
You can create your own convenience initializer instead:
override convenience init() {
self.init(windowNibName: "MyWindowNib")
}
You should instead opt in to replacing all designated initializers in your subclass, simply delegating to super where appropriate. Confer https://stackoverflow.com/a/24220904/1460929
I have a problem from 3 days :( I want to hook in CLLocationManagerDelegate protocol this method:
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations
I tried everything but without success. I know how to hook into class or framework but I can't find a solution to hook a Delegate.
Please help me!
Thanks
Hooking requires you to provide objective-C class you would like to hook. This is what Class type is for. One way to get obj-c class is by name via objc_getClass function. But in your case as I understand it correctly you don't have the name. You want to hook every class that conforms to CLLocationManagerDelegate protocol and implements specific method. Here is what you can do.
You can obtain every registered obj-C class and search for those which conform toCLLocationManagerDelegate protocol like this:
static IMP original_didUpdateLocations;
void replaced_didUpdateLocations(id self, SEL _cmd, CLLocationManager* manager, NSArray* locations)
{
NSLog(#"%# did update locations to %#", manager, locations);
original_didUpdateLocations(self, _cmd, manager, locations);
}
...
#import <objc/runtime.h>
int numClasses = objc_getClassList(NULL, 0);
Class* list = (Class*)malloc(sizeof(Class) * numClasses);
objc_getClassList(list, numClasses);
for (int i = 0; i < numClasses; i++)
{
if (class_conformsToProtocol(list[i], #protocol(CLLocationManagerDelegate)) &&
class_getInstanceMethod(list[i], #selector(locationManager:didUpdateLocations:)))
{
MSHookMessageEx(list[i], #selector(locationManager:didUpdateLocations:), (IMP)replaced_didUpdateLocations, (IMP*)&original_didUpdateLocations);
}
}
free(list);
We need to know how many classes there is. objc_getClassList(NULL, 0) returns number of all registered classes.
Allocating memory with malloc(sizeof(Class) * numClasses) and filling it with objects of type Class using objc_getClassList(list, numClasses).
Searching through all these classes for those which conform to CLLocationManagerDelegate protocol and implement locationManager:didUpdateLocations: method. If we found one we are hooking it with our own implementation.
In our own implementation we are printing some debug message and calling original implementation before returning. Of course, you can do whatever you what, this is just an example.
Freeing allocated memory using free(list).