Xamarin Objective Sharpie Binding Delegate to Interface - xamarin

I’m trying to bind an objective-c library with a delegate
#protocol PKTokenFieldDelegate <UITextFieldDelegate>
-(void)tokenShouldChangeHeight:(CGFloat)height;
#optional
-(void)tokenFieldDidSelectToken:(PKToken*)token;
-(void)tokenFieldDidBeginEditing:(PKTokenField*)tokenField;
-(void)tokenFieldDidEndEditing:(PKTokenField*)tokenField;
#end
Sharpie output based on the walkthrough on xamarin developer site.
// #protocol PKTokenFieldDelegate <UITextFieldDelegate>
[BaseType (typeof (NSObject))]
[Model]
interface PKTokenFieldDelegate : IUITextFieldDelegate
{
// #required -(void)tokenShouldChangeHeight:(CGFloat)height;
[Abstract]
[Export ("tokenShouldChangeHeight:")]
void TokenShouldChangeHeight (nfloat height);
// #optional -(void)tokenFieldDidSelectToken:(PKToken *)token;
[Export ("tokenFieldDidSelectToken:")]
void TokenFieldDidSelectToken (PKToken token);
// #optional -(void)tokenFieldDidBeginEditing:(PKTokenField *)tokenField;
[Export ("tokenFieldDidBeginEditing:")]
void TokenFieldDidBeginEditing (PKTokenField tokenField);
// #optional -(void)tokenFieldDidEndEditing:(PKTokenField *)tokenField;
[Export ("tokenFieldDidEndEditing:")]
void TokenFieldDidEndEditing (PKTokenField tokenField);
}
This only creates an object that I can inherit from instead of creating an interface. I need to have this as an interface. What am I missing?
Thanks

I just had to change the [Model] to [Protocol] in order for this to work.
I also ran into a problem when the namespace and class name are the same that you get errors. Which is what got me down the road of changing Protocol to Model in the first place.

Related

How to use MvvmCross without storyboards in iOS?

When I was using MvvmCross 5, I coded all my views and avoided using storyboards for my iOS app by writing this in my AppDelegate:
[Register("AppDelegate")]
public class AppDelegate : MvxApplicationDelegate
{
private MvxIosViewPresenter viewPresenter;
public override UIWindow Window
{
get;
set;
}
/// <summary>
/// MvvmCross Mods:
/// - Creates a new presenter, which determines how Views are shown
/// - This example uses a standard presenter.
/// </summary>
/// <param name="application"></param>
/// <param name="launchOptions"></param>
/// <returns></returns>
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
// create a new window instance based on the screen size
Window = new UIWindow(UIScreen.MainScreen.Bounds);
// MvvmCross Mod Start---------------------------------------------
// This class will determine how Views are shown
this.viewPresenter = new MvxIosViewPresenter(this, Window);//new ViewPresenter(Window);
// Init the Setup object, which initializes App.cs
var setup = new Setup(this, this.viewPresenter);
setup.Initialize();
//this.viewPresenter.PresentModalViewController(new ListenViewController(), true);
// Use IoC to find and start the IMvxAppStart object
var startup = Mvx.Resolve<IMvxAppStart>();
startup.Start();
// MvvmCross Mod End--------------------------------------------------
// make the window visible
Window.MakeKeyAndVisible();
return true;
}
public class Setup : MvxIosSetup
{
public Setup(MvxApplicationDelegate appDelegate, IMvxIosViewPresenter presenter)
: base(appDelegate, presenter)
{
}
protected override IMvxApplication CreateApp()
{
return new Core.App();
}
protected override IMvxTrace CreateDebugTrace()
{
return new DebugTrace();
}
}
But in MvvmCross 6.4.2, MvxIosSetup does not have the base constructor that takes 2 arguments. Instead I have:
[Register(nameof(AppDelegate))]
public partial class AppDelegate : MvxApplicationDelegate<Setup, App>
{
}
public class Setup : MvxIosSetup<App>
{
}
How can I configure it so that I can code my views without storyboards?
EDIT
I created a very small sample app with 1 view model/controller, using MvvmCross 7. The ViewDidLoad method never gets called in my MainViewController. Can someone please tell me why? I put my code here:
https://github.com/sinight95/TestApp/tree/main/TestApp
Neither the Setup.cs nor AppDelegate.cs files have anything to do whether you are presenting a storyboard or not. It is usually all up to which Presentation Attributes you are applying to a ViewController.
However, there are some stuff set in the Info.plist that changes how MvvmCross expects things to be set up.
Now in your example App you've put up on GitHub, you can do the following to make it not use the storyboard:
Remove Main.storyboard
In Info.plist set Main interface to (none)
In Info.plist set launch images to LaunchScreen.storyboard
Remove the scene delegate stuff in Info.plist
Remove the constructor in MainViewController
What is the main issue here is that you are essentially telling iOS that the ViewController has a storyboard by supplying this constuctor:
public MainViewController() : base(nameof(MainViewController), null)
{
}
Also are telling iOS to use storyboards through the Info.plist with all the scene delegation stuff.
I've created a PR showing what needs to be changed to make it run without the storboard and show the blue background color you've set on your ViewController.
https://github.com/sinight95/TestApp/pull/1

Xamarin iOS binding library duplicated constructors issue

I have an issue with duplicated constructor in Xamarin iOS binding library with code generated by sharpie tool from third-party SDK code. Basicly C# generated interface is using NSFileHandle as a base type and SDK header file declares identical designated initializer in its subclass like in NSFileHandler so I'm getting "Member ... is already defined error" because now binding library is generating C# constructor twice - first time from the base class and the second from subclassed initializer.
Objective-C code:
#interface MyFileHandle : NSFileHandle
//...
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
//...
C# binding library code:
[BaseType(typeof(NSFileHandle))]
public interface MyFileHandle
{
//...
[Export("initWithCoder:")]
[DesignatedInitializer]
IntPtr Constructor(NSCoder coder);
//...
}
Binding library generated code (*.g.cs):
[Register("MyFileHandle", true)]
public unsafe partial class MyFileHandle : NSFileHandle {
//...
[CompilerGenerated]
[DesignatedInitializer]
[EditorBrowsable (EditorBrowsableState.Advanced)]
[Export ("initWithCoder:")]
public MyFileHandle (NSCoder coder) : base (NSObjectFlag.Empty)
{
//...
}
[Export ("initWithCoder:")]
[DesignatedInitializer]
[CompilerGenerated]
public MyFileHandle (NSCoder coder)
: base (NSObjectFlag.Empty)
{
//...
}
//...
}
How can I prevent binding library from generating constructors twice thus get rid of the error?
It seems that you can simply remove duplicated Constructor from ApiDefinitions.cs as #SushiHangover suggested.

Obj-C Private Method Compiler Warnings

Private methods are a useful construct to keep code organised within class boundaries. An example being the organisation of long winded Quartz 2d instructions in a custom UIView subclass. I am able to include such methods in '.m' files with no declaration in '.h'. A working example from a UIView subclass '.m' file reads:
-(void)DoSomethingPrivate { //Not declared in interface
NSLog(#"Does this print a private function?");
}
- (id)initWithFrame:(CGRect)frame //Declared in inherited interface
{
self = [super initWithFrame:frame];
if (self) {
[self DoSomethingPrivate]; //Error: 'Instance method not found'
} //... but it works anyway.
return self;
}
My problem is that the compiler generates the warning "Instance method '-DoSomethingPrivate' not found (return type defaults to 'id')" on the line calling the private function. I'm aware from responses to this question that I can use a 'no name' interface category to 'hide' private method declarations.
However, when I review Apple sample code SimpleStocks, file "SimpleStockView.m", it contains a private function which is neither declared in a no-name category interface, nor does it generate compiler warnings:
//Private Function
- (void)drawRadialGradientInSize:(CGSize)size centeredAt:(CGPoint)center {
...
}
//Is called by this function...
- (UIImage *)patternImageOfSize:(CGSize)size {
...
//The next line doesn't generate any warnings!
[self drawRadialGradientInSize:size centeredAt:center];
...
}
I'd be grateful if anyone can shed any light on how Apple's sample-code private-methods appear to escape compiler checks, so I may avoid having to maintain a 'no-name' category header with all my private methods.
Many thanks.

Monotouch: understand the delegate mechanism pattern

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!

How to declare a function in Cocoa after the function using it?

I'm slowly building my application to a working state.
I'm using two functions called setCollection and addToCollection. These functions both accept NSArray as input.
I also have a function called add in which I use both of these functions. When I try to compile, Xcode shows an error:
'setCollection' undeclared (first use in this function)
I guess this has to do with the function called being defined below the active function. Another guess would be that the functions should be globalized to be useable inside my add function.
I'm normally a php coder. the way Php handles this is the first one. The functions called should be before the functions using them, because otherwise they just don't exist. Is there a way to make functions still to come available at runtime, or should I rearrange all functions to make them function properly?
You can declare functions ahead of time as follows:
void setCollection(NSArray * array);
void addToCollection(NSArray * array);
//...
// code that calls setCollection or addToCollection
//...
void setCollection(NSArray * array)
{
// your code here
}
void addToCollection(NSArray * array)
{
// your code here
}
If you are creating a custom class, and these are member functions (usually called methods in Objective-C) then you would declare the methods in your class header and define them in your class source file:
//MyClass.h:
#interface MyClass : NSObject
{
}
- (void)setCollection:(NSArray *)array;
- (void)addToCollection:(NSArray *)array;
#end
//MyClass.m:
#import "MyClass.h"
#implementation MyClass
- (void)setCollection:(NSArray *)array
{
// your code here
}
- (void)addToCollection:(NSArray *)array
{
// your code here
}
#end
//some other source file
#import "MyClass.h"
//...
MyClass * collection = [[MyClass alloc] init];
[collection setCollection:someArray];
[collection addToCollection:someArray];
//...
If your functions are global (not part of a class), you just have to put the declaration before the use, just like eJames suggests.
If your functions actually are methods (part of a class), you have to declare an anonymous category of your class before the implementation and put your method declarations in this interface:
#interface Myclass()
- (void) setCollection:(NSArray*)array;
- (void) addToCollection:(NSArray*)array;
#end
#implementation Myclass
// Code that calls setCollection or addToCollection
- (void) setCollection:(NSArray*)array
{
// your code here
}
- (void) addToCollection:(NSArray*)array
{
// your code here
}
#end
This way, you don't need to expose your functions in the main interface of MyClass.

Resources