Determining indexes in another ViewController - iphone

My NSMutableArray is from the NSCachesDirectory,I'm recreating/reloading the array in every VIEW. I'm displaying/preview it in UIScrollView which can be deleted in VIEW_A. In another ViewController VIEW_B, I have another preview of it, for another purpose.
What I needed is when I delete the image in VIEW_A, I will be able to determine in VIEW_B the deleted images or index. Because I'm using their indexes in VIEW_B. How can I be able to do it. I'm thinking of saving it all in NSUserDefaults but how.
Delete method:
[button removeFromSuperview];
[_buttons removeObjectAtIndex:button.tag];
[_images removeObjectAtIndex:button.tag];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"oneSlotImages%u.png", button.tag]];
[fileManager removeItemAtPath: fullPath error:NULL];
I just want to know/determine that indexes that are deleted from other view.

I'm not sure if i really understood your problem, but here's my response:
If you need to update some object after something change (like your array). You should use a pattern observer. You can get free one with NSNotificationCenter (in foundation lib), but i discourage it. You can use kvo/kvc but it's not as clean as if you'll do in a pattern observer.
By the way, you should use only one array, and if you need to perform change on it, use a specific controller to do it. (Not from the client side like in your UIViewA/B)
here on wiki how to use it (in java, but the skeleton is the same):
http://en.wikipedia.org/wiki/Observer_pattern
By the way NSUserDefault should be only used to store user preferences, not some app logic values.

Best option is to use a delegate method.
In VIEW_B set up a delegate protocol and in the delete method have a call to something like...
[self.delegate didDeleteImageAtIndex:button.tag];
then in VIEW_A before you push VIEW_B set it up as the delegate.
Then in VIEW_A have the method...
- (void)didDeleteImageAtIndex:(int)index
{
//delete image from VIEW_A's array
}

Related

iPhone refresh a view from another class

I want to be able to refresh a view from another class, but nothing I have tried is working. My application is a tabbed application with several tabs set up. I have a method called refresh in my ViewController1 class that looks like this
-(void)TestMe{
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"widgjson" ofType:#"json"];
NSData *myData = [NSData dataWithContentsOfFile:filePath];
NSString *responseString = [[NSString alloc] initWithData:myData encoding:NSUTF8StringEncoding];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [paths objectAtIndex: 0];
NSString *docFile = [docDir stringByAppendingPathComponent: #"json.txt"];
[responseString writeToFile:docFile atomically:NO encoding:NSUTF8StringEncoding error:Nil];
[self loadView];
[self viewDidLoad];
}
This works fine. When the application first loads up, it loads a different json, then when I click this button it loads a new JSON and then updates the view. This is just temporary to test out refreshing. However if I try to call this method from another class, or from the AppDelegates ApplicationDidEnterForeground methods, nothing happens. I tried calling it like this
-(void)TestMe{
ViewController1 *vc = [[ViewController1 alloc] init];
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"widgjson" ofType:#"json"];
NSData *myData = [NSData dataWithContentsOfFile:filePath];
NSString *responseString = [[NSString alloc] initWithData:myData encoding:NSUTF8StringEncoding];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [paths objectAtIndex: 0];
NSString *docFile = [docDir stringByAppendingPathComponent: #"json.txt"];
[responseString writeToFile:docFile atomically:NO encoding:NSUTF8StringEncoding error:Nil];
[vc loadView];
[vc viewDidLoad];
}
So why does this method not work from any other classes. All I want to is to be able to refresh a view from another class or when the application loads up, but just can't seem to find anything that works.
Would be very grateful if someone could point me in right direction!
EDIT: UPDATING TO CLARIFY
Okay, what I have done for the time being which I don't like is put this
exit(0);
Inside AppDelegates ApplicationDidEnterBackground. This works to an extent, but is not an ideal solution.
What I would like to have happen is that when application is opened again, the AppDelegate gets run again which sets up the tabs. The JSON pulled from the server can affect the order of the tabs which is why I need the AppDelegates ApplicationDidFinishLaunching method to reload itself again.
Also I would like to know can I do this from another class in the application. The first time my application is loaded it asks for the users phone number, which is sent to the server which then generates an PIN. After this is done, then I want the AppDelegate method to load up and begin setting up the tabs and the order etc.
To prevent your application running in the background (and so to force reloading when you exit and re-enter) you can set the UIApplicationExitsOnSuspend key to YES in your info.plist.
However, this is probably a bit drastic.
Your current code for refreshing from the application delegate is a non-starter because you are creating a new instance of the view controller rather than talking to one that is on the screen.
For this reason it is best not to involve your application delegate at all in the process.
Instead, your view controller can register itself as an observer for the UIApplicationDidBecomeActive notification, and do whatever it needs to itself. You would typically register for this notification on viewDidLoad, and unregister on viewDidUnload (since, by definition, if you have unloaded the view, you don't need to refresh it when the app comes back up!).
You would register as follows (using blocks here as they are the future):
Declare an ivar of type id to hold the observer object, let's call it becomeActiveObserver in this case. So, wherever you are declaring your ivars, add id becomeActiveObserver. Then, in viewDidLoad:
becomeActiveObserver = [[NSNotificationCenter defaultCenter]
addObserverForName:UIApplicationDidBecomeActive
object:nil
queue: nil
usingBlock:^(NSNotification *note){
//put your reloading code in here
[self TestMe];
}];
And remove like so in viewDidUnload:
[[NSNotificationCenter defaultCenter] removeObserver:becomeActiveObserver;
You can follow a similar pattern to pick up notifications (your own, or system ones) from anywhere else in your app. This is a better design than passing everything through the app delegate as it involves much looser coupling.
I guess clickMe is an action to a button.if you want to refresh the viewcontroller at startup then add a function in viewcontroller similar to clickme or any function that refreshes the view and call it from applicationWillEnterForeground method or you may try this :-
- (void)applicationWillEnterForeground:(UIApplication *)application
{
NSLog(#"app will enter foreground");
[viewController clickMe:NULL];
}
and you may change the file in the applicationWillEnterForeground method of appDelegate if your application requires.

iOS iPhone show user direction and orientation in space like the compass app on MKMapView

When the compass app uses a map view to display it's location, there's a little cone that displays the direction in which the phone is pointing. However, I was unable to reproduce it using MKMapView that shows user's location. Is this cone of sight feature available to developers, or will I have to implement one myself?
Thank you!
This is extremely easy like a piece of cake with MapKit.
This is called UserTrackingMode. You have write down just one line of code to make the map become "compass-like":
[mapView setUserTrackingMode:MKUserTrackingModeFollowWithHeading animated:YES];
In addition, there are about 3 type of UserTrackingMode:
- MKUserTrackingModeNone (free scrolling map)
- MKUserTrackingModeFollow (center the user's location)
- MKUserTrackingModeFollowWithHeading (center user's location and track user's heading (direction)).
If you're in one of 2 tracking (not free) mode, then you scroll the map out, the mode will be automatically change to free mode.
I faced a similar situation. I don't think we have library or settings to display the direction on the blue icon (at least my search was not successful).
However it is not difficult to create our own direction indicator using the CLHeading (reference in TommyG's answer).
What I did was to display the blue icon as in the map and provide a small arrow in a different view to indicate the direction.
Hope this helps in some way
Here is the relevant framework that you can use for this matter:
http://developer.apple.com/library/ios/#documentation/CoreLocation/Reference/CLHeading_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40008772
MTLocation has a great bundle of images including the cone that you are after:
https://github.com/myell0w/MTLocation/tree/master/Resources/MTLocation.bundle
Suppose you have a temporary image called "imageTexture.jpg" saved in cache directory. The favorite one "FavoritePhoto.jpg" is saved in documents directory.
To overwrite the favorite one in the documents directory you can do like this.
NSError *errorDesc;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *statesDescriptionPath = [documentsDirectory stringByAppendingPathComponent:#"FavoritePhoto.jpg"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *cacheDirectory = [NSFileManager getCacheDirectory];
NSString *temporaryPath = [cacheDirectory stringByAppendingPathComponent:#"imageTexture.jpg"];
NSURL *originalURL = [NSURL fileURLWithPath:statesDescriptionPath];
[fileManager replaceItemAtURL:originalURL withItemAtURL:[NSURL fileURLWithPath:temporaryPath] backupItemName:nil options:NSFileManagerItemReplacementUsingNewMetadataOnly resultingItemURL:&originalURL error:&errorDesc];
if (errorDesc)
{
NSLog(#"there was an error overwriting the favorite photo: %#", errorDesc.description);
}
I am using a NSFileManager category to get the cache directory
Here is the code for NSFileManager+Powertools.h
#import <Foundation/Foundation.h>
#interface NSFileManager (Powertools)
+ (NSString *)getCacheDirectory;
#end
Here you can see the code for NSFileManager+Powertools.m
#import "NSFileManager+Powertools.h"
#implementation NSFileManager (Powertools)
+ (NSString *)getCacheDirectory
{
NSString *path = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
if ([paths count])
{
NSString *bundleName =
[[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleIdentifier"];
path = [[paths objectAtIndex:0] stringByAppendingPathComponent:bundleName];
}
return path;
}
#end

Why redefine pListPath in didSelectRowAtIndexPath?

So I've read a ton of SO-questions about plists and how to save to them, and although I don't know, why no iPhone-Dev-Book I've seen so far covered this (they all used the tableView editing function), I managed to REALLY write to a plist by copying it to the documents folder like this:
pListPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
pListPath = [pListPath stringByAppendingPathComponent:#"piggyBanks.plist"];
NSLog(#"Path: %#", pListPath);
// if the file does not exist yet, create it, and copy the plist data into it, that can be found in the bundle
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:pListPath]) {
NSString *sourcePath = [[NSBundle mainBundle] pathForResource:#"piggyBanks" ofType:#"plist"];
[fileManager copyItemAtPath:sourcePath toPath:pListPath error:nil];
}
// make the plist content available for usage
pListContent = [[NSMutableDictionary alloc] initWithContentsOfFile:pListPath];
NSLog(#"pListContent: %#", pListContent);
So far so good, but now, if I wanna change some plist value when a user taps on a tableViewCell (it's a custom one, if that's important), although pListPath, pListContent and others are properties, defined in .h and synthesized in .m, I have to redefine pListPath and pListContent inside didSelectRowAtIndexPath, to get the path to be known in that moment.
Could someone please tell me why? I mean, it's nice, that it works, but I'd like to know, why it has to be like that, or if I did a mistake somewhere else..
Thanks!
If plistPath is a string property of your class, you need to assign it as such:
if (!self.pListPath)
{
NSString *newPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
newPath = [newPath stringByAppendingPathComponent:#"piggyBanks.plist"];
self.pListPath = newPath;
}
Then afterwards, use self.pListPath instead of pListPath.
You are having to re-set it because at the moment, pListPath is being assigned to an autoreleased string which will have been removed by the time you need it again. Setting the property (assuming the property is retained or copied) will retain the string for you.
I googled for class variables in objective C, and found my way through various articles, till i found this blog/blogpost, which explains the self. and _underscore thing really well!
I now always declare my Ivars with an underscore, the properties without. Works out quite well. And to sum it up, why to ALWAYS use self.yourPropertiesName, you are calling a method (setter and getter)! And like any other method you are calling, you need to say, who is calling.
Hope this will help someone else too :)

extract data from plist & then store permanently on tableview's cell iphone?

In my application one is mainviewcontroller which is subclass of uiTableviewcontroller holds data from plist when clicked on a particular cell its displays detailviewcontroller(dvc) which is subclass of uiviewcontroller. dvc holds textfield & button .actually when i clicked on button i write textfield's text on tableviewcell through plist but it is actually write data temporary but i want to write data permanently on tableview what can i do for that .its a navigation based application?
Try this:
Loading the plist to dictionary:
//Get the path to documents directory
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *plistPath = [rootPath stringByAppendingPathComponent:#"listData.plist"];
//if the file does not exist in documents directory, then load from the bundle.
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
plistPath = [[NSBundle mainBundle] pathForResource:#"listData" ofType:#"plist"];
}
//load plist into NSMutableDictionary.
nes.plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile:plistPath];
Saving dictionary to plist:
//Get the path to documents directory
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *plistPath = [rootPath stringByAppendingPathComponent:#"listData.plist"];
//Write to documents directory! NOT bundle!!
[nes.plistDict writeToFile:plistPath atomically: YES];
Also use:
[nes.plistDict setObject:txtName.text forKey:#"col1"];
Not:
[nes.plistDict setValue:txtName.text forKey:#"col1"];
If this does not help can I see youre cellForRowAtIndexPath in mainviewcontroller?
could you show us some code?
What is the code for the action button? How do you reload the table view?
I'm guessing you are reading a plist into a NSDictionary? Then loading the tableviews cell.text from the values in the dictionary? When you push the button do you write the textfield text to the NSDIctionary? Do you remember to write the NSDictionary back to the plist?
Okay.
How do you load youre tableview cells?
Are you sure the value for col1 is of NSString type?
You should probably use setObject: forKey: instead of SetValue: forKey:..
Does the tableview not update at all or is it just that it is not saved after you quit?
Are you testing on simulator or device?
The application bundle is a readonly directory on device but not in simulator. you should use:
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES) objectAtIndex:0];
NSString *plistPath = [rootPath
stringByAppendingPathComponent:#"listData.plist"];

Plist not saving from dictionary (to Documents)

I've been trying to save a plist of a NSDictionary to my app's Documents folder. I haven't tried this on the device yet but I'd like it to work on the simulator for testing purposes. The [self createDictionaryFromChoreList] method just creates a NSDictionary from some data in another class of mine. I've pretty much copied/pasted this code from the web documents and when I go to see if the file was saved or not, I find that it isn't. Here is the method block.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *plistName = [[NSString alloc] initWithFormat:#"%#chores.plist", self.firstName];
NSString *path = [documentsDirectory stringByAppendingPathComponent:plistName];
NSDictionary *choresDictionary = [[NSDictionary alloc] initWithDictionary:[self createDictionaryFromChoreList]];
[choresDictionary writeToFile:path atomically:YES];
Any help is greatly appreciated. Thanks in advance.
-S
You should also capture the BOOL returned by writeToFile:atomically:. That will tell you if the write succeeded or not.
Also, are you sure you are looking in the right documents folder? If you have more than one app in the simulator its easy to open the wrong app's documents folder in the Finder. I did that once and it cost me a couple of hours of frustration.
Edit01:
writeToFile:atomically: returning false explains why no file exist. The simplest explanation is that something in the dictionary is not a property list object.
From the NSDictionary docs:
This method recursively validates that
all the contained objects are property
list objects (instances of NSData,
NSDate, NSNumber, NSString, NSArray,
or NSDictionary) before writing out
the file, and returns NO if all the
objects are not property list objects,
since the resultant file would not be
a valid property list.
It just takes one non-plist object buried deep in a dictionary to prevent it from being converted to a plist.
Don't forget serialize the plist data:
Here is a snippet of code that I use for writing information to a plist
NSString *errorString;
NSData *data = [NSPropertyListSerialization dataFromPropertyList:plistDict
format:NSPropertyListXMLFormat_v1_0
errorDescription:&errorString];
[plistDict release];
if (!data) {
NSLog(#"error converting data: %#", errorString);
return NO;
}
if ([data writeToFile:[XEraseAppDelegate loadSessionPlist] atomically: YES]) {
return YES;
} else {
NSLog(#"couldn't write to new plist");
return NO;
}
This is something I whipped up really quickly and it correctly writes a plist directory of name and company to the documents directory. I have a feeling your dictionary creation method might have an issue. Try this out for yourself, then add your code and make sure it works.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *plistDirectory = [paths objectAtIndex:0];
NSString *plistPath = [plistDirectory stringByAppendingPathComponent:#"userCompany.plist"];
NSArray *userObjects = [[NSArray alloc] initWithObjects:#"Joe", #"Smith", #"Smith Co", nil];
NSArray *userKeys = [[NSArray alloc] initWithObjects:#"First Name", #"Last Name", #"Company", nil];
NSDictionary *userSettings = [[NSDictionary alloc] initWithObjects:userObjects forKeys:userKeys];
[userSettings writeToFile:plistPath atomically:YES];
Is it correct, that the name of file your writing to is:
SOEMTHINGchores.plist?
Created via:
NSString *plistName = [[NSString alloc] initWithFormat:#"%#chores.plist", self.firstName];
Also, what is the output of:
[choresDictionary print];
Some additional info would help to debug this.
Where exactly are you looking for the file?
I have the exact same code and it works fine for me.
Just that I have to dig deep to get the file. Something like:
/Users/myUserName/Library/Application Support/iPhone Simulator/User/Applications/0E62A607-8EEB-4970-B198-81CE4BDDB7AA/Documents/data.plist
And the HEX number in the path changes with every run. So I print the file path with every run.
Insert a break point at
NSDictionary *choresDictionary = [[NSDictionary alloc] initWithDictionary:[self createDictionaryFromChoreList]];
now when you step out drag your mouse over choresDictionary and check in the tooltip that its size is not 0x0 or you can simply do an NSLog of the choresDictionary
like NSLog(#"%#",choresDictionary); I think your dictionary has 0 key key value pairs thats why you are getting null into your documents folder.
Thanks,
Madhup
I was running into this issue as well. In my case it turned out that I was using NSNumbers for keys - which is not valid.