Getting an Objective-C class name without having a MonoTouch managed class
Asked Answered
S

4

6

I can't for the life of me figure out how to translate this into MonoTouch. Any takers?

for(UIView* view in cell.subviews)
{
    if([[[view class] description] isEqualToString:@"UITableViewCellReorderControl"])
    {
    }
}

Mostly it's the View Class Description that has me stumped...

Sweeten answered 25/7, 2012 at 17:32 Comment(0)
R
7

In general, @Monoman's is solution is the right way to find instances of a particular class, even when you're looking for a CocoaTouch class from within a MonoTouch program.

Sometimes, however, you'll discover that there's an internal CocoaTouch class that's not exposed in MonoTouch (or even in the iOS Platform headers). In such a case, you'll have to resort to tricks like @poupou is doing.

Unfortunately, his answers won't work here either. view.GetType() is returning the most-derived MonoTouch type that each Subview implements, and then ToString(), Class.Name, or even @selector("description") each operate on the wrong type and give an overly generic answer ("UIView" in this case).

In order to make this work, you'll have to go one layer deeper under the covers than @poupou proposed.

// ** using MonoTouch.ObjCRuntime; **

private string GetClassName (IntPtr obj) {
    Selector description = new Selector ("description");
    Selector cls = new Selector ("class");
    IntPtr viewcls = Messaging.IntPtr_objc_msgSend (obj, cls.Handle);
    var name = NSString.FromHandle (Messaging.IntPtr_objc_msgSend (viewcls, description.Handle));
    return name;
}

Here's an alternative that's not a whole lot more fiddly (maybe even less?), but will work on any Objective-C class, not just those that respond to NSObject's description message:

// ** using System.Runtime.InteropServices; **

[DllImport ("/usr/lib/libobjc.dylib")]
private static extern IntPtr object_getClassName (IntPtr obj);

private string GetClassName (IntPtr obj) {
    return Marshal.PtrToStringAuto(object_getClassName(obj));
}

It's actually surprising and a little bit sad that MonoTouch doesn't provide an import for object_getClassName() already.

You would use either one of those like so:

foreach (UIView view in cell.Subviews) {
    if (GetClassName(view.Handle) == "UITableViewCellReorderControl") {
    }
}

Big fat disclaimer: Pretty much any time you're resorting to tricks like these, you're relying on CocoaTouch implementation details that Apple reserves the right to change. Rule of thumb: if you can do what you want with @Monoman's solution, you're probably safe. Otherwise, you're taking matters in to your own hands.

Rhino answered 26/7, 2012 at 6:3 Comment(0)
M
5

I'm not using Monotouch currently, but shouldn't you just check the managed type of the subview?

   foreach (UIView view in cell.subviews)
   {
     if (view is UITableViewCellReorderControl) {}
   }
Mailand answered 25/7, 2012 at 17:41 Comment(4)
Yep, the original direct is mostly doing: view.GetType ().ToString () where [view class] returns the type and description return the name (description is also used, by default, for NSObject.ToString(). However the adapted translation from @Mailand is better to represent what this means.Pinhead
And if System.Linq is available you could abbreviate it further: foreach(var view in cell.subViews.OfType<UITableViewCellReorderControl>())Hughie
foreach (UIView subView in cell.Subviews) { if(subView is UITableViewCellReorderControl) { } } throws error: Error CS0246: The type or namespace name `UITableViewCellReorderControl' could not be found. Are you missing a using directive or an assembly reference? (CS0246)Sweeten
This won't work in this case because UITableViewCellReorderControl is a deep UIKit internal.Rhino
P
5

The handling of Objective-C Class is mostly hidden in the API provided by MonoTouch. It's mostly the System.Type and description is mostly the ToString - but that won't show some internals.

Here's a few things (simple to more complex) you can try:

Calling ToString on the view instance might include the original class name (as part of the description) - but it's not the same as calling description of the class itself.

 foreach (UIView view in cell.Subviews) {
    if (view.ToString ().Contains ("UITableViewCellReorderControl")) {
    }
 }

Next try to see if Name on Class is identical to description.

foreach (UIView view in cell.Subviews) {
   MonoTouch.ObjCRuntime.Class c = new MonoTouch.ObjCRuntime.Class (view.GetType ());
   if (c.Name == "UITableViewCellReorderControl") {
   }
}

Finally really call the description selector on the Class instance. Something like (untried):

MonoTouch.ObjCRuntime.Selector description = new MonoTouch.ObjCRuntime.Selector ("description");
foreach (UIView view in cell.Subviews) {
   MonoTouch.ObjCRuntime.Class c = new MonoTouch.ObjCRuntime.Class (view.GetType ());
   var name = NSString.FromHandle (MonoTouch.ObjCRuntime.Messaging.IntPtr_objc_msgSend (c.Handle, description.Handle));
   if (name == "UITableViewCellReorderControl") {
   }
}
Pinhead answered 25/7, 2012 at 20:22 Comment(2)
I fixed up and tested your third one. None of these capture the types in the OP's case. I've added an answer with a couple of implementations that I've tested to work.Rhino
Works great! But now I have to figure out where to implement so that the cells in the table get a new location of the ReorderControl. If I put it in WillDisplay, everything looks good until I actually move a cell. Then all cells that move revert back to original control placement. Any thoughts on which Method will update the setting after a move?Sweeten
S
-1

This works for me:

foreach(var subview in this.ContentView.Superview.Subviews)
{
    if(subview.Class.Name.EndsWith("UITableViewCellReorderControl"))
    {
    }
}
Spread answered 14/1, 2016 at 15:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.