I found how to implement a searchBar, but I don't know what to do in updateSearchResultsForSearchController method. All my data is fetched from CoreData. Here I put my code. If anyone had similar problem please tell me what to do.
#interface PlumbListTableViewController ()<NSFetchedResultsControllerDelegate, UIPageViewControllerDelegate, UISearchBarDelegate, UISearchResultsUpdating, UISearchControllerDelegate>
#property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
#property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, strong) NSArray *array;
#property (nonatomic, strong) NSNumber *number;
#property (nonatomic) int sum;
#property (nonatomic, strong) PickerViewController *picker;
#property (nonatomic, strong) UISearchController *searchController;
#property (nonatomic, strong) NSMutableArray *fileteredTableData;
#property (nonatomic, strong) NSArray *products;
#property (nonatomic, strong) NSArray *recipies;
#property (nonatomic, strong) NSArray *searchResults;
#end
#implementation PlumbListTableViewController
-(void)viewDidLoad {
[super viewDidLoad];
//All data from CoreData
self.products = [Product allProducts];
self.searchResults = [NSMutableArray arrayWithCapacity:[self.products count]];
[self.fetchedResultsController performFetch:nil];
CoreDataStack *coreDataStack = [CoreDataStack defaultStack];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"AddEntry"];
fetchRequest.resultType = NSDictionaryResultType;
self.recipies = [coreDataStack.managedObjectContext executeFetchRequest:fetchRequest error:nil];
}
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:YES];
[self initializeSearchController];
self.tabBarController.tabBar.hidden = NO;
[self showTotalSum];
[self.tableView reloadData];
}
-(void)initializeSearchController {
//instantiate a search results controller for presenting the search/filter results (will be presented on top of the parent table view)
UITableViewController *searchResultsController = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
searchResultsController.tableView.dataSource = self;
searchResultsController.tableView.delegate = self;
//instantiate a UISearchController - passing in the search results controller table
self.searchController = [[UISearchController alloc] initWithSearchResultsController:searchResultsController];
//this view controller can be covered by theUISearchController's view (i.e. search/filter table)
self.definesPresentationContext = YES;
//define the frame for the UISearchController's search bar and tint
self.searchController.searchBar.frame = CGRectMake(self.searchController.searchBar.frame.origin.x, self.searchController.searchBar.frame.origin.y, self.searchController.searchBar.frame.size.width, 44.0);
self.searchController.searchBar.tintColor = [UIColor whiteColor];
//add the UISearchController's search bar to the header of this table
self.tableView.tableHeaderView = self.searchController.searchBar;
//this ViewController will be responsible for implementing UISearchResultsDialog protocol method(s) - so handling what happens when user types into the search bar
self.searchController.searchResultsUpdater = self;
//this ViewController will be responsisble for implementing UISearchBarDelegate protocol methods(s)
self.searchController.searchBar.delegate = self;
}
-(void)updateSearchResultsForSearchController:(UISearchController *)searchController {
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"edit"]) {
UITableViewCell *cell = sender;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
UINavigationController *navigationController = segue.destinationViewController;
PlumbAddViewController *entryViewController = (PlumbAddViewController *)navigationController.topViewController;
entryViewController.entry = [self.fetchedResultsController objectAtIndexPath:indexPath];
}
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return self.fetchedResultsController.sections.count;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
return [sectionInfo numberOfObjects];
}
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
ConfigureCellPlumb *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
AddEntry *entry = [self.fetchedResultsController objectAtIndexPath:indexPath];
[cell configureCellforTable:entry];
return cell;
}
-(NSFetchRequest *)entryListFetchRequest {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"AddEntry"];
fetchRequest.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"date" ascending:NO]];
return fetchRequest;
}
-(NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
CoreDataStack *coreDataStack = [CoreDataStack defaultStack];
NSFetchRequest *fetchRequest = [self entryListFetchRequest];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:coreDataStack.managedObjectContext sectionNameKeyPath:#"sectionName" cacheName:nil];
_fetchedResultsController.delegate = self;
return _fetchedResultsController;}
According to the docs, updateSearchResultsForSearchController: is "called when the search bar's text or scope has changed or when the search bar becomes first responder."
So, within this method you want to update your table to show the proper search results. This is what mine looks like (yours will look different but the idea is the same):
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
{
//make sure model has only results that correspond to the search
[self updateFilteredContentWithSearchText:[self.searchController.searchBar text]];
//update the table now that the model has been updated
[self.specialtySearchResultsTVC.tableView reloadData];
}
//helper method
- (void)updateFilteredContentWithSearchText:(NSString*)searchText
{
[self.specialtySearchResultsTVC.filteredSpecialties removeAllObjects];
for (Specialty *specialty in self.specialties)
{
NSRange nameRange = [specialty.name rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (nameRange.location != NSNotFound)
{
[self.specialtySearchResultsTVC.filteredSpecialties addObject:specialty];
}
}
}
Related
I was wondering how to set the progress bar equal to max user input in xcode.
#property (weak, nonatomic) IBOutlet UIProgressView *progressBar;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_inputAmount.keyboardType = UIKeyboardTypeDecimalPad;
self.amount = [NSMutableArray new];
[self.amount addObject:#"Total Amount of Push-Ups:"];
[self.myList setDataSource:self];
_inputAmount = UIProgressViewStyleBar;
}
This is just a snipet of my code, the inputamount = progress bar is what I want to do, but I'm not really sure how to do it. I want the greatest input amount the be equal to the progressviewbar as well. So i would have to compare all the #s that were added to the array. Any ideas? Thanks!
//
// ViewController.m
// Push Up Tracker
//
// Created by Paul Lesny on 10/26/14.
// Copyright (c) 2014 Paul Lesny. All rights reserved.
//
#import "ViewController.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UITextField *inputAmount;
#property (weak, nonatomic) IBOutlet UIButton *addButton;
#property NSMutableArray *amount;
#property (weak, nonatomic) IBOutlet UITableView *myList;
#property (weak, nonatomic) IBOutlet UIProgressView *progressBar;
#end
#implementation ViewController
//#synthesize progressBar, progressValue;
///NSInteger stringToInt(NSString *string) {
//return [string integerValue];
//}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_inputAmount.keyboardType = UIKeyboardTypeDecimalPad;
self.amount = [NSMutableArray new];
[self.myList setDataSource:self];
[self readDataFromFile:#"lalala1"];
if (_amount.count!=0)
{
NSMutableDictionary *max=[_amount objectAtIndex:0];
NSString *m = max[#"max"];
NSLog (#"%#",m);
NSInteger num=[m intValue];
NSLog(#"amount is not empty");
_progressBar.progress = (float) [m intValue]/50;
}
else
{
_progressBar.progress=0;
}
//[self.amount addObject:#"0"];
//[self.myList reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return[self.amount count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString * cellId = #"pancake";//identifier for the cells
// get a cell from the cache
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellId];
// if the cell is not cached then
if(cell ==nil)
{
// create a new cell
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
}
NSMutableDictionary *newEntry=_amount[indexPath.row];
[cell.textLabel setText:newEntry[#"pushUpNum"]];
// put the corresponding element of the array as text.
// return the cell.
return cell;
}
-(BOOL) textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
-(void) readDataFromFile:(NSString*)fileName
{
NSData *fileData=[NSData dataWithContentsOfURL:[self urlOfEntries:#"lalala1"]];
if(fileData!=nil)
{
_amount=[NSPropertyListSerialization
propertyListWithData:fileData options:
NSPropertyListMutableContainers format:nil error:nil];
}
else
{
_amount=[NSMutableArray new];
}
}
-(NSURL *) urlOfEntries:(NSString*)name
{
NSURL *docDirectory=[[[NSFileManager defaultManager]
URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]lastObject];
NSURL* fullPath=[docDirectory URLByAppendingPathComponent:name];
return fullPath;
}
-(void) savePushUpTotal:(NSMutableArray *)myEntries
{
[myEntries writeToURL:[self urlOfEntries:#"lalala1"]atomically:YES];
}
- (IBAction)sender:(id)addButton
{
NSMutableDictionary *newRecord = [[NSMutableDictionary alloc]init];
[newRecord setObject:self.inputAmount.text forKey:#"pushUpNum"];
if (_amount.count==0)
{
[newRecord setObject:#"0" forKey:#"max"];
}
[self.amount addObject:newRecord];
[self savePushUpTotal:_amount];
[self.myList reloadData];
[self.inputAmount resignFirstResponder];
[self savePushUpTotal:_amount];
NSMutableDictionary *max=_amount[0];
NSString *m = max[#"max"];
NSInteger num=[m intValue];
NSMutableDictionary *newDictionary=_amount[_amount.count-1];
NSString *blah = newDictionary[#"pushUpNum"];
NSInteger number=[blah intValue];
NSLog(#"dadala%#",blah);
if (_amount.count!=1)
{
if (number>num)
{
_progressBar.progress= (float)number/50;
NSString *pushUp = max[#"pushUpNum"];
NSMutableDictionary *maxPush = [[NSMutableDictionary alloc]init];
[maxPush setObject:pushUp forKey:#"pushUpNum"];
[maxPush setObject:blah forKey:#"max"];
[_amount removeObjectAtIndex:(NSUInteger)0];
[_amount insertObject:maxPush atIndex:0];
[self savePushUpTotal:_amount];
}
}
else
{
_progressBar.progress = (float)number/50;
}
}
- (IBAction)clear:(UIButton *)sender
{
_progressBar.progress=0;
[_amount removeAllObjects];
[[NSUserDefaults standardUserDefaults] setObject:_amount forKey:0];
[_myList reloadData];
}
#end
I have done this on another project before and got it working then, but I cannot find out why this piece of my programming will not work.
It displays the table without any of the plist data on it.
Please take a look through my code to see if you can spot where I have made a mistake, thanks.
#import "MarketViewController.h"
#import "SecondViewController.h"
#import "ECSlidingViewController.h"
#import "MenuViewController.h"
#interface MarketViewController ()
#end
#implementation MarketViewController
#synthesize shares, shareValues, number, shareName;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void)viewDidLoad
{
self.navigationItem.title = #"Stock Market";
NSString *sharesFile = [[NSBundle mainBundle] pathForResource:#"Shares" ofType:#"plist"];
shares = [[NSDictionary alloc] initWithContentsOfFile: sharesFile];
shareValues = [shares objectForKey:#"Share Values"];
shareName = [shares objectForKey:#"Share Names"];
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.layer.shadowOpacity = 0.75f;
self.view.layer.shadowRadius = 10.0f;
self.view.layer.shadowColor = [UIColor blackColor].CGColor;
if (![self.slidingViewController.underLeftViewController isKindOfClass:[MenuViewController class]]) {
self.slidingViewController.underLeftViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Menu"];
}
[self.view addGestureRecognizer:self.slidingViewController.panGesture];
self.menuBtn = [UIButton buttonWithType:UIButtonTypeCustom];
_menuBtn.frame = CGRectMake(8, 10, 34, 24);
[_menuBtn setBackgroundImage:[UIImage imageNamed:#"menuButton.png"] forState:UIControlStateNormal];
[_menuBtn addTarget:self action:#selector(revealMenu:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.menuBtn];
}
//Table View
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [shares count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
static NSString *cellIdentifier = #"cell";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
NSString *nameOfShare = [shareName objectAtIndex:[indexPath row]];
NSString *valueOfShare = [shareValues objectAtIndex:[indexPath row]];
cell.detailTextLabel.text = valueOfShare;
cell.textLabel.text = nameOfShare;
return cell;
self.title = #"Stock Market";
}
- (IBAction)revealMenu:(id)sender
{
[self.slidingViewController anchorTopViewTo:ECRight];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
My h. file is as follows:
#import <UIKit/UIKit.h>
#interface MarketViewController : UIViewController
<UITableViewDataSource, UITableViewDelegate>
#property (strong, nonatomic) UIButton *menuBtn;
#property (nonatomic, readonly) NSDate *CurrentDate;
#property (nonatomic, strong) NSDictionary *shares;
#property (nonatomic, strong) NSArray *shareValue;
#property (nonatomic, strong) NSArray *number;
#property (nonatomic, strong) NSArray *shareName;
- (NSString*)filePath;
- (void)openDB;
#end
I'm trying to save an array of objects with NSCoding when the home button or program is exited, but nothing seems to happen.
What am I doing wrong????
//Fieldbook.h custom object
#interface FieldBook : NSObject <NSCoding>
{
NSString *title;
NSDate *date;
}
#property (nonatomic, retain) NSString *title;
#property (nonatomic, retain) NSDate *date;
-(id) init:(NSString *)aTitle;
- (NSString *) description;
#end
//Fieldbook.m
#import "FieldBook.h"
#implementation FieldBook
#synthesize title, date;
-(id) init:(NSString *)aTitle
{
title = aTitle;
return self;
}
- (NSString *) description
{
NSString *desc = [NSString stringWithFormat:#"%#\n", title];
return desc;
}
- (id)initWithCoder:(NSCoder *)decoder
{
if (self = [super init])
{
self.title = [decoder decodeObjectForKey:#"title"];
self.date = [decoder decodeObjectForKey:#"date"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:title forKey:#"time"];
[encoder encodeObject:date forKey:#"date"];
}
#end
//ViewController.h
#interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UIAlertViewDelegate>
#property (weak, nonatomic) IBOutlet UITableView *myTableView;
#property (strong, nonatomic) NSMutableArray *fieldBooks;
#property (nonatomic, retain) UITextField *projectName;
- (NSString *)dataFilePath;
#end
//ViewController.m
#import "FieldBook.h"
#import "ViewController.h"
#implementation ViewController
#synthesize myTableView, fieldBooks, projectName;
- (void)viewDidLoad
{
// Do any additional setup after loading the view, typically from a nib.
[super viewDidLoad];
if(fieldBooks == nil)
fieldBooks = [[NSMutableArray alloc]init];
NSMutableArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithFile:[self dataFilePath]];
[self.fieldBooks addObjectsFromArray:tempArray];
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotification
object:app];
[self.myTableView reloadData];
//Set title
self.title = #"FieldBooks";
//Add edit button
self.navigationItem.leftBarButtonItem = self.editButtonItem;
//Add the add button
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self
action:#selector(insertNewObject)];
self.navigationItem.rightBarButtonItem = addButton;
}
- (void)applicationWillResignActive:(NSNotification *)notification
{
[NSKeyedArchiver archiveRootObject:self.fieldBooks toFile:[self dataFilePath]];
}
- (NSString *)dataFilePath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"datafile"];
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated];
[myTableView setEditing:editing animated:animated];
}
- (void)insertNewObject
{
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:#"Enter name"
message:#""
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;
projectName = [alert textFieldAtIndex:0];
[alert show];
}
#pragma mark - UIAlertView delegate method
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
//Only perform if OK button is pressed
if (buttonIndex == 1)
{
FieldBook *fb = [[FieldBook alloc] init:(NSString *)projectName.text];
[fieldBooks addObject:fb];
[myTableView reloadData];
NSLog(#"FieldBook created: %#", fb);
}
}
#pragma mark - UITableView Datasource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [fieldBooks count];
}
- (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];
}
FieldBook *fbName = [self.fieldBooks objectAtIndex:[indexPath row]];
cell.textLabel.text = fbName.title;
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
//remove from NSMutable array
[fieldBooks removeObjectAtIndex:indexPath.row];
//remove from table view
[myTableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
#end
Quitting my app and reloading seems to clear my array or I'm not reloading it properly. I can't see what I'm missing. webview loads and adds urltextfield to object pasturls in to array. Each time user starts typing something they already sent to webiew from textfield it displays in a tableview. Works great every time until the app is killed. Background return is fine. Please?
ViewController.h
#interface ViewController : UIViewController <UITextFieldDelegate, UIWebViewDelegate, UIActionSheetDelegate, MFMailComposeViewControllerDelegate, UITableViewDelegate, UITableViewDataSource> {
NSMutableArray *pastUrls;
NSMutableArray *autocompleteUrls;
UITableView *autocompleteTableView;
}
#property (nonatomic, retain) IBOutlet UITableView *autocompleteTableView;
#property (nonatomic, retain) NSMutableArray *pastUrls;
#property (nonatomic, retain) NSMutableArray *autocompleteUrls;
- (void)searchAutocompleteEntriesWithSubstring:(NSString *)substring;
#end
ViewController.m
#interface ViewController ()
#end
#implementation ViewController
#synthesize pastUrls;
#synthesize autocompleteUrls;
#synthesize autocompleteTableView;
- (void)webViewDidStartLoad:(UIWebView *)webView {
if (![pastUrls containsObject:urlField.text]) {
[pastUrls addObject:urlField.text];
[[NSUserDefaults standardUserDefaults] setObject:pastUrls forKey:#"PastUrls"];
}
- (void)loadView {
pastUrls = [[[NSUserDefaults standardUserDefaults] arrayForKey:#"PastUrls"] mutableCopy];
pastUrls = [[NSMutableArray alloc] initWithObjects:#"PastUrls", nil];
[super loadView];
}
- (void)viewDidLoad
{
autocompleteUrls = [[NSMutableArray alloc] init];
}
- (void)searchAutocompleteEntriesWithSubstring:(NSString *)substring {
[autocompleteUrls removeAllObjects];
for(NSString *curString in pastUrls) {
NSRange substringRange = [curString rangeOfString:substring];
if (substringRange.location == 0) {
[autocompleteUrls addObject:curString];
}
}
[autocompleteTableView reloadData];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger) section {
return [autocompleteUrls count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
static NSString *AutoCompleteRowIdentifier = #"AutoCompleteRowIdentifier";
cell = [tableView dequeueReusableCellWithIdentifier:AutoCompleteRowIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:(UITableViewCellStyle)UITableViewCellStyleDefault reuseIdentifier:AutoCompleteRowIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleGray;
}
#end
First of all, there is an issue with your pastUrls. In the loadView, you set two different contents in it :
- (void)loadView {
pastUrls = [[[NSUserDefaults standardUserDefaults] arrayForKey:#"PastUrls"] mutableCopy];
pastUrls = [[NSMutableArray alloc] initWithObjects:#"PastUrls", nil];
}
The second one should be removed. Moreover, there are some strange things in your implementation, like the loadView and viewDidLoad functions. More likely you should have :
- (void)webViewDidStartLoad:(UIWebView *)webView {
if (![pastUrls containsObject:urlField.text]) {
[pastUrls addObject:urlField.text];
[[NSUserDefaults standardUserDefaults] setObject:pastUrls forKey:#"PastUrls"];
// You might need to synchronize your NSUserDefaults content
// if so, uncomment the following line
// [[NSUserDefaults standardUserDefaults] synchronize];
}
}
// Used only if you do not use a XIB file to load the graphical interface
//- (void)loadView {
//}
- (void)viewDidLoad
{
[super viewDidLoad]; // Was missing in your current implementation
autocompleteUrls = [[NSMutableArray alloc] init];
pastUrls = [[[NSUserDefaults standardUserDefaults] arrayForKey:#"PastUrls"] mutableCopy];
}
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?