UISearchController results are not being filtered - objective-c

So I am using the UISearchController in my project, and it seems to work all fine but whatever I type on the Search Bar no I get no results. So I believe the problem is on my
(void)updateSearchResultsForSearchController:(UISearchController *)searchController {
}. To be honest, I don't really know what code to put in that method. Here is the rest of the code:
#property (nonatomic, strong) UISearchController *searchController;
#property (nonatomic, strong) SearchResultsTableViewController *resultsTableController;
#property (nonatomic, strong) NSMutableArray *searchResults; // Filtered search results
// for state restoration
#property BOOL searchControllerWasActive;
#property BOOL searchControllerSearchFieldWasFirstResponder;
_resultsTableController = [[SearchResultsTableViewController alloc] init];
_searchController = [[UISearchController alloc] initWithSearchResultsController:self.resultsTableController];
self.searchController.searchResultsUpdater = self;
[self.searchController.searchBar sizeToFit];
self.tableView.tableHeaderView = self.searchController.searchBar;
self.searchController.delegate = self;
self.searchController.dimsBackgroundDuringPresentation = NO;
self.searchController.searchBar.delegate = self;
self.searchController.searchBar.tintColor = [UIColor darkGrayColor];
self.definesPresentationContext = YES;
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// restore the searchController's active state
if (self.searchControllerWasActive) {
self.searchController.active = self.searchControllerWasActive;
_searchControllerWasActive = NO;
if (self.searchControllerSearchFieldWasFirstResponder) {
[self.searchController.searchBar becomeFirstResponder];
_searchControllerSearchFieldWasFirstResponder = NO;
}
}}
#pragma mark - UISearchBarDelegate
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[searchBar resignFirstResponder];}
#pragma mark - UISearchResultsUpdating
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
}

You'll need to take the NSArray that your table view data comes from, filter it as per your search criteria, and then reload the table view. Your code might look something like this:
- (void) doSearch:(NSString *)searchText {
[self.filteredClients removeAllObjects];
if ( [searchText length] == 0 ) {
[self.filteredClients addObjectsFromArray:self.users];
} else {
for (User *user in self.users) {
NSString *name = [user fullName];
NSRange range = [[name lowercaseString] rangeOfString:[searchText lowercaseString]];
if ( range.location != NSNotFound )
[self.filteredClients addObject:user];
}
}
[self.tableView reloadData];
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if ( [searchText length] > 0 )
{
[self showCancelButton:YES];
}
else {
[self showCancelButton:NO];
[searchBar resignFirstResponder];
}
[self doSearch:searchText];
}
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
[self showCancelButton:YES];
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
if ( [searchBar.text length] > 0 ) [self showCancelButton:YES];
else [self showCancelButton:NO];
[self doSearch:_searchBar.text];
}

Related

Removing an element from NSDictionary causes it to be deallocated prematurely

Full project can be seen here (for context: https://github.com/atlas-engineer/next-cocoa)
The following code returns EXC_BAD_ACCESS:
- (bool)windowClose:(NSString *)key
{
NSWindow *window = [[self windows] objectForKey:key];
[[self windows] removeObjectForKey:key];
[window close];
return YES;
}
The following code, however, works
- (bool)windowClose:(NSString *)key
{
[[self windows] removeObjectForKey:key];
return YES;
}
As does the following:
- (bool)windowClose:(NSString *)key
{
NSWindow *window = [[self windows] objectForKey:key];
[window close];
return YES;
}
It is only somehow when you put them together that everything breaks.
For reference, I've provided the AutokeyDictionary implementation below, which is the value of [self windows] in the examples above
//
// AutokeyDictionary.m
// next-cocoa
//
// Created by John Mercouris on 3/14/18.
// Copyright © 2018 Next. All rights reserved.
//
#import "AutokeyDictionary.h"
#implementation AutokeyDictionary
#synthesize elementCount;
- (instancetype) init
{
self = [super init];
if (self)
{
[self setElementCount:0];
_dict = [[NSMutableDictionary alloc] init];
}
return self;
}
- (NSString *) insertElement:(NSObject *) object
{
NSString *elementKey = [#([self elementCount]) stringValue];
[_dict setValue:object forKey: elementKey];
[self setElementCount:[self elementCount] + 1];
return elementKey;
}
- (NSUInteger)count {
return [_dict count];
}
- (id)objectForKey:(id)aKey {
return [_dict objectForKey:aKey];
}
- (void)removeObjectForKey:(id)aKey {
return [_dict removeObjectForKey:aKey];
}
- (NSEnumerator *)keyEnumerator {
return [_dict keyEnumerator];
}
- (NSArray*)allKeys {
return [_dict allKeys];
}
#end
Lastly, for the record, turning on zombies does make the code work, though that is obviously not a solution.
Your window's releasedWhenClosed property is probably defaulting to YES, which is likely to conflict with ARC's memory management. Set it to NO when you create the window.
The correct answer ended up being the following sequence of code
- (bool)windowClose:(NSString *)key
{
NSWindow *window = [[self windows] objectForKey:key];
[window setReleasedWhenClosed:NO];
[window close];
[[self windows] removeObjectForKey:key];
return YES;
}
any other order of events and the object would be prematurely released.

Problems Implementing Search Bar with Parse Data

I have searched the land of Google far and wide and I have found tutorials on this, but they are before IOS8. I have tried to piece this together as best as I can, but yet when the app runs, and I type words into the search bar, nothing returns, or it crashes. So here's how the view looks, and what each object means:
Here is my JobListViewController.m File:
#import "JobDetailViewController.h"
#import "JobListViewController.h"
#import "Job.h"
#import "SearchedResultCell.h"
#interface JobListViewController () <UISearchDisplayDelegate, UISearchBarDelegate> {
}
#property (nonatomic, weak) IBOutlet UISearchBar *searchedBar;
#property (nonatomic, strong) NSString *mainTitle;
#property (nonatomic, strong) NSString *subTitle;
#property (nonatomic, assign) BOOL canSearch;
#end
#interface JobListViewController ()
#end
#implementation JobListViewController
{
NSArray *jobs;
NSArray *searchResults;
}
#synthesize searchedBar;
#synthesize mainTitle;
#synthesize subTitle;
#synthesize canSearch;
- (id)initWithCoder:(NSCoder *)aCoder
{
self = [super initWithCoder:aCoder];
if (self) {
// Custom the table
// The className to query on
self.parseClassName = #"Jobs";
// The key of the PFObject to display in the label of the default cell style
self.textKey = #"Position";
// Whether the built-in pull-to-refresh is enabled
self.pullToRefreshEnabled = YES;
// Whether the built-in pagination is enabled
self.paginationEnabled = YES;
// The number of objects to show per page
self.objectsPerPage = 10;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.searchedBar becomeFirstResponder];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.canSearch = 0;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (void)objectsWillLoad {
[super objectsWillLoad];
// This method is called before a PFQuery is fired to get more objects
}
- (PFQuery *)queryForTable
{
PFQuery *query;
if (self.canSearch == 0)
{
query = [PFQuery queryWithClassName:#"Jobs"];
}
else
{
query = [PFQuery queryWithClassName:#"Jobs"];
NSString *searchThis = [searchedBar.text uppercaseString];
[query whereKey:#"Position" containsString:searchThis];
}
[query orderByAscending:#"Position"];
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
return query;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object: (PFObject *)object
{
static NSString *simpleTableIdentifier = #"JobCell";
static NSString *pimpleTableIdentifier = #"JobCell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
SearchedResultCell *cell = [self.tableView dequeueReusableCellWithIdentifier:pimpleTableIdentifier];
if (cell == nil) {
cell = [[SearchedResultCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:pimpleTableIdentifier];
}
[self configureSearchResult:cell atIndexPath:indexPath object:object];
}
// Configure the cell
PFFile *thumbnail = [object objectForKey:#"imageFile"];
PFImageView *thumbnailImageView = (PFImageView*)[cell viewWithTag:100];
thumbnailImageView.image = [UIImage imageNamed:#"placeholder.jpg"];
thumbnailImageView.file = thumbnail;
[thumbnailImageView loadInBackground];
UILabel *positionLabel = (UILabel*) [cell viewWithTag:101];
positionLabel.text = [object objectForKey:#"Position"];
UILabel *rotationLabel = (UILabel*) [cell viewWithTag:102];
rotationLabel.text = [object objectForKey:#"Rotation"];
UILabel *locationLabel = (UILabel*) [cell viewWithTag:103];
locationLabel.text = [object objectForKey:#"Location"];
UILabel *typeLabel = (UILabel*) [cell viewWithTag:104];
typeLabel.text = [object objectForKey:#"Type"];
return cell;
}
- (void) objectsDidLoad:(NSError *)error
{
[super objectsDidLoad:error];
NSLog(#"error: %#", [error localizedDescription]);
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showJobDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
Job *job = [[Job alloc] init];
JobDetailViewController *destViewController = segue.destinationViewController;
PFObject *object = [self.objects objectAtIndex:indexPath.row];
job.position = [object objectForKey:#"Position"];
job.name = [object objectForKey:#"Name"];
job.email = [object objectForKey:#"Email"];
job.phone = [object objectForKey:#"Phone"];
job.imageFile = [object objectForKey:#"imageFile"];
job.rotation = [object objectForKey:#"Rotation"];
job.location = [object objectForKey:#"Location"];
job.type = [object objectForKey:#"Type"];
job.clearance = [object objectForKey:#"Clearance"];
job.job_description = [object objectForKey:#"Job_Description"];
job.qualifications = [object objectForKey:#"Qualifications"];
destViewController.job = job;
}
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
[self clear];
self.canSearch = 1;
[self.searchedBar resignFirstResponder];
[self queryForTable];
[self loadObjects];
}
/*
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (searchResults == nil) {
return 0;
} else if ([searchResults count] == 0) {
return 1;
} else {
return [self.objects count];
}
}
*/
- (void)configureSearchResult:(SearchedResultCell *)cell atIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
mainTitle = [object objectForKey:#"Position"];
cell.mainTitle.text = mainTitle;
subTitle = [object objectForKey:#"Type"];
cell.detail.text = subTitle;
// Implement this if you want to Show image
cell.showImage.image = [UIImage imageNamed:#"placeholder.jpg"];
PFFile *imageFile = [object objectForKey:#"imageFile"];
if (imageFile) {
cell.showImage.file = imageFile;
[cell.showImage loadInBackground];
}
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[searchedBar resignFirstResponder];
if ([self.objects count] == indexPath.row) {
[self loadNextPage];
} else {
PFObject *photo = [self.objects objectAtIndex:indexPath.row];
NSLog(#"%#", photo);
// Do something you want after selected the cell
}
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[self.searchedBar resignFirstResponder];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *) searchBar {
[self.searchedBar resignFirstResponder];
[self queryForTable];
[self loadObjects];
}
#end
I cannot find where I am going wrong. Any help whatsoever would be much appreciated. Or if anything if you can point me in the right direction.
I had problems with that I few months ago, so below is a project that reeeally helped me. Hope for you too.
https://github.com/mwazir2/ParseSearchNoPagination
cheers!
Update
In my adaptation of this example above I followed everything from the project. The project uses PFQueryTableViewController from Parse SDK and not the default iOS TableViewController.
In the first part -(id)initWithCoder I have commented the
//self.textKey = #"username";
Then I altered the - (PFQuery *)queryForTable method in some ways. One just changed the whereKey to adequate my project and the second changed the query a little bit more because it should only show in the table objects related to specific users. Below follows respective queries.
The first more general query
- (PFQuery *)queryForTable
{
PFQuery *query = [PFUser query];
if (self.canSearch == 0) {
query = [PFQuery queryWithClassName:#"_User"];
} else {
query = [PFQuery queryWithClassName:#"_User"];
NSString *searchThis = [searchedBar.text capitalizedString];
//PFQuery *query = [PFQuery queryWithClassName:#"Channel"];
//[query whereKey:#"channelName" equalTo:searchThis];
[query whereKey:#"username" containsString:searchThis];
}
The second, specific to current user:
- (PFQuery *)queryForTable
{
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
[query whereKey:#"owner" equalTo:[PFUser currentUser]];
[query orderByDescending:#"createdAt"];
if (self.pullToRefreshEnabled) {
query.cachePolicy = kPFCachePolicyNetworkOnly;
}
// If no objects are loaded in memory, we look to the cache first to fill the table
// and then subsequently do a query against the network.
if (self.objects.count == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
return query;
}
For the tableView methods I only used: tableView cellForRowAtIndexPath(pretty much like yours). And also the cellForRowAtIndexPath for the LoadMoreCell, like in the example.
Also the tableView heightForRowAtIndexPath: for the LoadMoreCell and tableView didSelectRowAtIndexPath, like below:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
//[searchedBar resignFirstResponder];
if ([self.objects count] == indexPath.row) {
[self loadNextPage];
} else {
PFObject *photo = [self.objects objectAtIndex:indexPath.row];
NSLog(#"%#", photo);
// Do something you want after selected the cell
}
}
Of course below the } else { statement you can change it or delete the NSLog line.
I suggest you try to alter the example itself with your object and keys, until it works an then alter yours, exactly like the example... :)
Compound Query
PFQuery *query1 = [PFQuery queryWithClassName:#"Class1"];
[name whereKey:#"column1" equalTo:[fieldInCode text]];
PFQuery *query2 = [PFQuery queryWithClassName:#"Class1"];
[enterKey whereKey:#"column2" equalTo:[anotherField text]];
PFQuery *query = [PFQuery orQueryWithSubqueries:#[query1,query2]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
Cheers!

Trying to add pages to a UIPageViewController

This is my SlidesViewController.m file. I'm trying to load a bunch of images into a UIPageViewController but am having no luck. The screen is black except for the two dots at the bottom. No image, and as far as I can tell I can't switch between the 2 views.
The output I get from my NSLog lines is
2012-11-13 20:25:10.535 [17680:c07] Slide show guid: ab7718c9-3495-4882-a1e3-71f39530963b
2012-11-13 20:25:10.536 [17680:c07] Loading image: /Users/Me/Library/Application Support/iPhone Simulator/6.0/Applications/667EF28E-4E5F-4452-BF0E-336730C2D3FE/Documents/media/dh1.jpg
2012-11-13 20:25:10.536 [17680:c07] Loading image: /Users/Me/Library/Application Support/iPhone Simulator/6.0/Applications/667EF28E-4E5F-4452-BF0E-336730C2D3FE/Documents/media/dh2.jpg
2012-11-13 20:25:10.538 [17680:c07] presentationCountForPageViewController: 2
2012-11-13 20:25:10.538 [17680:c07] presentationIndexForPageViewController: 0
Not really sure where to go from here.
#import "SlidesViewController.h"
#import "SQLiteManager.h"
#interface SlidesViewController ()
#property (nonatomic, strong) NSMutableArray *imageViews;
#end
#implementation SlidesViewController
#synthesize guid = _guid;
#synthesize imageViews = _imageViews;
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Slide show guid: %#", self.guid);
self.dataSource = self;
// Get the system paths
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
NSString *mediaDir = [docsDir stringByAppendingPathComponent:#"media"];
// Grab the images from the database
SQLiteManager *db = [[SQLiteManager alloc] initWithDatabaseNamed:#"local.db"];
[db openDatabase];
NSString *sql = [NSString stringWithFormat:#"select * from pictures where point='%#' order by rank", self.guid];
NSArray *pictures = [db getRowsForQuery:sql];
// Create the views
self.imageViews = [[NSMutableArray alloc] initWithCapacity:[pictures count]];
for (NSDictionary *picture in pictures)
{
NSString *filename = [mediaDir stringByAppendingPathComponent:[picture objectForKey:#"imagename"]];
NSLog(#"Loading image: %#", filename);
UIImage *img = [[UIImage alloc] initWithContentsOfFile:filename];
UIImageView *iv = [[UIImageView alloc] initWithImage:img];
UIViewController *ivc = [[UIViewController alloc] init];
ivc.view = iv;
[self.imageViews addObject:ivc];
}
}
- (NSUInteger)indexOfViewController:(UIView *)view
{
return [self.imageViews indexOfObject:view];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSLog(#"viewControllerBeforeViewController");
int index = [self.imageViews indexOfObject:viewController] - 1;
if (index >= 0)
return [self.imageViews objectAtIndex:index];
return nil;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSLog(#"viewControllerAfterViewController");
int index = [self.imageViews indexOfObject:viewController] + 1;
if (index < [self.imageViews count])
return [self.imageViews objectAtIndex:index];
return nil;
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
{
NSLog(#"presentationIndexForPageViewController: %d", 0);
return 0;
}
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController
{
NSLog(#"presentationCountForPageViewController: %d", [self.imageViews count]);
return [self.imageViews count];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(BOOL) gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if (index==0) {
if ([(UIPanGestureRecognizer*)gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] &&
[(UIPanGestureRecognizer*)gestureRecognizer velocityInView:gestureRecognizer.view].x > 0.0f) {
NSLog(#"DENIED SWIPE PREVIOUS ON FIRST PAGE");
return NO;
}
if ([(UITapGestureRecognizer*)gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] &&
[(UITapGestureRecognizer*)gestureRecognizer locationInView:gestureRecognizer.view].x < self.view.frame.size.width/2) {
NSLog(#"DENIED TAP PREVIOUS ON FIRST PAGE");
return NO;
}
}
return YES;
}
#end
Try this in pageviewcontroller.m
use UIGestureRecognizerDelegate in .h
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if (index==0) {
if ([(UIPanGestureRecognizer*)gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] &&
[(UIPanGestureRecognizer*)gestureRecognizer velocityInView:gestureRecognizer.view].x > 0.0f) {
NSLog(#"DENIED SWIPE PREVIOUS ON FIRST PAGE");
return NO;
}
if ([(UITapGestureRecognizer*)gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] &&
[(UITapGestureRecognizer*)gestureRecognizer locationInView:gestureRecognizer.view].x < self.view.frame.size.width/2) {
NSLog(#"DENIED TAP PREVIOUS ON FIRST PAGE");
return NO;
}
}
return YES;
}

No known class method for selector 'sharedStore'

Have a singleton class for BNRItemStore, but when I tried to call it, I get the above error which causes an ARC issue. Have commented out the error.
DetailViewController.m
#import "DetailViewController.h"
#import "BNRItem.h"
#import "BNRImageStore.h"
#import "BNRItemStore.h"
#implementation DetailViewController
#synthesize item;
-(id)initForNewItem:(BOOL)isNew
{
self = [super initWithNibName:#"DetailViewController" bundle:nil];
if(self){
if (isNew) {
UIBarButtonItem *doneItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self
action:#selector(save:)];
[[self navigationItem] setRightBarButtonItem:doneItem];
UIBarButtonItem *cancelItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
target:self
action:#selector(cancel:)];
[[self navigationItem] setLeftBarButtonItem:cancelItem];
}
}
return self;
}
-(id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle
{
#throw [NSException exceptionWithName:#"Wrong initializer"
reason:#"Use initForNewItem:"
userInfo:nil];
return nil;
}
-(void)viewDidLoad
{
[super viewDidLoad];
UIColor *clr = nil;
if ([[UIDevice currentDevice]userInterfaceIdiom]== UIUserInterfaceIdiomPad) {
clr = [UIColor colorWithRed:0.875 green:0.88 blue:0.91 alpha:1];
} else {
clr = [UIColor groupTableViewBackgroundColor];
}
[[self view]setBackgroundColor:clr];
}
- (void)viewDidUnload {
nameField = nil;
serialNumberField = nil;
valueField = nil;
dateLabel = nil;
imageView = nil;
[super viewDidUnload];
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[nameField setText:[item itemName]];
[serialNumberField setText:[item serialNumber]];
[valueField setText:[NSString stringWithFormat:#"%d", [item valueInDollars]]];
// Create a NSDateFormatter that will turn a date into a simple date string
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
// Use filtered NSDate object to set dateLabel contents
[dateLabel setText:[dateFormatter stringFromDate:[item dateCreated]]];
NSString *imageKey = [item imageKey];
if (imageKey) {
// Get image for image key from image store
UIImage *imageToDisplay = [[BNRImageStore sharedStore]imageForKey:imageKey];
// Use that image to put on the screen in imageview
[imageView setImage:imageToDisplay];
} else {
// Clear the imageview
[imageView setImage:nil];
}
}
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// Clear first responder
[[self view]endEditing:YES];
// "Save" changes to item
[item setItemName:[nameField text]];
[item setSerialNumber:[serialNumberField text]];
[item setValueInDollars:[[valueField text] intValue]];
}
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)io
{
if ([[UIDevice currentDevice]userInterfaceIdiom]==UIUserInterfaceIdiomPad) {
return YES;
} else {
return (io==UIInterfaceOrientationPortrait);
}
}
-(void)setItem:(BNRItem *)i
{
item = i;
[[self navigationItem] setTitle:[item itemName]];
}
- (IBAction)takePicture:(id)sender {
if ([imagePickerPopover isPopoverVisible]) {
// If the popover is already up, get rid of it
[imagePickerPopover dismissPopoverAnimated:YES];
imagePickerPopover = nil;
return;
}
UIImagePickerController *imagePicker =
[[UIImagePickerController alloc]init];
// If our device has a camera, we want to take a picture, otherwise, we
// just pick from the photo library
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
[imagePicker setSourceType:UIImagePickerControllerSourceTypeCamera];
} else {
[imagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
// This line of code will generate a warning right now, ignore it
[imagePicker setDelegate:self];
//Place image picker on the screen
// Check for iPad device before instantiating the popover controller
if ([[UIDevice currentDevice]userInterfaceIdiom]==UIUserInterfaceIdiomPad) {
// Create a new popover controller that will display the imagepicker
imagePickerPopover = [[UIPopoverController alloc]initWithContentViewController:imagePicker];
[imagePickerPopover setDelegate:self];
// Display the popover controller; sender
// is the camera bar button item
[imagePickerPopover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
} else {
[self presentViewController:imagePicker animated:YES completion:nil];
}
}
}
-(void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
{
NSLog(#"User dismissed popover");
imagePickerPopover = nil;
}
- (IBAction)backgroundTapped:(id)sender {
[[self view]endEditing:YES];
}
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString *oldKey = [item imageKey];
// Did the item already have an image?
if (oldKey) {
// Delete the old image
[[BNRImageStore sharedStore]deleteImageForKey:oldKey];
}
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
// Create a CFUUID object - it knows how to create unique identifier strings
CFUUIDRef newUniqueID = CFUUIDCreate(kCFAllocatorDefault);
// Create a string from unique identifier
CFStringRef newUniqueIDString = CFUUIDCreateString(kCFAllocatorDefault, newUniqueID); // Incompatible integer to pointer conversion initializing
// Use that unique ID to set our item's imageKey
NSString *key = (__bridge NSString *)newUniqueIDString;
[item setImageKey:key];
// Store image in the BNRImageStore with this key
[[BNRImageStore sharedStore] setImage:image forKey:[item imageKey]];
CFRelease(newUniqueIDString);
CFRelease(newUniqueID);
[imageView setImage:image];
if ([[UIDevice currentDevice]userInterfaceIdiom]==UIUserInterfaceIdiomPad) {
// If on the phone, the image picker is presented modally. Dismiss it.
[self dismissViewControllerAnimated:YES completion:nil];
} else {
// If on the pad, the image picker is in the popover. Dismiss the popover.
[imagePickerPopover dismissPopoverAnimated:YES];
imagePickerPopover = nil;
}
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
-(void)save:(id)sender
{
[[self presentingViewController]dismissViewControllerAnimated:YES
completion:nil];
}
-(void)cancel:(id)sender
{
// If the user cancelled, then remove the BNRItem from the store
[[BNRItemStore sharedStore]removeItem:item]; // No known class method for selector 'sharedStore'
[[self presentingViewController]dismissViewControllerAnimated:YES completion:nil];
}
DetailViewController.h
#import <UIKit/UIKit.h>
#class BNRItem;
#interface DetailViewController : UIViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate,UITextFieldDelegate, UIPopoverControllerDelegate>
{
__weak IBOutlet UITextField *nameField;
__weak IBOutlet UITextField *serialNumberField;
__weak IBOutlet UITextField *valueField;
__weak IBOutlet UILabel *dateLabel;
__weak IBOutlet UIImageView *imageView;
UIPopoverController *imagePickerPopover;
}
#property(nonatomic,strong)BNRItem *item;
-(id)initForNewItem:(BOOL)isNew;
- (IBAction)takePicture:(id)sender;
- (IBAction)backgroundTapped:(id)sender;
#end
BNRItemStore.m
#import "BNRItemStore.h"
#import "BNRItem.h"
#implementation BNRItemStore
+ (BNRItemStore *)defaultStore
{
static BNRItemStore *defaultStore = nil;
if(!defaultStore)
defaultStore = [[super allocWithZone:nil] init];
return defaultStore;
}
+ (id)allocWithZone:(NSZone *)zone
{
return [self defaultStore];
}
- (id)init
{
self = [super init];
if(self) {
allItems = [[NSMutableArray alloc] init];
}
return self;
}
- (void)removeItem:(BNRItem *)p
{
[allItems removeObjectIdenticalTo:p];
}
- (NSArray *)allItems
{
return allItems;
}
- (void)moveItemAtIndex:(int)from
toIndex:(int)to
{
if (from == to) {
return;
}
// Get pointer to object being moved so we can re-insert it
BNRItem *p = [allItems objectAtIndex:from];
// Remove p from array
[allItems removeObjectAtIndex:from];
// Insert p in array at new location
[allItems insertObject:p atIndex:to];
}
- (BNRItem *)createItem
{
BNRItem *p = [BNRItem randomItem];
[allItems addObject:p];
return p;
}
#end
BNRItemStore.h
#import <Foundation/Foundation.h>
#class BNRItem;
#interface BNRItemStore : NSObject
{
NSMutableArray *allItems;
}
+ (BNRItemStore *)defaultStore;
- (void)removeItem:(BNRItem *)p;
- (NSArray *)allItems;
- (BNRItem *)createItem;
- (void)moveItemAtIndex:(int)from
toIndex:(int)to;
#end
You are calling +sharedStore on BNRItemStore where your error occurs. This is because it really does not exist according to the code you posted.
All of the others calling +sharedStore are using the one presumably made available by BNRImageStore which you didn't provide. I assume it exists in that class? :)
In short, change:
[[BNRItemStore sharedStore]removeItem:item];
to
[[BNRImageStore sharedStore]removeItem:item];

UIMenuController: how to tell which menuItem is pressed?

I have a UILongPressGestureRecognizer on a UITableViewCell that displays a UIMenuController with some categories the user can pick from. These categories are stored in a NSMutableArray and can be customized by the user. I want to use one method to handle all category-presses in the UIMenuController. How can I pass the index of the selected UIMenuItem? Thanks in advance.
#pragma mark -
#pragma mark Custom Quick Menu Item
#interface QuickMenuItem : UIMenuItem
{
}
#property (nonatomic, retain) NSIndexPath *indexPath;
#property (nonatomic, retain) NSMutableString *category;
#end
#implementation QuickMenuItem
#synthesize indexPath, category;
- (void)dealloc
{
[indexPath release];
[category release];
[super dealloc];
}
#end
#pragma mark -
#pragma mark Handle UILongPressGesture
- (void)handleLongItemPress:(UILongPressGestureRecognizer *)longPressRecognizer
{
if (longPressRecognizer.state == UIGestureRecognizerStateBegan)
{
NSIndexPath *pressedIndexPath = [queueTableView indexPathForRowAtPoint:[longPressRecognizer locationInView:queueTableView]];
if (pressedIndexPath && (pressedIndexPath.row != NSNotFound) && (pressedIndexPath.section != NSNotFound))
{
[self becomeFirstResponder];
UIMenuController *menuController = [UIMenuController sharedMenuController];
NSMutableArray *categoryMenuItems = [NSMutableArray array];
NSEnumerator *e = [self.stats.categories objectEnumerator]; // array with categories
NSMutableString *cat;
while (cat = [e nextObject])
{
QuickMenuItem *categoryMenuItem = [[QuickMenuItem alloc] initWithTitle:cat action:#selector(quickMenuCategoryPressed:)];
categoryMenuItem.indexPath = pressedIndexPath;
categoryMenuItem.category = cat;
[categoryMenuItems addObject:categoryMenuItem];
[categoryMenuItem release];
}
menuController.menuItems = [NSArray arrayWithArray:categoryMenuItems];
[menuController setTargetRect:[queueTableView rectForRowAtIndexPath:pressedIndexPath] inView:queueTableView];
[menuController setMenuVisible:YES animated:YES];
}
}
}
- (void)quickMenuCategoryPressed:(UIMenuController *)menuController
{
QuickMenuItem *menuItem = [[[UIMenuController sharedMenuController] menuItems] objectAtIndex: ??]; // How to tell which category is selected?
if (menuItem.indexPath)
{
[self resignFirstResponder];
// Perform action
}
}
You'll probably need to create some dynamic selectors, as described at Dynamic UIMenuItems with #selector and dynamic methods