Showing posts with label Code. Show all posts
Showing posts with label Code. Show all posts

Thursday, September 16, 2010

Capturing part of an image from a UIImageView

I have a set of views over a picture and I wanted to pick the part of the picture under the views.  I found some code on stack overflow that helped me extract the part of the photo I wanted, however when I used it, it seemed to be scaled improperly and showed up strangely.

I finally figured out that the reason was because the UIImageView scales the image, and I was using the view coordinates of the part I was interested to try and get the subimage from the picture.  This means that when I'm choosing a number from 0-320 in the view and grabbing those coordinates from a 1200x1600 image.  This meant that I was always in the top left part of the picture.

The following code seems pretty close (it might still be a little off) but at least it grabs stuff in the right vicinity.  I  wanted to solve this problem even though I'm currently planning to actually abandon the 'grab' screen and put on separate dialog approach.  Instead I'm going to create an overlay and place it on top of the current view, allowing precise dynamic changes to occur.


- (UIImage*) getView:(CGRect )area
{
    if (self.image==nil)
        return nil;
    if (self.image.image==nil)
        return nil;
    UIGraphicsBeginImageContext(area.size);
    // we are using aspect fit, so we will be using which ever is the larger of these two scale factors.
    float widthScaleFactor=self.image.bounds.size.width/self.image.image.size.width;
    float heightScaleFactor=self.image.bounds.size.height/self.image.image.size.height;
    
    
    float scaleFactor=widthScaleFactor;
    NSLog(@"scale factors are height=%f width=%f",widthScaleFactor,heightScaleFactor);
    if (widthScaleFactor
        scaleFactor=heightScaleFactor;
    
    CGRect drawRect=CGRectMake(-area.origin.x/scaleFactor, -area.origin.y/scaleFactor,
                               self.image.image.size.width,self.image.image.size.height);

    UIImageOrientation currentOrient=self.image.image.imageOrientation;
    
    CGRect destRect=CGRectMake(0, 0, area.size.width, area.size.height);
    CGContextRef context=UIGraphicsGetCurrentContext();
    CGContextClipToRect(context, destRect);
    [self.image.image drawInRect:drawRect];
    UIImage* im=UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return im;
}

Friday, March 26, 2010

The list edit form

The next form is the core of managing my lists. It dispays the name of the list and the category and allows direct editing of these features. Categories are intended to be use for filtering and sorting so that a larger number of lists can be used.

Next a series of large friendly buttons allow transfer to other areas:
Edit list
This brings up the current contents of the list the user can then delete words in bulk or add new words.

Merge from other lists
This will allow the user to select one or more other lists. After selecting these lists a dialog will be brought up showing the combined set of words in those lists. The user can then select which words they want. Select and clear all will be available.

Merge to list
This will bring up a dialog that allows the user to select words to merge. Once done a dialog will be brought up allowing the user to select lists to send these words to.

A common factor in these dialogs is the selection of lists.  The core feature of this application vs other sight word applications is intended to be the easy manipulation of lists.

Below is the initial cut of the dialog.  After I get the application working I intend to do an 'aesthetic' run-through to add landscape capability and to make it look prettier.  This is the primary reason I didn't do this as a table, I think it is easier to implement as a standard dialog, and it offers more freedom to make modifications.



Now this screenshot isn't the most attractive dialog, but it includes the essential information.  

I do intend to clean it up prior to release, and to also make another .nib file that contains a landscape version of the dialog.












I also refactored the sight word display, since I am having it initiated from two locations, I made a static method on the SightWordDisplay class that shows the dialog.   I also intend to clean up this some more, I'm not satisfied with my 'singleton' sight word state.

Saturday, March 13, 2010

Sight Words: Select Suggested Items

The Select Suggested words dialog is a very handy one.  It has some nice features.


  • There is a search bar that allows you to enter text.  Every time a character is entered, a fetchcontroller is updated with a new search criteria.
  • When an item is selected, it will fully populate the search bar.
  • When the add button is selected, it will create a GUI image of the search bar and animate it going towards the add button.
  • It is fairly generic.  It takes as parameters the table, and search key allowing it to hit any single table query for selecting/searching items.  When an item is selected a delegate is called with the selection data.
  • When the keyboard is displayed the list shrinks so that it doesn't go behind the keyboard.
It has a few flaws as well
  • These nice features are embedded in a special purpose class.
  • The add button is a standard button next to the search bar.  This doesn't look as nice as the standard (and easier) adding a button the navigation controller.
  • The .xib file is a problem when using this library.  I had it go out of date, and had to manually copy changes to the main project.  There probably is a better way to send xib files to a main project, but I haven't figured it out yet.



The original source and .xib file can be found at:

Original XIB file
Some Choice tidbits:
Shrinking the view


In viewDidLoad place the following code:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardAppearing:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDisappearing:) name:UIKeyboardWillHideNotification object:nil];

and then add the following two methods.   Note that suggestions is the outlet for the list being displayed.

- (void)keyboardDisappearing:(NSNotification *)notification {
suggestions.frame=originalSize;
}

- (void)keyboardAppearing:(NSNotification *)notification {
NSDictionary *keys=[notification  userInfo];
NSValue *value=[keys objectForKey:UIKeyboardBoundsUserInfoKey];
CGRect bounds;
[value getValue:&bounds];
CGRect myFrame=[suggestions frame];
originalSize=myFrame;
suggestions.frame=CGRectMake(myFrame.origin.x,myFrame.origin.y,myFrame.size.width,
myFrame.size.height-bounds.size.height);
}






Delegating item creation and actions:
By defining a simple protocol and ensuring a delegate exists we can delegate out the selection of an
existing item and also the actual action that takes place upon selection.

@protocol SuggesionActionHandler<NSObject>

- (id)   createNewItem:(NSString *) searchValue;
- (void) doActionForItem:(id) item;

@end

Adding an item

Adding an item: Note that this combines the animation and action into the same area.  It really should be separated out.  I'm not sure I like how I search for the currently existing item.  At the very least it should be broken out into another method.

- (IBAction) addCurrentItem:(id) sender
{
NSManagedObject *matchingItem=nil;  
NSString *currentText=searchBar.text;
UIGraphicsBeginImageContext(searchBar.bounds.size);
[searchBar.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

UIImageView *imageView=[[UIImageView alloc] initWithImage :viewImage];
imageView.frame=searchBar.frame;
[self.view addSubview:imageView];
[UIView beginAnimations:nil context:imageView];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.25];
[imageView setFrame:addButton.frame];

// Search for the existing item if one.
if (fetchController!=nil)
{
if (fetchController.sections!=nil)
{
for (id<NSFetchedResultsSectionInfo> sectionInfo in fetchController.sections)
{
for (NSManagedObject *object in [sectionInfo objects])
{
NSString *keyForSearch=[object valueForKey:self.searchVariable];
if ([keyForSearch isEqualToString: currentText])

{
matchingItem=object;
break;
}
}
if (matchingItem!=nil)
break;
}
}
}
if (delegate!=nil)
{
if (matchingItem==nil)
{
matchingItem=[delegate createNewItem:currentText];
}
[delegate doActionForItem:matchingItem];
}
else
{
NSLog(@"Delegate is nil, no action can be performed ");
}


[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
[UIView commitAnimations];

}


Updating a Fetch controller with a search predicate:
This was slightly tricky, since the examples assumed a hardcoded search variable.  I had to format the string twice so that I could build the format string with the field name prior to doing substitution.




NSFetchRequest *request=[[NSFetchRequest alloc] init];


NSEntityDescription *entity=[NSEntityDescription entityForName:managedTable 
 
  inManagedObjectContext:coreControl.managedObjectContext];
[request setEntity:entity];
[request setFetchBatchSize:20];
NSSortDescriptor *sortDesc=[[NSSortDescriptor alloc] initWithKey:searchVariable ascending:YES];
NSArray *sortDescs=[[NSArray alloc] initWithObjects:sortDesc,nil];
NSString *searchString=[searchText stringByAppendingString:@"*"];
NSString *predicateFormat=[[NSString allocinitWithFormat:@"%@ like[cd] %%@",searchVariable];
NSLog(@"Searching table %@ %@ for %@",managedTable,searchVariable,searchString);
NSPredicate *predicate = [NSPredicate
  predicateWithFormat:predicateFormat,
 
  searchString];
    NSLog(@"Pred Desc %@", [predicate description]);
[request setPredicate:predicate];
[request setSortDescriptors:sortDescs];
if (fetchController!=nil)
{
  [fetchController release];
}
self.fetchController=[[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:coreControl.managedObjectContext sectionNameKeyPath:nil cacheName:managedTable];

self.fetchController.delegate=self;
NSError *error;
[self.fetchController performFetch:&error] ;
// NSLog(@"Unresolved error %@", error);

// NSLog(@"Fetch count %d",fetchController.sections);
[self.suggestions reloadData];
[predicateFormat release];
[request release];
[sortDesc release];
[sortDescs release];

Monday, February 1, 2010

Creating an array from a set

I'm working with Core Data, and ran into the problem that I need ordered data from a result set from Core Data. the difficulty is that it's not a primary query, but from a relationship. My solution was to create a category for NSSet that allows me to create an ordered array (based on a sort criteria):

// An extension of NSSet to get a sorted array from that set.


@interface NSSet(ArrayMethods)

- (NSArray*) getSortedArray:(NSString*) primaryKey;

@end


@implementation NSSet(ArrayMethods)

- (NSArray*) getSortedArray:(NSString*) primaryKey

{

NSSortDescriptor *sortDesc =

[[NSSortDescriptor alloc] initWithKey:primaryKey

ascending:YES

selector:@selector(localizedCaseInsensitiveCompare:) ];

NSArray *descArray=[[NSArray alloc] initWithObjects:sortDesc,nil] ;

NSArray *returnValue=[[self allObjects] sortedArrayUsingDescriptors:descArray] ;

[descArray release];


return returnValue ;

}

@end


There are probably better ways to do this, and I might find a memory error in there at some point in time,(although I think I have it right).