Showing posts with label Debugging. Show all posts
Showing posts with label Debugging. 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, September 10, 2010

Programmatically adding views to fit view not working

I was attempting to modify my sliders so that I add the programmatically to fit the available space.  In doing so I was taking an input view and calculating the size that the 4 bars should be to fill up the available space.  I was running into a problem that they weren't displaying correctly.  The bottom bar was always running over.

The weird thing was that all the logging showed that it was working.  I finally figured out that the navigation bar at the top seemed to be messing me up.  I was calculating my view sizes prior to pushing them onto the navigation stack, and that was compressing the data.

The code that calculates and populates my slider where viewToUse is a parameter:
this is done with the loadView command:



    NSLog(@"view height %f, width %f x %f, y %f",viewToUse.frame.size.height,viewToUse.frame.size.width,viewToUse.frame.origin.x,viewToUse.frame.origin.y);
    
    int sliderHeight=(viewToUse.frame.size.height)/4-spacing;
    
    NSLog(@"SliderHeight %d",sliderHeight);
    
    for (int i=0;i<4;i++)
    {
        CGRect labelFrame=CGRectMake(0, (sliderHeight+spacing)*i+sliderHeight/2-labelHeight/2, labelWidth, labelHeight);
        UILabel *label=[[UILabel alloc] initWithFrame:labelFrame];
        label.adjustsFontSizeToFitWidth=true;
        label.text=[sliderLabels objectAtIndex:i];
        [viewToUse addSubview:label];
        CGRect areaFrame=CGRectMake(leftMargin+labelWidth, (sliderHeight+spacing)*i,viewToUse.bounds.size.width-leftMargin-rightMargin-labelWidth, sliderHeight);
        
        sliders[i]=[[UISlider alloc]init];
        [sliders[i] addTarget:self action:@selector(sliderChanged:) forControlEvents:UIControlEventValueChanged];
        sliders[i].tag=i;        
        sliders[i].accessibilityLabel=[sliderLabels objectAtIndex:i];
        NSLog(@"Y is %d",(sliderHeight+spacing)*i);
        sliders[i].frame=areaFrame;
        [viewToUse addSubview:sliders[i]];
        
     }    

This precise code looks very different depending on how my view is initialized:

With a navigation bar:


 NSLog(@"Select color chosen");
    
[self.textView resignFirstResponder];
    ColorEdit *colorEditor=[[ColorEdit alloc] init];
    
    [self.navController pushViewController:colorEditor animated:true];
    [self.navController setNavigationBarHidden:false ];











And with this, (no navigation bar)


self.textView resignFirstResponder];
    ColorEdit *colorEditor=[[ColorEdit allocinit];
    
    [self.navController pushViewController:colorEditor animated:true];
    [self.navController setNavigationBarHidden:true ];















Finally I moved the initialization to another function instead of when the view loads and call this.

  NSLog(@"Select color chosen");
    
[self.textView resignFirstResponder];
    ColorEdit *colorEditor=[[ColorEdit alloc] init];
    
    [self.navController pushViewController:colorEditor animated:true];
    [self.navController setNavigationBarHidden:false ];
    [colorEditor initColors];













Note that this changes the size of the view, probably because it autoresizes then the navigation bar is added.
With the navigation bar it is 241 pixels high.  Without the navigation bar it is 285 pixels high.


The lesson is that when programmatically filling in a view, pay attention to WHEN you do it. If you are going to have other items hanging around check to see if those are changing the heights of your views.  This might throw off your results.  This particular one cost me several hours of debugging, since I was sure my math was correct.

Tuesday, April 20, 2010

Fetched LIst Controller: Using a predicate on a relationship.

In my previous blog entry I was pretty proud of figuring out the way to select multiple word lists and then build a predicate that found the union of all the words belong to those lists.  It wasn't clear from the documentation and websearches how to do it.   It worked great on the simulator.  However when I put it on the device, it was extremely slow (which is probably why there wasn't great documentation on how to do it).

I ended up just abandoning using a fetched data controller for this information, and just building a complete set that contains all the words I want.  I modified:


JLTableContainer *container=
[JLTableContainer createFetchControlledTable:nil    forEntity:@"WordInformation" forSimpleKey:@"wordName"    inContext: [SightWordState Instance].control.managedObjectContext 
createSectionsBy:nil  controlledBy:nil];
NSArray *selectedLists=[listSelectDialog currentSelectedItems:@"ListName"];
NSPredicate *predicate= [NSPredicate predicateWithFormat:@"ANY containedIn in %@",selectedLists ]; 
[container updatePredicate:predicate];
UISelectionMaster * selector=[[UISelectionMaster alloc] init];
selector.listToSelect=container;
[self.navigationController pushViewController:selector animated:true];
[selector startSelection];
self.wordSelectDialog=selector;
[selector showToolbar:@"Add Words" target:self action:@selector(wordsSelected:)];
To:
- (IBAction) listsSelected:(id) sender
{

NSArray *selectedLists=[listSelectDialog currentSelectedItems:@"ListName"];
NSMutableSet *words=[[NSMutableSet alloc]init];

for (WordList *list in selectedLists)
{
[words unionSet:list.containedWords];
}
JLTableContainer *container=
[JLTableContainer createSetController:nil forList:words sortKey:@"wordName"
inContext:[SightWordState Instance].control.managedObjectContext
controlledBy:nil];
UISelectionMaster * selector=[[UISelectionMaster alloc] init];
selector.listToSelect=container;
[self.navigationController pushViewController:selector animated:true];

[selector startSelection];
self.wordSelectDialog=selector;
[selector showToolbar:@"Add Words" target:self action:@selector(wordsSelected:)];

}
I didn't get timing numbers, but there was a perceptibly strong performance improvement for the second set of code.  This is one of the reasons I'm writing a general library, I can change how something is implemented with (hopefully) minimal impact to the remainder of the code.


Sunday, April 18, 2010

Xcode Fun: warning: building for deployment target '3.1.3' requires an armv6 architecture.

I just spent an hour tracking down this error, I was trying to deploy to my iPhone device after testing in the simulator, and upgrading to a universal application.  I found that I was unable to select armv6 from the active architecture window.  I kept getting the error:
warning: building for deployment target '3.1.3' requires an armv6 architecture.
and another error about needing to use 3.2 for an IPAD.

I selected project settings from xcode, and everything was set up.  What I finally figured out is that I had to select the target, and then select 'Get Info' in order to display the dialog for MY TARGET.  From there I found my Architectures was set incorrectly.  I changed it to standard, (armv6 & armv7) and then I was able to deploy to my target device.

The key concept to remember seems to be that the targets is what matters, not the project setting (which is different from Visual Studio).

Tuesday, March 23, 2010

Categories in Static Library

I'm using categories to expand a few classes in my static library, I have a category on UIView that creates an image of the view, and a category on NSPredicate that generates a predicate for looking for words starting with a search string.

This worked fine on the simulator, but CRASHED the iPhone.  I had developed for a week simulator only, and was dismayed to find my iPhone not working.

After some digging I found a few posts on stack overflow that referenced using the -all_load flag, I tried that and it did not work.  Then I noticed that I had another category that DID work, an expansion of set.  The difference was that the expansion of NSSet did not have it's own files.

I then created a dummy class, and put all my categories into that class, after doing this my application worked fine, even though I didn't actually instantiate the class anywhere.

Sample include file (note in one case I included the category interface from another include, in the other I just cut & pasted it in:
//

//  CategoryDummy.h
//  JLFoundation
//
//  Created by Jon Lundy on 3/23/10.
//

#import
#import "UIView_Helper.h"
#import "JLPredicateHelper_NSPredicate.h"

@interface UIView(AnimationHelper)

//
// Create a image of the current view, and give it a frame 
// identical to the current location.  The image view is autoreleased.
// 

- (UIImageView *)createImageOfView;

@end

// A bug seems to cause categories to NOT work if they aren't in a file that
// is explicitly included.


@interface CategoryDummy : NSObject {

}

@end


And for the source file:

//
//  CategoryDummy.m
//  JLFoundation
//
//  Created by Jon Lundy on 3/23/10.
//

#import "CategoryDummy.h"

#import


@implementation CategoryDummy

@end




@implementation NSPredicate(JLPredicateHelper)


+ (NSPredicate *) createSearchPredicate:(NSString*) fieldToSearchOn startingText:(NSString*) startingText
{
NSString *searchString=[startingText stringByAppendingString:@"*"];
NSString *predicateFormat=[[NSString alloc] initWithFormat:@"%@ like[cd] %%@",fieldToSearchOn];
NSPredicate *predicate=[NSPredicate predicateWithFormat:predicateFormat,searchString];
[predicateFormat release];
return predicate ;
}
@end

@implementation UIView(AnimationHelper)



//
// Create a image of the current view, and give it a frame 
// identical to the current location.  The image view is autoreleased.
// 
- (UIImageView *)createImageOfView
{
// First getting a view of the current image.
UIGraphicsBeginImageContext(self.bounds.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
// this function returns an autoreleased image.
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView *imageView=[[UIImageView alloc] initWithImage :viewImage];
imageView.frame=self.frame;
return [imageView autorelease];
}
@end


Where JLPredicateHelper is:


//
//  JLPredicateHelper_NSPredicate.h
//  JLFoundation
//
//  Created by Jon Lundy on 3/19/10.


@interface NSPredicate(JLPredicateHelper) 

// This routine will create a predicate that searches a table on a certain field for any values
// that start with the same text (case insensitive).  The predicate is returned autorelease.
+ (NSPredicate *) createSearchPredicate:(NSString*) fieldToSearchOn startingText:(NSString*) startingText;

@end

This was all I had to do to get my categories working on the iPhone.  I was even able to remove the -all_load compiler flag.

Monday, February 1, 2010

Properties and Retain

I just found the answer to a problem that has been bedeviling me for a while, I had a crash with a deallocated object. The object was a property and I was POSITIVE that I had the proper memory management. I could workaround it with an extra retain, but I hated that solution.

I finally found an answer on Stack Overflow by Chris Hanson:


I had a property named currentWords that I was setting equal to another value:

@property (nonatomic,retain) NSArray * currentWords;


and using

currentWords=result;


The problem was that invoking a variable this way DOES not invoke the retain or other characteristics of the automatically created setter. The proper way to represent this is:


self. currentWords=result;


I'm sure for someone used to Objective-C this is obvious, but coming from C# and Java this was not an obvious error, and caused me much debugging. I had a workaround with an extra retain, but I don't want to have bogus code present, I want to solve the problem for real.