CATextlayer with AttributedString in MonoTouch - xamarin.ios

I am trying to create a "label" with different styles on different words, kind of like described here.
The problem is - as far as I can see - the MonoTouch implementation of UATextLayer does not accept assigning an NSAttributedString to the String property since the String property has the type string.
Is this an error in the implementation or is there another way of doing this?
(Yes, I am aware I can add separate labels - but I would rather not when there is a better solution).
EDIT (in response to the answer from Miguel):
After changing to GetHandler and correcting to "void_objc_msgSend_IntPtr" instead of "void_objc_msgSend_IntPrt" the code in the answer compiles and runs, but it doesn't quite work anyway (I was a bit fast in marking it as the answer).
No errors are thrown, but the text doesn't show.
Code:
string _text="Example string";
if(_textLayer==null) {
_textLayer = new CATextLayer();
_textLayer.Frame = new RectangleF(50,698,774,50);
_textLayer.Wrapped=true;
_textLayer.ForegroundColor=UIColor.White.CGColor;
_textLayer.BackgroundColor=UIColor.Clear.CGColor;
Layer.AddSublayer(_textLayer);
}
//_textLayer.String=_text;
CTFont _font=new CTFont("MarkerFelt-Thin",48);
CTStringAttributes _attrs=new CTStringAttributes();
_attrs.Font=_font;
_attrs.ForegroundColor = UIColor.White.CGColor;
var nsa = new NSAttributedString(_text);
Messaging.void_objc_msgSend_IntPtr(
_textLayer.Handle,
Selector.GetHandle("string"),
nsa.Handle);
If I uncomment the _textLayer.String=_text I see the text (but without attributes of course), so the problem is not with the layer.

For now, you can try:
using MonoTouch.ObjCRuntime;
var caTextLayer = new CATextLayer ();
var nsa = new NSAttributedString ();
[..]
Messaging.void_objc_msgSend_IntPrt (
caTextLayer.Handle,
Selector.sel_registerName ("string"),
nsa.Handle);
Alternatively, can you download this preview of the upcoming version:
http://tirania.org/tmp/monotouch.dll
It implements a property AttributedString in CATextLayer that you can set.

Related

Haxe - use string as variable name with DynamicAccess

I am trying to use a string ('npcName') as a variable name. So far I have tried casting dialogMap into a DynamicAccess object, but it gives me the error 'Invalid array access' when I try this:
var npcName:String = 'TestNPC';
var casted = (cast Registry.dialogMap:haxe.DynamicAccess<Dynamic>);
var tempname = casted[root.npcName[0].message];
trace(tempname);
'dialogMap' is an empty map which I want to fill like so:
Registry.dialogMap['message'] = root.npcName[0].message;
How can I use npcName, a string, in the above line of code? Is there a way to transform the string into something usable? Any help would be appreciated.
The haxe.DynamicAccess doesn't have array access (like map[key]), but is an abstract type for working with anonymous structures that are intended to hold collections of objects by the string key. It is designed to work with map.get(key) and map.set(key). It is basically a nicer wrapper around Reflect.field and Reflect.setField and does some safety checks with Reflect.hasField.
var variable = "my_key";
var value = 123;
var dynamicMap = new haxe.DynamicAccess<Dynamic>();
dynamicMap.set(variable, value);
I'm noticing you are doing very much cast and dynamic, so untyped code, which is a bit of contradiction in a typed language. What is the actual type of dialogMap?
Not sure you are aware of it but, Haxe has its own maps, which are fully typed, so you don't need casts.
var map = new Map<String, Int>();
map[variable] = value;
I think this article helps understanding how to work with dynamic (untyped) objects.
Tip; for testing such small functionalities you can doodle around on the try.haxe site : http://try.haxe.org/#4B84E
Hope this helps, otherwise here is some relevant documentation:
http://api.haxe.org/haxe/DynamicAccess.html
https://haxe.org/manual/std-reflection.html
https://haxe.org/manual/types-dynamic.html
http://code.haxe.org/category/beginner/string-variable-reflection.html

Monotouch Dialog: Styling Elements

I'm using Dialog and would like to style all my cells. I have a background image, and in the samples I can see how you can use a StyledStringElement to use that image.
However, in real use some sections use other elements. For example the last element in one section is a RootElement - but it has no BackgroundUri property to set. The same would go for boolean elements.
I found this question - What's the best way to customise all monotouch.dialog TableViewCells to the same style (Background, etc..)? which is a similar question a year and a half back. The UIAppearance styling mentioned does exist for tablecells but does not work with MTDialog. krtrego's answer to this In monotouch.dialog can RootElement be easily styled? question purports to do the job, but no styling occurred when I implemented it.
Is there now any improved way to do this? Implementing my own 'styled' versions of these other control types would be a big effort and looking at the styledstringelement this is beyond my current skill level.
Here's an example of what I'd like to achieve (the shadow below the 'tags' cell, but the element is actually a RootElement with a set of radio options beneath it). Removing the default grey lines etc is easy enough, but putting a subtle shadow on the bottom cell of each section is what I cannot work out.
Many thanks!
PS. With a normal MTDialog screen with cell backgrounds and borders removed, there is a subtle white shadow/line beneath each section as it is. If I could just recolour that I'd be a long way to where I want to be...
Subclassing the element will let you style it via overriding the GetCell method, but that gets pretty tedious. The best solution I have come across is to to make a custom DialogViewController by subclassing it, and overriding the CreateSizingSource method with your own SizingSource and GetCell() methods using the images you want for each scenario of a cell (top, middle, bottom, alone). Its a bit of code and my example wont handle uneven rows, but it is the only solution I have seen that does not modify the MT.D source code.
Here is what you would override in your DialogViewController subclass:
public override Source CreateSizingSource(bool unevenRows)
{
return new CustomSource(unevenRows);
}
Then you would make a custom source class:
public class CustomSource : Source
{
public CustomSource(DialogViewController parent) : base (parent)
{
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
var theCell = base.GetCell(tableView, indexPath);
if (RowsInSection(tableView, indexPath.Section) == 1) //use one with top and bottom rounded
{
theCell.BackgroundView = new UIImageView(Theme.CellBackgroundFull);
theCell.SelectedBackgroundView = new UIImageView(Theme.CellBackgroundFullActive);
} else if (indexPath.Row == 0) //top only
{
theCell.BackgroundView = new UIImageView(Theme.CellBackgroundTop);
theCell.SelectedBackgroundView = new UIImageView(Theme.CellBackgroundTopActive);
} else if (indexPath.Row+1 == RowsInSection(tableView, indexPath.Section)) // bottom only
{
theCell.BackgroundView = new UIImageView(Theme.CellBackgroundBottom);
theCell.SelectedBackgroundView = new UIImageView(Theme.CellBackgroundBottomActive);
} else //anything in the middle
{
theCell.BackgroundView = new UIImageView(Theme.CellBackgroundMiddle);
theCell.SelectedBackgroundView = new UIImageView(Theme.CellBackgroundMiddleActive);
}
return theCell;
}
}
Theme is just a static class that returns UIImages, similar to the example Field Service app from Xamarin. So here I have made 8 images total. 4 to represent the top, middle, bottom and alone for an element. Each has different rounded corners to appear correct. And then a "highlighted" version of each for when its touched.
The big drawback here is you have to do this for every different styled controller you would need. If you are ok with modifying the MT.D source code, you can get a different solution that will allow you to control it at the Section level here: http://fastchicken.co.nz/2012/05/20/earnest-debrief-visual-styles-in-ios-apps-uiappearence-custom-sections-in-monotouch-dialog/
Which has the same effect, but you only need to subclass Section for each different style, which makes including multiple styles in one Root easier. A pull request was made for this change, but Miguel favored the first solution instead, seen here: https://github.com/migueldeicaza/MonoTouch.Dialog/pull/180

Undo operation with NSUndoManager in rich UITextView (iOS 6)

I want to change some or all of the attributed text of a rich UITextView (iOS 6), and allow the user to undo the change.
After reading NSUndoManager documentation, I tried the first way:
“Simple undo” based on a simple selector with a single object argument.
I expected an undo operation to be as simple as:
Declare this method:
- (void)setAttributedStringToTextView:(NSAttributedString *)newAttributedString {
NSAttributedString *currentAttributedString = self.textView.attributedText;
if (! [currentAttributedString isEqualToAttributedString:newAttributedString]) {
[self.textView.undoManager registerUndoWithTarget:self
selector:#selector(setAttributedStringToTextView:)
object:currentAttributedString];
[self.textView.undoManager setActionName:#"Attributed string change"];
[self.textView setAttributedText:newAttributedString];
}
}
Change the text in my UITextView by calling:
[self setAttributedStringToTextView:mutableAttributedString];
But after doing that, NSUndoManager says it cannot undo.
NSLog(#"Can undo: %d", [self.textView.undoManager canUndo]);
// Prints: "Can undo: 0"
So I tried the second way:
“Invocation-based undo” which uses an NSInvocation object.
Declare this:
- (void)setMyTextViewAttributedString:(NSAttributedString *)newAttributedString {
NSAttributedString *currentAttributedString = [self.textView attributedText];
if (! [currentAttributedString isEqualToAttributedString:newAttributedString]) {
[[self.textView.undoManager prepareWithInvocationTarget:self]
setMyTextViewAttributedString:currentAttributedString];
[self.textView.undoManager setActionName:#"Attributed string change"];
[self.textView setAttributedText:newAttributedString];
}
}
and change the text with:
[self setMyTextViewAttributedString:mutableAttributedString];
After that, NSUndoManager also says it cannot undo.
Why?
Note that the user is editing the UITextView when triggering the code that will change the attributed text.
A workaround would be to replace the text directly via UITextInput protocol method. The following method is quite convenient, but I haven't found an equivalent for NSAttributedString. Did I miss it?
- (void)replaceRange:(UITextRange *)range withText:(NSString *)text
An hack suggested here is to simulate a paste operation. If possible, I would prefer to avoid this (no reason yet, just feels too dirty to not come back bite me later).
I'm kinda still in shock this works. I posted the same answer over here UITextView undo manager do not work with replacement attributed string (iOS 6).
- (void)applyAttributesToSelection:(NSDictionary*)attributes {
UITextView *textView = self.contentCell.textView;
NSRange selectedRange = textView.selectedRange;
UITextRange *selectedTextRange = textView.selectedTextRange;
NSAttributedString *selectedText = [textView.textStorage attributedSubstringFromRange:selectedRange];
[textView.undoManager beginUndoGrouping];
[textView replaceRange:selectedTextRange withText:selectedText.string];
[textView.textStorage addAttributes:attributes range:selectedRange];
[textView.undoManager endUndoGrouping];
[textView setTypingAttributes:attributes];
}

Resharper Code Pattern for IDisposable not inside using

I'd like to know how to build a Resharper (6.1) code pattern to search and replace the following issues:
var cmd = new SqlCommand();
cmd.ExecuteNonQuery();
and turn it into this:
using (var cmd = new SqlCommand())
{
cmd.ExecuteNotQuery();
}
and:
StreamReader reader = new StreamReader("myfile.txt");
string line = reader.Read();
Console.WriteLine(line);
becomes:
using (StreamReader reader = new StreamReader("file.txt"))
{
string line = reader.ReadLine();
Console.WriteLine(line);
}
EDIT: Thanks for the answers, but I'm looking for anything that implements IDisposable
Search pattern:
var $cmd$ = $sqlcommand$;
$cmd$.ExecuteNonQuery();
Replace pattern:
using (var $cmd$ = $sqlcommand$)
{
$cmd$.ExecuteNonQuery();
}
where cmd = identifier
and sqlcommand = expression of type System.Data.SqlClient.SqlCommand
It looks like what you're really after is an inspection mechanism that goes off looking for IDisposable objects and ensures they are disposed. If that's the case, I doubt custom patterns would be the right approach - after all, what if you do call Dispose() a few lines later?
One way to implement this is by using the ReSharper SDK. In fact, one of the examples the SDK comes with is a PowerToy which implements IDisposable on a particular class, so you could take that code as a foundation for possible analysis of usage.
Use the Search with Pattern tool under the ReSharper | Find menu.
In the Search pattern make sure you have C# selected and enter the code you're searching for in the box. Click the Replace button in the top-right, and enter the code you want to replace it with in the Replace pattern box.
You can save the search and replace pattern and R# will use it for subsequent code analysis should you so desire. You can also add additional patterns in R# Options under Code Inspection | Custom Patterns.

iOS 5 + GLKView: How to access pixel RGB data for colour-based vertex picking?

I've been converting my own personal OGLES 2.0 framework to take advantage of the functionality added by the new iOS 5 framework GLKit.
After pleasing results, I now wish to implement the colour-based picking mechanism described here. For this, you must access the back buffer to retrieve a touched pixel RGBA value, which is then used as a unique identifier for a vertex/primitive/display object. Of course, this requires temporary unique coloring of all vertices/primitives/display objects.
I have two questions, and I'd be very grateful for assistance with either:
I have access to a GLKViewController, GLKView, CAEAGLLayer (of the GLKView) and an EAGLContext. I also have access to all OGLES 2.0
buffer related commands. How do I combine these to identify the color
of a pixel in the EAGLContext I'm tapping on-screen?
Given that I'm using Vertex Buffer Objects to do my rendering, is there a neat way to override the colour provided to my vertex shader
which firstly doesn't involve modifying buffered vertex (colour)
attributes, and secondly doesn't involve the addition of an IF
statement into the vertex shader?
I assume the answer to (2) is "no", but for reasons of performance and non-arduous code revamping I thought it wise to check with someone more experienced.
Any suggestions would be gratefully received. Thank you for your time
UPDATE
Well I now know how to read pixel data from the active frame buffer using glReadPixels. So I guess I just have to do the special "unique colours" render to the back buffer, briefly switch to it and read pixels, then switch back. This will inevitably create a visual flicker, but I guess it's the easiest way; certainly quicker (and more sensible) than creating a CGImageContextRef from a screen snapshot and analyzing that way.
Still, any tips as regards the back buffer would be much appreciated.
Well, I've worked out exactly how to do this as concisely as possible. Below I explain how to achieve this and list all the code required :)
In order to allow touch interaction to select a pixel, first add a UITapGestureRecognizer to your GLKViewController subclass (assuming you want tap-to-select-pixel), with the following target method inside that class. You must make your GLKViewController subclass a UIGestureRecognizerDelegate:
#interface GLViewController : GLKViewController <GLKViewDelegate, UIGestureRecognizerDelegate>
After instantiating your gesture recognizer, add it to the view property (which in GLKViewController is actually a GLKView):
// Inside GLKViewController subclass init/awakeFromNib:
[[self view] addGestureRecognizer:[self tapRecognizer]];
[[self tapRecognizer] setDelegate:self];
Set the target action for your gesture recognizer; you can do this when creating it using a particular init... however I created mine using Storyboard (aka "the new Interface Builder in Xcode 4.2") and wired it up that way.
Anyway, here's my target action for the tap gesture recognizer:
-(IBAction)onTapGesture:(UIGestureRecognizer*)recognizer {
const CGPoint loc = [recognizer locationInView:[self view]];
[self pickAtX:loc.x Y:loc.y];
}
The pick method called in there is one I've defined inside my GLKViewController subclass:
-(void)pickAtX:(GLuint)x Y:(GLuint)y {
GLKView *glkView = (GLKView*)[self view];
UIImage *snapshot = [glkView snapshot];
[snapshot pickPixelAtX:x Y:y];
}
This takes advantage of a handy new method snapshot that Apple kindly included in GLKView to produce a UIImage from the underlying EAGLContext.
What's important to note is a comment in the snapshot API documentation, which states:
This method should be called whenever your application explicitly
needs the contents of the view; never attempt to directly read the
contents of the underlying framebuffer using OpenGL ES functions.
This gave me a clue as to why my earlier attempts to invoke glReadPixels in attempts to access pixel data generated an EXC_BAD_ACCESS, and the indicator that sent me down the right path instead.
You'll notice in my pickAtX:Y: method defined a moment ago I call a pickPixelAtX:Y: on the UIImage. This is a method I added to UIImage in a custom category:
#interface UIImage (NDBExtensions)
-(void)pickPixelAtX:(NSUInteger)x Y:(NSUInteger)y;
#end
Here is the implementation; it's the final code listing required. The code came from this question and has been amended according to the answer received there:
#implementation UIImage (NDBExtensions)
- (void)pickPixelAtX:(NSUInteger)x Y:(NSUInteger)y {
CGImageRef cgImage = [self CGImage];
size_t width = CGImageGetWidth(cgImage);
size_t height = CGImageGetHeight(cgImage);
if ((x < width) && (y < height))
{
CGDataProviderRef provider = CGImageGetDataProvider(cgImage);
CFDataRef bitmapData = CGDataProviderCopyData(provider);
const UInt8* data = CFDataGetBytePtr(bitmapData);
size_t offset = ((width * y) + x) * 4;
UInt8 b = data[offset+0];
UInt8 g = data[offset+1];
UInt8 r = data[offset+2];
UInt8 a = data[offset+3];
CFRelease(bitmapData);
NSLog(#"R:%i G:%i B:%i A:%i",r,g,b,a);
}
}
#end
I originally tried some related code found in an Apple API doc entitled: "Getting the pixel data from a CGImage context" which required 2 method definitions instead of this 1, but much more code is required and there is data of type void * for which I was unable to implement the correct interpretation.
That's it! Add this code to your project, then upon tapping a pixel it will output it in the form:
R:24 G:46 B:244 A:255
Of course, you should write some means of extracting those RGBA int values (which will be in the range 0 - 255) and using them however you want. One approach is to return a UIColor from the above method, instantiated like so:
UIColor *color = [UIColor colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha/255.0f];

Resources