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];
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'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
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 need to find my current location with CoreLocation, I tried multiple methods but so far my CLLocationManager has only returned 0's.. (0.000.00.000).
Here's my code (updated to work):
Imports:
#import <CoreLocation/CoreLocation.h>
Declared:
IBOutlet CLLocationManager *locationManager;
IBOutlet UILabel *latLabel;
IBOutlet UILabel *longLabel;
Functions:
- (void)getLocation { //Called when needed
latLabel.text = [NSString stringWithFormat:#"%f", locationManager.location.coordinate.latitude];
longLabel.text = [NSString stringWithFormat:#"%f", locationManager.location.coordinate.longitude];
}
- (void)viewDidLoad {
locationManager = [[CLLocationManager alloc] init];
locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters; // 100 m
[locationManager startUpdatingLocation];
}
You can find your location using CoreLocation like this:
import CoreLocation:
#import <CoreLocation/CoreLocation.h>
Declare CLLocationManager:
CLLocationManager *locationManager;
Initialize the locationManager in viewDidLoad and create a function that can return the current location as an NSString:
- (NSString *)deviceLocation {
return [NSString stringWithFormat:#"latitude: %f longitude: %f", locationManager.location.coordinate.latitude, locationManager.location.coordinate.longitude];
}
- (void)viewDidLoad
{
locationManager = [[CLLocationManager alloc] init];
locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters; // 100 m
[locationManager startUpdatingLocation];
}
And calling the deviceLocation function will return the location as expected:
NSLog(#"%#", [self deviceLocation]);
This is just an example. Initializing CLLocationManager without the user being ready for it isn't a good idea. And, of course, locationManager.location.coordinate can be used to get latitude and longitude at will after CLLocationManager has been initialized.
Don't forget to add the CoreLocation.framework in your project settings under the Build Phases tab (Targets->Build Phases->Link Binary).
With CLLocationManager you don't necessary get the location information immediately. The GPS and other devices that obtain location information might not be initialized. They can take a while before they have any information. Instead you need to create a delegate object that responds to locationManager:didUpdateToLocation:fromLocation: and then set it as the delegate of the location manager.
see here
Here you can show current location with annotation details
in ViewController.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
//My
#import <MapKit/MapKit.h>
#import <MessageUI/MFMailComposeViewController.h>
#interface ViewController : UIViewController<CLLocationManagerDelegate,MKMapViewDelegate,MFMailComposeViewControllerDelegate>
{
IBOutlet UILabel *lblLatitiude;
IBOutlet UILabel *lblLongitude;
IBOutlet UILabel *lblAdress;
}
//My
#property (nonatomic, strong) IBOutlet MKMapView *mapView;
-(IBAction)getMyLocation:(id)sender;
#end
in ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController{
CLLocationManager *locationManager;
CLGeocoder *geocoder;
CLPlacemark *placemark;
}
#synthesize mapView;
- (void)viewDidLoad
{
[super viewDidLoad];
locationManager = [[CLLocationManager alloc] init];
geocoder = [[CLGeocoder alloc] init];
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *defaultsDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:#"pradipvdeore#gmail.com", #"fromEmail",
#"pavitrarupani89#gmail.com", #"toEmail",
#"smtp.gmail.com", #"relayHost",
#"mobileapp.qa#gmail.com", #"login",
#"mobile#123", #"pass",
[NSNumber numberWithBool:YES], #"requiresAuth",
[NSNumber numberWithBool:YES], #"wantsSecure", nil];
[userDefaults registerDefaults:defaultsDictionary];
self.mapView.delegate=self;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Custom Methods
-(IBAction)getMyLocation:(id)sender{
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"didFailWithError: %#", error);
UIAlertView *errorAlert = [[UIAlertView alloc]
initWithTitle:#"Error" message:#"Failed to Get Your Location" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSLog(#"didUpdateToLocation: %#", newLocation);
CLLocation *currentLocation = newLocation;
if (currentLocation != nil) {
lblLongitude.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude];
lblLatitiude.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
}
// Stop Location Manager
[locationManager stopUpdatingLocation];
// Reverse Geocoding
NSLog(#"Resolving the Address");
[geocoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placemarks, NSError *error) {
NSLog(#"Found placemarks: %#, error: %#", placemarks, error);
if (error == nil && [placemarks count] > 0) {
placemark = [placemarks lastObject];
lblAdress.text = [NSString stringWithFormat:#"%# %#\n%# %#\n%#\n%#",
placemark.subThoroughfare, placemark.thoroughfare,
placemark.postalCode, placemark.locality,
placemark.administrativeArea,
placemark.country];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(currentLocation.coordinate, 800, 800);
[self.mapView setRegion:[self.mapView regionThatFits:region] animated:YES];
// Add an annotation
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = currentLocation.coordinate;
point.title = #"Where am I?";
point.subtitle = [NSString stringWithFormat:#"%# %#\n%# %#\n%#\n%#",
placemark.subThoroughfare, placemark.thoroughfare,
placemark.postalCode, placemark.locality,
placemark.administrativeArea,
placemark.country];
[self.mapView addAnnotation:point];
} else {
NSLog(#"%#", error.debugDescription);
}
} ];
}
//My
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKAnnotationView *annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"loc"];
annotationView.canShowCallout = YES;
annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
return annotationView;
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
[self getSignScreenShot];
MFMailComposeViewController* controller = [[MFMailComposeViewController alloc] init];
controller.mailComposeDelegate = self;
[controller setSubject:#"My Subject"];
[controller setMessageBody:#"Hello there." isHTML:NO];
if (controller) [self presentModalViewController:controller animated:YES];
}
- (void)mailComposeController:(MFMailComposeViewController*)controller
didFinishWithResult:(MFMailComposeResult)result
error:(NSError*)error;
{
if (result == MFMailComposeResultSent) {
NSLog(#"It's away!");
}
[self dismissModalViewControllerAnimated:YES];
}
//-----------------------------------------------------------------------------------
//This methos is to take screenshot of map
//-----------------------------------------------------------------------------------
-(UIImage *)getSignScreenShot
{
CGRect rect = CGRectMake(self.mapView.frame.origin.x,self.mapView.frame.origin.y-50,self.mapView.frame.size.width+60,self.mapView.frame.size.height+15);
UIGraphicsBeginImageContextWithOptions(self.mapView.frame.size, NO, 1.0);
[self.mapView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
CGImageRef imageRef = CGImageCreateWithImageInRect([screenshot CGImage], rect);
UIImage *newImage = [UIImage imageWithCGImage:imageRef];
return newImage;
}
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];
}