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).
Showing posts with label iphone. Show all posts
Showing posts with label iphone. Show all posts
Sunday, April 18, 2010
Monday, April 5, 2010
Selection Controller: Decorating with checkmarks
The first stage was to create a selection controller which allows any list to be decorated with selections. In this case I extended my previous table list so that it can decorate one of my tables and intercept the selections and modify the cells. The goal is that I can easily add selection to any list.
Usage:
The following method will create a unique selector for a particular table, in this case I'm creating a new table from scratch, however this should work when changing to 'select mode' on another list (such as multiple delete).
Right now I'm just using the Checkmark accessory to select items. Eventually I want to add custom images on the left (such as the mail app on the iPhone).
Usage:
The following method will create a unique selector for a particular table, in this case I'm creating a new table from scratch, however this should work when changing to 'select mode' on another list (such as multiple delete).
Right now I'm just using the Checkmark accessory to select items. Eventually I want to add custom images on the left (such as the mail app on the iPhone).
JLTableContainer *container=
[JLTableContainer createFetchControlledTable:nil
forEntity:@"WordList"
forSimpleKey:@"ListName"
inContext:
[SightWordState Instance].control.managedObjectContext
createSectionsBy:nil
controlledBy:nil];
[container updatePredicate:nil];
Now after creating the list, add the selector.
UISelectionMaster * selector=[[UISelectionMaster alloc] init];
selector.listToSelect=container;
[self.navigationController pushViewController:selector animated:true];
Now the selector takes one of my JLTableContainer objects and decorates it with selection items:
@interface JLSelectionController : NSObject {
id<JLTableController> originalController;
id<JLCellProvider> originalCellProvider;
NSMutableSet *selectedItems;
UITableView *tableView;
}
@property (nonatomic,retain) id originalController;
@property (nonatomic,retain) id originalCellProvider;
@property (nonatomic,retain) NSMutableSet *selectedItems;
@property (nonatomic,retain) UITableView *tableView;
- (void) toggleObject:(id) objToToggle;
- (void) addToContainer:(JLTableContainer*) container;
- (void) removeFromContainer:(JLTableContainer*) container;
Here is the implementation. What this is doing is creating a new level of controller for the strategy pattern keeping track of the old pattern. In addition it creates a new cellForObject method, which takes the lower level one and changes the accessory type. (I want to change it to use an icon instead).
@implementation JLSelectionController
@synthesize originalController;
@synthesize originalCellProvider;
@synthesize selectedItems;
@synthesize tableView;
- (UITableViewCell *)cellForObject:(id) object atIndexPath:(NSIndexPath *)indexPath forTable:(UITableView*) table
{
tableView=table;
// Delegate to previous provider.
UITableViewCell *returnCell=[originalCellProvider
cellForObject:object
atIndexPath:indexPath forTable:table] ;
// Check it for now, later I'd like to make a custom image and put it at the left (ala mail).
if ([selectedItems containsObject:object])
{
returnCell.accessoryType=UITableViewCellAccessoryCheckmark;
}
else {
returnCell.accessoryType=UITableViewCellAccessoryNone;
}
return returnCell;
}
- (void) rowSelected:(id) selectedObject
{
[self toggleObject:selectedObject];
}
- (void) acessorySelected:(id) selectedObject
{
[self toggleObject:selectedObject];
}
- (void) toggleObject:(id) objToToggle
{
if (selectedItems==nil)
selectedItems=[[NSMutableSet alloc]init];
if ([selectedItems containsObject:objToToggle])
{
[selectedItems removeObject:objToToggle];
}
else {
[selectedItems addObject:objToToggle];
}
if (tableView!=nil)
{
[tableView reloadData];
}
}
- (void) dealloc
{
[super dealloc];
[self.selectedItems release];
}
- (void) addToContainer:(JLTableContainer*) container
{
self.originalController=container.tableController;
container.tableController=self;
self.originalCellProvider=container.cellProvider;
container.cellProvider=self;
[container connectObjects];
container.table =container.table;
[container.table reloadData];
}
- (void) removeFromContainer:(JLTableContainer*) container
{
container.tableController=self.originalController;
container.cellProvider=self.originalCellProvider;
[container.table reloadData];
}
I have this working for one case. The next step will be to extend eventing so that choices can be made on toolbars and navigation bars for the items and passing the results on in a workflow.
In addition I suspect the reload data for the table is a performance problem, I did it when I was having trouble getting the checkmarks to update.
Labels:
iphone,
JLTableContainer,
Selection,
sight words,
strategy pattern
Wednesday, March 24, 2010
Refactoring Suggested Items: The template
The container template
The first template is a small set of code that has some utility routines to create two types of tables, and which contains the other strategies. It is currently a little clunky, a future refactoring will try to make the API cleaner
The header file
/
// JLTableContainer.h
// JLFoundation
//
// Created by Jon Lundy on 3/20/10.
//
#import
#import
@interface JLTableContainer : NSObject
{
NSObject<JLCallbackHandler> *callbackHandler;
NSObject<JLTableSource> *tableSource;
NSObject<JLCellProvider> *cellProvider;
NSObject<JLTableController> *tableController;
UITableView *table;
}
@property (retain,nonatomic) NSObject *callbackHandler;
@property (retain,nonatomic) NSObject *tableSource;
@property (retain,nonatomic) NSObject *cellProvider;
@property (retain,nonatomic) NSObject *tableController;
@property (retain,nonatomic) UITableView *table;
// Create a set of controllers for a simple fetch controller that
// sorts on a given sort with a key displayed for the value of hat field.
// the tableController is passed in.
+ (JLTableContainer*) createFetchControlledTable:(UITableView *)table
forEntity:(NSString *)entityName forSimpleKey:(NSString *)keyName
inContext:(NSManagedObjectContext *) context
createSectionsBy:(NSString *) sectionName
controlledBy:(NSObject<JLTableController>*) controller;
+ (JLTableContainer*) createFetchControlledTable:(UITableView *)table
forEntity:(NSString *)entityName forSimpleKey:(NSString *)keyName
inContext:(NSManagedObjectContext *) context
createSectionsBy:(NSString *) sectionName
controlledBy:(NSObject<JLTableController>*) controller
accessoryDisplay:(UITableViewCellAccessoryType) accessoryType;
// If this source supports searching via a predicate, then update the
// predicate with that call. Note that if the source DOES NOT
// restrict by predicate, then this call will do nothing.
- (void) updatePredicate:(NSPredicate *) predicate;
// Connect the objects to each other to form the delegation chain of the
// template object.
- (void) connectObjects;
@end
The source file
//
// JLTableContainer.m
// JLFoundation
//
// Created by Jon Lundy on 3/20/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import "JLTableContainer.h"
@implementation JLTableContainer
@synthesize callbackHandler;
@synthesize tableSource;
@synthesize cellProvider;
@synthesize tableController;
@synthesize table;
+ (JLTableContainer*) createFetchControlledTable:(UITableView *)table
forEntity:(NSString *)entityName forSimpleKey:(NSString *)keyName
inContext:(NSManagedObjectContext *) context
createSectionsBy:(NSString *) sectionName
controlledBy:(NSObject<JLTableController>*) controller
{
return [JLTableContainer createFetchControlledTable:table forEntity:entityName forSimpleKey:keyName inContext:context
createSectionsBy:sectionName controlledBy:controller
accessoryDisplay:UITableViewCellAccessoryNone];
}
+ (JLTableContainer*) createFetchControlledTable:(UITableView *)table
forEntity:(NSString *)entityName forSimpleKey:(NSString *)keyName
inContext:(NSManagedObjectContext *) context
createSectionsBy:(NSString *) sectionName
controlledBy:(NSObject<JLTableController>*) controller
accessoryDisplay:(UITableViewCellAccessoryType) accessoryType
{
JLTableContainer *returnValue=[[JLTableContainer alloc]init];
SimpleDescriptionCellProvider *simpleCellProvider=[[SimpleDescriptionCellProvider alloc] init];
simpleCellProvider.accessoryType=accessoryType;
returnValue.table=table;
JLFetchedControllerSource *source=[[JLFetchedControllerSource alloc] init];
source.sortField=keyName;
source.managedEntityName=entityName;
source.sectionField=sectionName;
source.fetchContext=context;
JLStandardCallbackHandler *callHandler=[[JLStandardCallbackHandler alloc]init];
returnValue.tableController=controller;
returnValue.cellProvider=simpleCellProvider;
returnValue.tableSource=source;
returnValue.callbackHandler=callHandler;
[returnValue connectObjects];
return [returnValue autorelease];
}
- (void) connectObjects
{
table.delegate=callbackHandler;
table.dataSource=tableSource;
if (callbackHandler!=nil)
{
callbackHandler.tableController=tableController;
callbackHandler.source=tableSource;
}
if (tableSource!=nil)
{
tableSource.cellProvider=cellProvider;
tableSource.table=table;
}
if (cellProvider!=nil)
{
}
if (tableController!=nil)
{
}
}
- (void) updatePredicate:(NSPredicate *) predicate
{
// Check to make source our source is not null and implements the
// requested protocol.
if (self.tableSource!=nil)
{
if ( [tableSource conformsToProtocol:@protocol(RestrictWithPredicate)] ) {
id<RestrictWithPredicate> restricted=(id<RestrictWithPredicate>) self.tableSource;
[restricted updatePredicate:predicate];
}
}
}
- (void) dealloc
{
[callbackHandler release];
[tableSource release];
[cellProvider release];
[tableController release];
[super dealloc];
}
@end
Points of Interest
Static builder methods
The first thing to note is that there is a rather large public static method that lets you construct a fetched result controller which has standard behavior. This API is clumsy, but it should work.
In addition I defined an updatePredicate utility method. This method will first verify that the table source
corresponds to the new protocol, RestrictWithPredicate. If it does, then it will update the predicate with
the passed in data. This allows me to have various optional functions that can be used by manipulating the container object instead of having a lot of casting in the middle.
My goal is to restructure this API a little bit so that it is cleaner, and more modular to use.
if (self.tableSource!=nil)
{
if ( [tableSource conformsToProtocol:@protocol(RestrictWithPredicate)] ) {
id<RestrictWithPredicate> restricted=(id<RestrictWithPredicate>) self.tableSource;
[restricted updatePredicate:predicate];
}
}
Restructuring calls
The method connectObjects is used to restructure the various strategies. Each strategy knows about other strategies in the suite. This call enables you to change strategies, and then make a single call to connect everything back up again.
Usage
Now that I have these methods in place I was able to quickly restructure my main sight word list to instead of selecting all sight words into memory, using a NSFetchedResultsController. I disconnected the .nib file from the File Owner in Interface Builder, and added the following code in viewDidLoad.
self.container=[JLTableContainer createFetchControlledTable:self.tableView
forEntity:@"WordList"
forSimpleKey:@"ListName"
inContext:currentState.control .managedObjectContext
createSectionsBy:@"Category"
controlledBy:self
accessoryDisplay:UITableViewCellAccessoryDetailDisclosureButton
];
[self.container updatePredicate:nil];
I then removed all of the standard table callbacks, and implemented these two callbacks:
Selecting a row
- (void) rowSelected:(id) selectedObject
{
WordList *list=(WordList*)selectedObject;
SightWordDisplay *display=[[SightWordDisplay alloc] initWithNibName:@"SightWordDisplay" bundle:nil];
SightWordProvider *provider=[[SightWordProvider alloc] initWords:list];
display.provider = provider;
// display.title=provider.wordList.ListName;
// Copied from Beginning IPhone Development. Not sure I like the global reference.
[self.navigationController pushViewController:display animated:YES];
[display release];
}
Selecting an accessory
- (void) acessorySelected:(id) selectedObject
{
ListEditor *editor=[[ListEditor alloc ] initWithNibName:@"ListEditor" bundle:nil];
editor.wordlist=(WordList*)selectedObject;
SightWordsUnlimitedAppDelegate *delegate =
[[UIApplication sharedApplication] delegate];
[delegate.rootController pushViewController:editor animated:YES];
[editor.tableView reloadData];
[editor release];
}
The goal is to remove the table management logic from my view controller, and let it concentrate on business logic.
Note that I broke the ability to delete word lists when I did this. I'm going to have to go put it back in. The nice thing about a shared class is that when I add this functionality back in, it will be available to any class using the library.
Labels:
Fetch Controller,
flash cards,
iphone,
Refactoring,
sight words
Refactoring Suggested Items. List Logic
The next goal is a big one, refactor the display of items from the DataFetchController into a separate class. The reasons behind this refactoring are:
This is the template object that contains the other sources. It will contain utility routines that enable you to quickly create common tables, and configure their behavior. The initial version will be rather thin, just the existing fetched controller behavior, but additional functionality will be added as the libraries mature.
- This is a common task which needs to be performed frequently. Anytime you find yourself pasting in a set of similar methods, you really should consider refactoring that.
- This is a more object oriented approach, and favors composition over inheritance. This allows for smaller sets of objects to work together. Every sample class I see usually has the view controller implementing the UITableViewDelegate and the UITableViewDataSource. I want to factor these out into a set of common classes that can work together.
- Currently I'm targeting IPhone OS 3.0 or greater since I'm using core data. At some point I might want to target earlier versions of the OS. In this case I would need to work with arrays instead of core data objects. In addition I can switch between using relationship properties, and queries with minimal effort.
- As I learn more about the framework, additional capabilities can be backfilled into earlier projects by changing the base classes. [Note that this does add additional testing time when I make changes].
The design:
The top layer of this will be a container class that encapsulates a set of strategies. This is basically a template design pattern with individual strategies for certain tasks. These strategies will be 4 protocols which define the behavior characteristics. The first version of implementing these protocols will be simple. In addition various implementations will have different capabilities rhey might support. Instead of implementing these in every class we will define additional protocols that define this behavior
JLTableContainer
This is the template object that contains the other sources. It will contain utility routines that enable you to quickly create common tables, and configure their behavior. The initial version will be rather thin, just the existing fetched controller behavior, but additional functionality will be added as the libraries mature.
JLTableSource
This implements the UITableViewDataSource protocol and defines methods that determine which data items are available. This will probably have very few methods other than properties for the other members of the hierarchy.
Initial subclasses will be:
- JLFetchedControllerSource -- Implements a wrapper around a fetch controller.
- JLSetArrayController -- Implements a wrapper around a set or array.
JLCellProvider
This is a very specific class that given a given index path provides the cell for this index.
Initial subclasses will include:
- JLKeyValueCellProvider -- Provides a value based on a key from the object.
- JLDescriptionProvider -- Uses the standard NSObject description method as text.
- JLSelectionProvider -- Provides a checkbox and monitors it's behavior. This will also implement the callbacks as well.
JLCallbackHandler
This is a wrapper around the common actions, such as selecting an accessor or cell, and then performing an action. It will encapsulate the association of the action with a specific object, and then call the JLTableController to actually do the action.
Initial subclasses will include:
- JLStandardCallbacks -- Delegate the standard callbacks to the designated list.
- JLSelectionProvider -- Encapsulate a class that tracks selections/deselection in combination with the cell provider.
JLTableController
This is the domain class that acts upon choices. It implements the UITableViewDelegate It provides methods that respond to the choices the user made, and potentially provides methods for controlling a navigation bar or control bar, or a search bar.
Initial versions:
- JLSelectionProvider -- A base class for doing selections.
- JLTableSearcher -- Searching through a list for values.
Labels:
iphone,
Refactoring,
Table Controller
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:
//
And for the source file:
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:
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.
Labels:
bugs,
Categories,
Debugging,
iphone,
Objective-C,
selector not found,
static library
Subscribe to:
Posts (Atom)