Right now, I am developing an application which is composed of a table view, with a search bar. When someone taps on one of the rows on the table, it will load a new view that has the corresponding page. This is my code:
RootViewController.h:
#interface RootViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchDisplayDelegate, UISearchBarDelegate>
{
UITableView *mainTableView;
NSMutableArray *contentsList;
NSMutableArray *searchResults;
NSString *savedSearchTerm;
}
#property (nonatomic, retain) IBOutlet UITableView *mainTableView;
#property (nonatomic, retain) NSMutableArray *contentsList;
#property (nonatomic, retain) NSMutableArray *searchResults;
#property (nonatomic, copy) NSString *savedSearchTerm;
- (void)handleSearchForTerm:(NSString *)searchTerm;
#end
RootViewController.m:
#import "RootViewController.h"
#import "Hydrogen.h"
#import "Helium.h"
#implementation RootViewController
#synthesize mainTableView;
#synthesize contentsList;
#synthesize searchResults;
#synthesize savedSearchTerm;
- (void)dealloc
{
[mainTableView release], mainTableView = nil;
[contentsList release], contentsList = nil;
[searchResults release], searchResults = nil;
[savedSearchTerm release], savedSearchTerm = nil;
[super dealloc];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Save the state of the search UI so that it can be restored if the view is re-created.
[self setSavedSearchTerm:[[[self searchDisplayController] searchBar] text]];
[self setSearchResults:nil];
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:#"Hydrogen"];
[array addObject:#"Helium"];
[self setContentsList:array];
[array release], array = nil;
// Restore search term
if ([self savedSearchTerm])
{
[[[self searchDisplayController] searchBar] setText:[self savedSearchTerm]];
}
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[self mainTableView] reloadData];
}
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)handleSearchForTerm:(NSString *)searchTerm
{
[self setSavedSearchTerm:searchTerm];
if ([self searchResults] == nil)
{
NSMutableArray *array = [[NSMutableArray alloc] init];
[self setSearchResults:array];
[array release], array = nil;
}
[[self searchResults] removeAllObjects];
if ([[self savedSearchTerm] length] != 0)
{
for (NSString *currentString in [self contentsList])
{
if ([currentString rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound)
{
[[self searchResults] addObject:currentString];
}
}
}
}
#pragma mark -
#pragma mark UITableViewDataSource Methods
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
NSInteger rows;
if (tableView == [[self searchDisplayController] searchResultsTableView])
rows = [[self searchResults] count];
else
rows = [[self contentsList] count];
return rows;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger row = [indexPath row];
NSString *contentForThisRow = nil;
if (tableView == [[self searchDisplayController] searchResultsTableView])
contentForThisRow = [[self searchResults] objectAtIndex:row];
else
contentForThisRow = [[self contentsList] objectAtIndex:row];
static NSString *CellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
// Do anything that should be the same on EACH cell here. Fonts, colors, etc.
}
// Do anything that COULD be different on each cell here. Text, images, etc.
[[cell textLabel] setText:contentForThisRow];
return cell;
}
#pragma mark -
#pragma mark UITableViewDelegate Methods
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if ([[contentsList objectAtIndex:indexPath.row] isEqual:#"Hydrogen"])
{
Hydrogen *hydrogen = [[Hydrogen alloc] initWithNibName:#"Hydrogen" bundle:nil];
[self.navigationController pushViewController:hydrogen animated:YES];
[hydrogen release];
}
else if ([[contentsList objectAtIndex:indexPath.row] isEqual:#"Helium"])
{
Helium *helium = [[Helium alloc] initWithNibName:#"Helium" bundle:nil];
[self.navigationController pushViewController:helium animated:YES];
[helium release];
}
}
#pragma mark -
#pragma mark UISearchDisplayController Delegate Methods
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString
{
[self handleSearchForTerm:searchString];
return YES;
}
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
[self setSavedSearchTerm:nil];
[[self mainTableView] reloadData];
}
#end
This code all works, perfectly, except for one flaw. When I use the search, the results appear as expected, with results matching the string that was typed in. However, when I tap on one of the rows of the search results, the corresponding XIB file does not load; instead, it loads the XIB file that corresponded with the original text in the UITableView without the search.
For example, if I were to type in the word "Helium" in the search, it would display one result saying "Helium," but when I tap into the result it loads the "Hydrogen" page, because hydrogen was the original link that was accessed when the first row was tapped.
Can anyone help me with this? I have spent many days on this code, and it is really getting frustrating.
Thank anyone out there who can help me out very, very much!
Here you go. Change your content in didSelectRowAtIndexPath to something like this:
NSArray *objectsToUse = nil;
if (tableView == [[self searchDisplayController] searchResultsTableView])
objectsToUse = [self searchResults];
else
objectsToUse = [self contentsList];
if ([[objectsToUse objectAtIndex:indexPath.row] isEqual:#"Hydrogen"])
{
Hydrogen *hydrogen = [[Hydrogen alloc] initWithNibName:#"Hydrogen" bundle:nil];
[self.navigationController pushViewController:hydrogen animated:YES];
[hydrogen release];
}
else if ([[objectsToUse objectAtIndex:indexPath.row] isEqual:#"Helium"])
{
Helium *helium = [[Helium alloc] initWithNibName:#"Helium" bundle:nil];
[self.navigationController pushViewController:helium animated:YES];
[helium release];
}
Related
I have a cocoa app with a window containing an NSTableView. Each row has a few columns, three radio boxes and two buttons. Currently the table has 260 rows and when the window is focused the scrolling in the table view is atrociously slow and jittery. When the window is not focused and I mouse over the table view and scroll it's buttery smooth.
I've tried to solve the slow performance by changing the background drawing and enabling CoreAnimation Layer to no avail.
Why would the scrolling be fast when the window isn't focused but slow when it is?
I'm just baffled as to why the scrolling is so darn slow.
Here's my ProposalTableViewController.h
#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
#import <QuickLook/QuickLook.h>
#import <Quartz/Quartz.h>
#interface ProposalTableViewController : NSObject<NSTableViewDataSource, NSTableViewDelegate, QLPreviewPanelDelegate, QLPreviewPanelDataSource>{
#public
NSMutableArray *list;
IBOutlet NSTableView *tableView;
IBOutlet NSSearchField *searchText;
IBOutlet NSTextField *countTextField;
}
#property (strong) QLPreviewPanel *previewPanel;
+ (ProposalTableViewController *)getInstance;
- (IBAction)deleteRow:(id)sender;
- (IBAction)exportData:(id)sender;
- (void)loadData;
- (void)countItems;
#end
And my ProposalTableViewController.m
#import "ProposalTableViewController.h"
#import "Proposal.h"
#import "StatusRadioView.h"
#import "DBManager.h"
#import "filePathButtonView.h"
#import <Quartz/Quartz.h>
#import "AppDelegate.h"
#implementation Proposal (QLPreviewItem)
- (NSURL *)previewItemURL
{
return [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#.pdf",self.filePath]];
}
- (NSString *)previewItemTitle
{
return [NSString stringWithFormat:#"Proposal %#",self.proposalNumber];
}
#end
#implementation ProposalTableViewController
static ProposalTableViewController *instance;
+ (ProposalTableViewController *)getInstance{
return instance;
}
//------------------------------------------------------------------------------------------------------------------------------------
// Init
//------------------------------------------------------------------------------------------------------------------------------------
- (id)init{
self = [super init];
if(self){
instance = self;
list = [[NSMutableArray alloc] init];
[self loadData];
for (NSTableColumn *tableColumn in tableView.tableColumns ) {
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:tableColumn.identifier ascending:YES selector:#selector(compare:)];
[tableColumn setSortDescriptorPrototype:sortDescriptor];
}
}
return self;
}
//------------------------------------------------------------------------------------------------------------------------------------
// Load Data from SQLite
//------------------------------------------------------------------------------------------------------------------------------------
- (void)loadData {
list = [[DBManager getProposalTable] select:#""];
NSSortDescriptor* desc = [[NSSortDescriptor alloc] initWithKey:#"proposalNumber" ascending:NO selector:#selector(compare:)];
[list sortUsingDescriptors:[NSArray arrayWithObjects:desc, nil]];
[tableView reloadData];
[self countItems: list];
}
//------------------------------------------------------------------------------------------------------------------------------------
// Number of Rows in Table View
//------------------------------------------------------------------------------------------------------------------------------------
- (NSInteger) numberOfRowsInTableView:(NSTableView *)tableView {
return [list count];
}
-(void)countItems:(NSArray *)list; {
NSString *countText;
int size = [list count];
if (size == 1){
countText = #"item in list";
}else{
countText = #"items in list";
}
NSString *itemCount = [NSString stringWithFormat:#"%d %#", size, countText];
[countTextField setStringValue:itemCount];
}
//------------------------------------------------------------------------------------------------------------------------------------
// Get View for Table Column
//------------------------------------------------------------------------------------------------------------------------------------
- (NSView *)tableView:(NSTableView *)table_view viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
Proposal *p = [list objectAtIndex:row];
NSString *identifier = [tableColumn identifier];
NSString *holdingValue;
NSTableCellView *cell = [table_view makeViewWithIdentifier:identifier owner:self];
if([identifier isEqualToString:#"status"]){
StatusRadioView *radioView = [[StatusRadioView alloc] initWithProposal:p];
return radioView;
}else if ([identifier isEqualToString:#"filePath"]){
filePathButtonView *buttonView = [[filePathButtonView alloc]initWithProposal:p];
return buttonView;
}else if ([identifier isEqualToString:#"clientAccessPoint"]){
holdingValue = [p valueForKey:identifier];
if (!holdingValue){
cell.textField.stringValue = #"N/A";
}else{
cell.textField.stringValue = [p valueForKey:identifier];
}
}else{
cell.textField.stringValue = [p valueForKey:identifier];
}
return cell;
}
//------------------------------------------------------------------------------------------------------------------------------------
// Sort Descriptors did Change
//------------------------------------------------------------------------------------------------------------------------------------
-(void)tableView:(NSTableView *)mtableView sortDescriptorsDidChange:(NSArray *)oldDescriptors {
[list sortUsingDescriptors: [mtableView sortDescriptors]];
[tableView reloadData];
}
//table row height
-(CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row {
return 25;
}
//------------------------------------------------------------------------------------------------------------------------------------
// Search Text did Change
//------------------------------------------------------------------------------------------------------------------------------------
- (void)controlTextDidChange:(NSNotification *)notification {
NSSortDescriptor* desc = [[NSSortDescriptor alloc] initWithKey:#"proposalNumber" ascending:NO selector:#selector(compare:)];
NSTextField *textField = [notification object];
NSString *str = [textField stringValue];
list = [[DBManager getProposalTable] select:str];
[list sortUsingDescriptors:[NSArray arrayWithObjects:desc, nil]];
[tableView reloadData];
[self countItems: list];
}
//------------------------------------------------------------------------------------------------------------------------------------
// Export DATA
//------------------------------------------------------------------------------------------------------------------------------------
- (IBAction)exportData:(id)sender{
NSString *content = #"";
for(Proposal *p in list){
NSString *row = [NSString stringWithFormat:#"%#,%#,%#,%#,%#,%#,%#,%#,%#,%#", p.proposalNumber,p.itemNumber,p.clientName,p.medium,p.support,p.cost,p.dateCreated,p.status,p.dateStatusChanged,p.clientAccessPoint];
row = [row stringByReplacingOccurrencesOfString:#"\n" withString:#""];
row = [NSString stringWithFormat:#"%#\n", row];
content = [content stringByAppendingString:row];
}
//get the documents directory:
NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:#"Baumgartner Fine Art Restoration"];
NSString *fileName = [documentsDirectory stringByAppendingPathComponent:#"proposalBuilderDatabase.csv"];
[[NSFileManager defaultManager] createDirectoryAtPath:documentsDirectory
withIntermediateDirectories:NO
attributes:nil
error:nil];
[content writeToFile:fileName
atomically:NO
encoding:NSStringEncodingConversionAllowLossy
error:nil];
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:#"Export Succeeded"];
[alert addButtonWithTitle:#"Ok"];
[alert runModal];
}
//------------------------------------------------------------------------------------------------------------------------------------
// Delete row from DB and table
//------------------------------------------------------------------------------------------------------------------------------------
- (IBAction)deleteRow:(id)sender{
NSString *row = list[tableView.selectedRow];
NSString *mid = [row valueForKey:#"m_id"];
ProposalTable *deleteRow = [[ProposalTable alloc] init];
[deleteRow deleteWithId: mid];
[self loadData];
[tableView reloadData];
}
//------------------------------------------------------------------------------------------------------------------------------------
// QuickLook
//------------------------------------------------------------------------------------------------------------------------------------
- (NSInteger)numberOfPreviewItemsInPreviewPanel:(QLPreviewPanel *)panel {
return [list count];
}
- (id <QLPreviewItem>)previewPanel:(QLPreviewPanel *)panel previewItemAtIndex:(NSInteger)index {
return list[tableView.selectedRow];
}
- (BOOL)previewPanel:(QLPreviewPanel *)panel handleEvent:(NSEvent *)event {
// redirect all key down events to the table view
if ([event type] == NSKeyDown) {
[tableView keyDown:event];
return YES;
}
return NO;
}
// This delegate method provides the rect on screen from which the panel will zoom.
- (NSRect)previewPanel:(QLPreviewPanel *)panel sourceFrameOnScreenForPreviewItem:(id <QLPreviewItem>)item {
return NSZeroRect;
}
- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)rowIndex {
[[QLPreviewPanel sharedPreviewPanel]reloadData];
return YES;
}
#end
Here's my ProposalTableView.h
#import <Cocoa/Cocoa.h>
#interface ProposalTableView : NSTableView
#end
And my ProposalTableView.m
#import "ProposalTableView.h"
#import "AppDelegate.h"
#implementation ProposalTableView
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
self.wantsLayer = YES;
// Drawing code here.
}
- (void)keyDown:(NSEvent *)theEvent
{
NSString *key = [theEvent charactersIgnoringModifiers];
if ([key isEqual:#" "])
{
[[NSApp delegate] togglePreviewPanel:self];
}
else
{
[super keyDown:theEvent];
}
}
#end
There's also code that establishes the SqLite DB connection and interacts with the DB to get the records or delete etc... but that's not really needed here... I also have code that draws the radio buttons and the other buttons but again, I don't think that's necessary unless someone thinks that the drawing is creating the slowdown...?
So it turns out that the drawing of the radio buttons is what is causing the slowdown... back to the drawing board.
I am working on a project that has a simple tableview with detail view.
Data source is a plist. I am trying to allow user input to be saved into the plist and shown in tableview. i have created a add view controller which gets presented and dismissed modally and has two text fields which allow the user to add the name of the city and name of the states, also a text field to input description.
Problem: how to save this data to my existing plist and show it in the tableview. Here is my code for the table view:
#implementation TableViewController
#synthesize content, searchResults;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
content = [[NSArray alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"]];
}
- (IBAction)add;
{
AddViewController* controller = [[AddViewController alloc] init];
[self presentViewController:controller animated:YES completion:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [self.searchResults count];
} else {
return [self.content count];
}
}
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat: #"SELF['city'] BEGINSWITH[c] %# ", searchText];
searchResults = [[content filteredArrayUsingPredicate:resultPredicate] retain];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: CellIdentifier] autorelease];
}
if (tableView == self.searchDisplayController.searchResultsTableView) {
cell.textLabel.text = [[searchResults objectAtIndex:indexPath.row] valueForKey:#"city"];
cell.detailTextLabel.text = [[searchResults objectAtIndex:indexPath.row] valueForKey:#"state"];
cell.imageView.image = [UIImage imageNamed:[[self.searchResults objectAtIndex:indexPath.row] valueForKey:#"cityImage"]];
} else {
cell.textLabel.text = [[self.content objectAtIndex:indexPath.row] valueForKey:#"city"];
cell.detailTextLabel.text = [[self.content objectAtIndex:indexPath.row] valueForKey:#"state"];
cell.imageView.image = [UIImage imageNamed:[[self.content objectAtIndex:indexPath.row] valueForKey:#"cityImage"]];
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
[self performSegueWithIdentifier: #"showDetails" sender: self];
}
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showDetails"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
DetailViewController *DVC = [segue destinationViewController];
if ([self.searchDisplayController isActive]) {
DVC.cityImageString = [[searchResults objectAtIndex:indexPath.row] valueForKey:#"cityImage"];
DVC.cityTextString = [[searchResults objectAtIndex:indexPath.row] valueForKey:#"cityText"];
DVC.cityNameString = [[searchResults objectAtIndex:indexPath.row] valueForKey:#"city"];
DVC.stateNameString = [[searchResults objectAtIndex:indexPath.row] valueForKey:#"state"];
} else {
DVC.cityImageString = [[self.content objectAtIndex:indexPath.row] valueForKey:#"cityImage"];
DVC.cityTextString = [[self.content objectAtIndex:indexPath.row] valueForKey:#"cityText"];
DVC.cityNameString = [[self.content objectAtIndex:indexPath.row] valueForKey:#"city"];
DVC.stateNameString = [[self.content objectAtIndex:indexPath.row] valueForKey:#"state"];
}
}
}
and here is the code for addViewController.h:
#interface AddViewController : UIViewController <UINavigationControllerDelegate,UIImagePickerControllerDelegate>{
IBOutlet UITextField *cityTextField;
IBOutlet UITextField *stateTextField;
IBOutlet UITextView *cityDescription;
UIImagePickerController* imagePicker;
}
#property (nonatomic, copy) NSString* name;
#property (nonatomic, copy) NSString* description;
#property (nonatomic, strong) UIImage* image;
#property (nonatomic, retain) IBOutlet UINavigationBar* navigationBar;
#property (nonatomic, strong) UITextField *cityTextField;
#property (nonatomic, strong) UITextField *stateTextField;
#property (nonatomic, strong) UITextView *cityDescription;
#property (nonatomic, strong) IBOutlet UIButton* choosePhotoButton;
#property (nonatomic, strong) IBOutlet UIButton* takePhotoButton;
- (IBAction)save;
- (IBAction)cancel;
- (IBAction)choosePhoto;
- (IBAction)takePhoto;
#end
and at last the add view controller .m
#implementation AddViewController
#synthesize cityTextField, stateTextField, cityDescription;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (IBAction)save
{
// Make sure the user has entered at least a recipe name
if (self.cityTextField.text.length == 0)
{
UIAlertView* alertView = [[UIAlertView alloc]
initWithTitle:#"Whoops..."
message:#"Please enter a city name"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
[alertView release];
return;
}
if (self.stateTextField.text.length == 0)
{
UIAlertView* alertView = [[UIAlertView alloc]
initWithTitle:#"Whoops..."
message:#"Please enter a city name"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
[alertView release];
return;
}
// Make sure the user has entered at least a recipe name
if (self.cityDescription.text.length == 0)
{
UIAlertView* alertView = [[UIAlertView alloc]
initWithTitle:#"Whoops..."
message:#"Please enter city description"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
[alertView release];
return;
}
self.name = self.cityTextField.text;
self.name = self.stateTextField.text;
self.description = self.cityDescription.text;
if ([[self parentViewController] respondsToSelector:#selector(dismissViewControllerAnimated:)]){
[[self parentViewController] dismissViewControllerAnimated:YES completion:nil];
} else {
[[self presentingViewController] dismissViewControllerAnimated:YES completion:nil];
}
}
- (IBAction)cancel {
{
if ([[self parentViewController] respondsToSelector:#selector(dismissModalViewControllerAnimated:)]){
[[self parentViewController] dismissViewControllerAnimated:YES completion:nil];
} else {
[[self presentingViewController] dismissViewControllerAnimated:YES completion:nil];
}
}
}
- (IBAction)choosePhoto
{
// Show the image picker with the photo library
imagePicker = [[UIImagePickerController alloc] init];
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
imagePicker.delegate = self;
imagePicker.allowsEditing = YES;
[self presentViewController:imagePicker animated:YES completion:nil];
}
- (IBAction)takePhoto {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
//picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
[self presentViewController:picker animated:YES completion:NULL];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if ([cityDescription isFirstResponder] && [touch view] != cityDescription) {
[cityDescription resignFirstResponder];
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[cityTextField resignFirstResponder];
[stateTextField resignFirstResponder];
}
#pragma mark -
#pragma mark UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info
{
// We get here when the user has successfully picked an image.
// Put the image in our property and set it on the button.
if (imagePicker) {
self.image = [info objectForKey:UIImagePickerControllerEditedImage];
[self.choosePhotoButton setImage:self.image forState:UIControlStateNormal];
} else {
if (picker) {
self.image = [info objectForKey:UIImagePickerControllerEditedImage];
[self.takePhotoButton setImage:self.image forState:UIControlStateNormal];
}
}
[self dismissViewControllerAnimated:YES completion:nil];
[imagePicker release];
imagePicker = nil;
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController*)picker
{
[self dismissViewControllerAnimated:YES completion:nil];
[imagePicker release];
imagePicker = nil;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
i know there are tons of almost similar questions and believe me i have looked at so many of them and i think i am hitting the wall with this one and i cant think of a proper way to do this. it may be so simple but for the life of me i cant figure this out and thats why i am trying to ask for help. i truly appreciate any help i can get. if needs be i can place the sample project on git hub for ease of access. also the project is built in ios 6 and the latest Xcode.
P.S. here is the link to the project on git hub: https://github.com/AdrianPhillips/TableSearch
I can't find your code that do the actual "save" work. I guess it should be in the UIAlertView delegate,right ? And you did not tell us what's your problem. Following code maybe what's you seeking.
[content writeToFile:filePath atomically:YES]
Another reminder is: you should NOT save the plist back to main bundle, save it to Documents or Cache or other folder.
Just as every one has pointed out you can not write to the plist that is in your main bundle. You need to copy it from the bundle to your application document directory. Then you can just init from and write to the path in your documents directory. The other issue you have, is that nothing is communicating the new data from the AddViewController back to the TableViewController. You should create a protocol and delegate for your AddViewController.
For all the gore details see my pull request on GitHub.
https://github.com/GayleDDS/TableSearch.git
You have to copy the plist to the Documents folder first. You can't edit it into the original app folder. You can use this class to do this:
- (void) CreatePlistCopyInDocuments:(NSString *)plistName {
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:plistName];
success = [fileManager fileExistsAtPath:writablePath];
if (success) {
return;
}
// The writable file does not exist, so copy from the bundle to the appropriate location.
NSString *defaultPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:plistName];
success = [fileManager copyItemAtPath:defaultPath toPath:writablePath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable file with message '%#'.", [error localizedDescription]);
}
}
You have just to pass the plist name to it.
Then you have to load it into some NSMutableDictionary like this:
NSString *documents = [NSHomeDirectory() stringByAppendingString:#"/Documents"];
NSString *plistInDocuments = [documents stringByAppendingPathComponent:#"UserData.plist"];
NSMutableDictionary *plistData = [[NSMutableDictionary alloc]initWithContentsOfFile:plistInDocuments];
After you have updated the values for the dictionary, you have to write the file back to the documents like this:
[plistData writeToFile:plistInDocuments atomically:YES];
I'm pretty new to xcode so I'm having trouble performing this task. I've created a table with a search bar with names that are passed to a detail view with a UILabel that shows the corresponding name of the cell clicked. The search bar works and it filters the results. I used this tutorial to help me with it:
http://www.appcoda.com/how-to-add-search-bar-uitableview/
Now I want to have an image in the detail view instead of a UILabel, that corresponds to each of the cells but I'm having trouble figuring out how to do that. Here is the code I'm working with:
TableViewController.h:
#interface SearchViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, strong) IBOutlet UITableView *tableView;
TableViewController.m:
#interface SearchViewController ()
#end
#implementation SearchViewController {
NSArray *cards;
NSArray *searchResults;}
#synthesize tableView = _tableView;
-(void)viewDidLoad
{
[super viewDidLoad];
cards = [NSArray arrayWithObjects:
#"Snivy",
#"Servine",
#"Serperior",
#"Tepig",
#"Pignite",
#"Emboar",
#"Oshawott",
#"Dewott",
#"Samurott", nil];
}
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"SELF contains[cd] %#",
searchText];
searchResults = [cards filteredArrayUsingPredicate:resultPredicate];}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [searchResults count];
} else {
return [cards count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SearchCardCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
if (tableView == self.searchDisplayController.searchResultsTableView) {
cell.textLabel.text = [searchResults objectAtIndex:indexPath.row];
} else {
cell.textLabel.text = [cards objectAtIndex:indexPath.row];
}
return cell;}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"ShowSearchCard"]) {
SearchCardViewController *destViewController = segue.destinationViewController;
NSIndexPath *indexPath = nil;
if ([self.searchDisplayController isActive]) {
indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
destViewController.cardName = [searchResults objectAtIndex:indexPath.row];
} else {
indexPath = [self.tableView indexPathForSelectedRow];
destViewController.cardName = [cards objectAtIndex:indexPath.row];
}
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
[self performSegueWithIdentifier: #"ShowSearchCard" sender: self];
}
}
UIViewController.h:
#property (strong, nonatomic) IBOutlet UILabel *cardLabel;
#property (strong, nonatomic) NSString *cardName;
#property (strong, nonatomic) NSArray *searchCardDetail;
UIViewController.m:
#implementation SearchCardViewController
#synthesize cardLabel;
#synthesize cardName;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
cardLabel.text = cardName;
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewDidUnload {
[super viewDidUnload];
}
So in detail, the "cards" are the names in the table. Right now, it segue to a UILabel of the name of the card and I would like to have it instead, segue to the corresponding image of the card in the regular table and filtered table when searched. I appreciate your time and help! Thanks!
I'm not sure if I understanded your question correctly, but I would use one of these three approaches:
1st:
Easiest way is to follow this tutorial
2nd: Create custom cell which you will use instead of default cell. To get you on track, you can check this good tutorial How to create custom cell. In your custom cell, you'll declare additional variable, UIImage, which will or won't be displayed in your table view. Point is, you can send it in prepareForSegue method to your detailViewController.
3rd: Create NSDictionary where you will have values (images of your items) for keys (name of item). Then, pass it in prepareForSegue to your NSDictionary in detailViewController. After that, just assign your UIImage to UIImageView in your detailViewController based on what name of item did you receive. (So you'll still be sending name and then detailImage = [yourdict objectForKey:myItemName];)
I'm writing this on my windows laptop, because I'm not at work right now (that's where I have my mac mini), so there may be some syntax errors in my answer :)
I have a class ClsDatabase that is called to save / retrieve data.
There are also 2 views. The 2nd view have table that will display information based on what ClsDataBase retrieved. A tab-bar controls the 2 views.
It can load initially with the 1st view shown. However, when I select the 2nd view, it stops at main.m's
return UIApplicationMain(argc, argv, nil, NSStringFromClass([TestTabBarAppDelegate class]));
with error message: EXC_BAD_ACCESS.
I tried create another project but without ClsDatabase and just print some dummmy values in table, and it works. Not sure if its because of the class ClsDatabase
AppDelegate.m
#import "TestTabBarAppDelegate.h"
#import "TestTabBarFirstViewController.h"
#import "TestTabBarSecondViewController.h"
#implementation TestTabBarAppDelegate
#synthesize window = _window;
#synthesize tabBarController = _tabBarController;
- (void)dealloc
{
[_window release];
[_tabBarController release];
[super dealloc];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
UIViewController *viewController1 = [[[TestTabBarFirstViewController alloc] initWithNibName:#"TestTabBarFirstViewController" bundle:nil] autorelease];
UIViewController *viewController2 = [[[TestTabBarSecondViewController alloc] initWithNibName:#"TestTabBarSecondViewController" bundle:nil] autorelease];
self.tabBarController = [[[UITabBarController alloc] init] autorelease];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:viewController1, viewController2, nil];
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
return YES;
}
(2nd view).h
#import <UIKit/UIKit.h>
#import "ClsDatabase.h"
#interface TestTabBarSecondViewController : UIViewController<UIApplicationDelegate, UITabBarControllerDelegate>{
NSArray *arrDebtor;
ClsDatabase *dbDebtor;
}
#end
(2nd view.m)
#import "TestTabBarSecondViewController.h"
#implementation TestTabBarSecondViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"Second", #"Second");
self.tabBarItem.image = [UIImage imageNamed:#"second"];
}
return self;
}
- (void)viewDidLoad
{
NSString *strSql;
dbDebtor = [[ClsDatabase alloc] initWithDbName:#"Debt"];
arrDebtor = [[NSArray alloc] init];
strSql = [NSString stringWithFormat:#"SELECT * FROM Debtor"];
arrDebtor = [dbDebtor ReturnQueryArray:strSql];
[super viewDidLoad];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if ([arrDebtor count] == 0) {
return 1;
}
else {
return [arrDebtor count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Debtor";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if ([arrDebtor count] == 0) {
if (indexPath.row == 1) {
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
}
cell.textLabel.text = [NSString stringWithFormat:#"No tables or records found"];
return cell;
}
}
else {
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
}
NSMutableDictionary *dictDebtor = [arrDebtor objectAtIndex:indexPath.row];
cell.textLabel.text = [dictDebtor valueForKey:#"Name"];
return cell;
}
}
You are doing lot's of things in code that should be done with IB and storyboarding.
Your logic in cellForRowAtIndexPath will fail on the first row, when indexPath.row is 0 and your arrDebtor is empty and your cell has not been created yet. That would explain the EXC_BAD_ACCESS error.
I am having a real problem finding where my problem is in my search controller. This is a table view with search bar and search display controller. It used to work fine, but all the sudden it stopped working. I turned on NSZombieEnabled and it shows that my NSArray called searchDataSource is the zombie.
When you type a search term the "shouldReloadTableForSearchTerm" executes the handleSearchForTerm function. The handleSearchForTerm" function accesses my ProductInfo class that query a SQLite database and returns the query results. Those results are then placed in my searchDataSource Array. Everything appears to work fine there. However, once I get to the "cellForRowAtIndexPath" function and I try to load the cells from the searchDataSource, that is when I run in to the problem of the Array having been deallocated.
Here is my code for the search controller:
//
// SearchViewController.h
// Priority Wire
//
// Created by Keith Yohn on 2/2/11.
// Copyright 2011 Priority Wire & Cable. All rights reserved.
//
#import <UIKit/UIKit.h>
#interface FourthViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchDisplayDelegate, UISearchBarDelegate> {
UITableView *mainTableView;
NSArray *searchDataSource;
NSMutableArray *contentsList;
NSMutableArray *searchResults;
NSString *savedSearchTerm;
NSString *webURL;
}
#property (nonatomic, retain) IBOutlet UITableView *mainTableView;
#property (nonatomic, retain) IBOutlet NSArray *searchDataSource;
#property (nonatomic, retain) NSMutableArray *contentsList;
#property (nonatomic, retain) NSMutableArray *searchResults;
#property (nonatomic, copy) NSString *savedSearchTerm;
#property (nonatomic, retain) NSString *webURL;
- (void)handleSearchForTerm:(NSString *)searchTerm;
#end
SearchViewController.m
//
// SearchViewController.m
// Priority Wire
//
// Created by Keith Yohn on 2/2/11.
// Copyright 2011 Priority Wire & Cable. All rights reserved.
//
#import "FourthViewController.h"
#import "ProductsDatabase.h"
#import "ProductInfo.h"
#import "WebViewController.h"
#implementation FourthViewController
#synthesize mainTableView;
#synthesize searchDataSource;
#synthesize contentsList;
#synthesize searchResults;
#synthesize savedSearchTerm;
#synthesize webURL;
- (void)viewDidLoad {
[super viewDidLoad];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.searchDataSource count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
// Set up the cell...
ProductInfo *info = [searchDataSource objectAtIndex:indexPath.row]; //This is where I get the 'message sent to deallocated instance' message.
[cell.textLabel setText:info.sName];
[cell.detailTextLabel setText:info.sType];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
ProductInfo *info = [searchDataSource objectAtIndex:indexPath.row];
webURL = [NSString stringWithFormat:#"http://www.prioritywire.com/specs/%#", info.sFile];
WebViewController *wvController = [[WebViewController alloc] initWithNibName:#"WebViewController" bundle:[NSBundle mainBundle]];
wvController.URL = webURL;
wvController.navTitle = #"Spec Sheet";
[self.navigationController pushViewController:wvController animated:YES];
[wvController release];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Relinquish ownership any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Save the state of the search UI so that it can be restored if the view is re-created.
[self setSavedSearchTerm:[[[self searchDisplayController] searchBar] text]];
[self setSearchResults:nil];
}
- (void)dealloc {
[searchDataSource release], searchDataSource = nil;
[mainTableView release];
[contentsList release];
[searchResults release];
[savedSearchTerm release];
[super dealloc];
}
- (void)handleSearchForTerm:(NSString *)searchTerm
{
[self setSavedSearchTerm:searchTerm];
if ([self searchResults] == nil)
{
NSMutableArray *array = [[NSMutableArray alloc] init];
[self setSearchResults:array];
[array release], array = nil;
} else {
NSArray *productInfo = [[ProductsDatabase database] searchListing:searchTerm];
self.searchDataSource = productInfo;
[self.mainTableView reloadData];
[productInfo release];
}
[[self searchResults] removeAllObjects];
if ([[self savedSearchTerm] length] != 0)
{
for (NSString *currentString in [self contentsList])
{
if ([currentString rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound)
{
[[self searchResults] addObject:currentString];
}
}
}
}
#pragma mark -
#pragma mark UISearchDisplayController Delegate Methods
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self handleSearchForTerm:searchString];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
[self setSavedSearchTerm:nil];
self.searchDataSource = nil;
[self.mainTableView reloadData];
}
#end
I am quite new to objective-C and can't understand what I did wrong. I have spent days trying to figure this out and have had no luck. I would appreciate any help anyone can offer.
Keith
This bit of code seems to be the only place searchDataSource gets set:
NSArray *productInfo = [[ProductsDatabase database] searchListing:searchTerm];
self.searchDataSource = productInfo;
[self.mainTableView reloadData];
[productInfo release];
If ProductsDatabase follows the rules, you don't own the returned array (i.e. it is already autoreleased) so the release on the fourth line is incorrect.
Don't you mean to use your searchResults-array instead of your searchDataSource, because in handleSearchForTerm: you are adding the results to it. Why do you even have the searchResult ivar? It's only used in handleSearchForTerm:, maybe some mixup there?