Firstly, sorry for my English... I want to write a program which is going to calculate distances between two cities. For example, in UITextField we write Paris, and in the second UITextField we write the second , "London. And We have a base of longitude s and latitude s of all Cities. Our program has distance formula which uses these four numbers. We know this formula.
When the user insert a name in UITextField i want to TAKE THIS .text AND COMPARE IT WITH OUR BASE. How to do that?? I can do a program like this but it`s ....stupid:
#interface City : UIViewController{
IBOutlet UITextField *city1;
IBOutlet UITextField *city2;
}
#property (nonatomic, retain) IBOutlet UITextField *city1;
#property (nonatomic, retain) IBOutlet UITextField *city2;
-(void) calculate;
#end
#import "City.h"
#implementation City
#synthesize city1, city2;
-(void) viewDidLoad
{
// to check changes in textfields i`m using NSTimer, I know it`s stupid but I haven`t learned how to make it in different way
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(calculate) userInfo:nil repeats:YES];
}
-(void) obliczenia
{
double distance;
if([city1 is EqualToString:#"Paris"] && [city2 is EqualToString:#"London"])
{
double Paris_lat = x1;
double Paris_lon = y1;
double London_lat = x2;
double London_lon = y2;
distance =...; // we know the formula for distance but this is not important for now.
// distance shows in some label but this is not a point of my problem.
}
}
It runs but when we have few cities. But when we have thousand cities, writing code would be a nonsens.
I am starting with iPhone programming.
Thank you for patience and please, help me. It s very important but I can't find a solution.
Take a look at this guide:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/PropertyLists/AboutPropertyLists/AboutPropertyLists.html
Basically, put all of your data into a property list, like so:
<dict>
<key>Paris</key>
<dict>
<key>x</key>
<real>20.2</real>
<key>y</key>
<real>30.4</real>
</dict>
...
</dict>
And then load it like this:
NSDictionary * data = [[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"NAMEOFPLIST" ofType:#"plist"]];
NSDictionary * paris = [data objectForKey:#"Paris"];
float xparis = [[paris objectForKey:#"x"] floatValue];
float yparis = [[paris objectForKey:#"y"] floatValue];
In your case you'd do something like this:
NSDictionary * data = [[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"NAMEOFPLIST" ofType:#"plist"]];
NSDictionary * city1Data = [data objectForKey:city1.text];
NSDictionary * city2Data = [data objectForKey:city2.text];
if (city1Data != nil && city2Data != nil) {
// Do what you need...
}
And then do what you need with the data.
You are probably better off using a picker view to display the available cities, or use a live search in your textview to autocomplete, or provide selections for your cities.
If you have a constrained set of inputs (in this case, the names of the cities for which you have long, lat values) it's a better idea to constrain the user's input to these values.
You can create a Array with all Country Names and compare each Country Name in the Array with your input.
NSArray *countries = [NSArray arrayWithObjects:#"Berlin", #"Rom", nil];
for (NSString *c in countries) {
if ([city1 isEqualToString:c]) {
//do something
}
}
Related
I have been debating between a .plist and a sqlite3 database to hold some data that I need to access, but not manipulate within the .plist/database. Here is my question. Lets say I want to store the height and color of trees, flowers, bushes and I want each piece of information to be accessible. Below is similar to what I would like:Trees Palm 6 feet Green Willow 8 feet Brown Bushes Evergreen 5 feet Green Cinquefoil 2 feet Yellow Flowers Rose 1 foot Red Tulips 2 feet Yellow
So if I want to individually access the 2 feet height under Tulips and display it in a text box in my app..what is the best form of data store/resource to use. .plist or sqlite? How would I lay this out as a .plist?
As always, I appreciate your time and efforts!!
Thanks!
Since you dont have much data just use a .plist it would be easier to manage
make each parent Trees,Flowers,Bushes and array make each child item a dictinary , so when you check if a child is satisfies your requirement like 2 feet height under Tulips use it.
create some plist like this:
Code Sample:
Note:I didnt test this you need to improve this
you can use some kind of logic here like this to check the color,kind or height.
I am giving an example from my project for you to see how you would filter the plist, so change the name of the function as you wish.
I won't change function names cause "nobody aint got time for that" :)
create a nsobject class called ParseSchedulePlist
in ParseSchedulePlist .h
#import <Foundation/Foundation.h>
#interface ParseSchedulePlist : NSObject
#property (nonatomic,strong) NSMutableDictionary * agendaDic;
- (void)passStringToAgendaDic:(NSString *)string;
//trees
-(NSArray*)getTrees;
-(NSDictionary*)getItems:(NSInteger ) section ;
-(NSString*)getItemKind :(NSInteger ) section;
-(NSString*)getItemColor :(NSInteger ) section;
-(NSNumber*)getItemheight :(NSInteger ) section;
//flowers
//do the same of above for flowers an bushes took
#end
in ParseSchedulePlist .m
#import "ParseSchedulePlist.h"
#implementation ParseSchedulePlist
#synthesize agendaDic = _agendaDic;
- (void)passStringToAgendaDic:(NSString *)string {
//get plist from bundle
NSString * path = [[NSBundle mainBundle] pathForResource:string ofType:#".plist"];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:path];
NSLog(fileExists ? #"Yes" : #"No");
NSLog(#"Path is %#",path);
NSLog(#"agendaDic is %u",[self.agendaDic count]);
self.agendaDic = [NSMutableDictionary dictionaryWithContentsOfFile:path];
}
-(NSArray*)getTrees
{
NSArray * value = [[self agendaDic] objectForKey:#"trees"];
return value;
}
-(NSDictionary*)getItems:(NSInteger ) section
{
NSDictionary * value =[[self getTrees] objectAtIndex:section];
return value;
}
-(NSString*)getItemKind :(NSInteger ) section;
{
NSString * value =[[[self getItems] objectAtIndex:section] objectForKey:#"kind"];
return value;
}
-(NSString*)getItemColor :(NSInteger ) section
{
NSString * value =[[[self getItems] objectAtIndex:section] objectForKey:#"color"];
return value;
}
-(NSNumber *)getItemheight :(NSInteger ) section;
{
NSNumber * value =[[[self getItems] objectAtIndex:section] objectForKey:#"height"];
return value;
}
//write the same functions for flowers and bushes
#end
in your regular view controller .h:
#import "ParseSchedulePlist .h"
#property (nonatomic, strong) ParseSchedulePlist *agenda;
in your regular view controller .m:
#import "ParseSchedulePlist .h"
#synthesize agenda;
//here calls for the special class
self.agenda=[[ParseSchedulePlist alloc] init];
[self.agenda passStringToAgendaDic:#"name of the plist"];
NSMutableArray *newArray=[ NSMutableArray alloc] init];
//here for example get all the palm trees under 6 feet
for(int i=0; i<[self.agenda getTrees] count] i++)
{
if([self.agenda getItemKind :i] isquealtostring #"palm"){
if([self.agenda getItemheight :i] <= 6)
[newArray add object:[self.agenda getItems:i];
}
}
Nslog(#"print your array %#",newArray);
You can achieve the same with core data. Core data as such would be a little difficult to use. I have always used it with Magical Record, a wonderful library.
It's very easy to map objects with relation. In your case there is one to many and one to one relation between parent and child.
With MagicalRecord if you wanted to find an item with height equal to 2. You can do this in two simple steps
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"height =%#",#2];
NSArray *items = [Item findAllWithPredicate:predicate];
Good thing is that since there is a one to one relationship from an item to group, each item will have info about group. So everything nicely fits and it's easier to search what ever way you like.
You can find the source code for the details.
I have built a specialized card application. What it does is allow a user to 'draw' a card, then view the card, and place it back in a random place in the deck. The only problem that I am having is that, more often than not, the card is being placed at the top of the deck.
Here is the contents of the .h file:
#class _Card;
#interface _Deck : NSObject
#property (readonly) NSString *deckData;
#property (readonly) NSUInteger count;
#property (readonly) NSUInteger deckCount;
#property (readonly) BOOL needsReset;
#property (readonly) NSArray *cards;
- (id)initWithArray:(NSArray *)array;
- (id)initWithContentsOfFile:(NSString *)filePath;
- (void)shuffle;
- (NSArray *)draw;
- (void)reset;
- (void)changeEdition;
#end
Now, here is my draw method, which will draw a card (more than one if the cards so specify it) and then place that card back into the deck, if it is allowed:
- (NSArray *)draw {
// first, we create an array that can be returned, populated with the
// cards that we drew
NSMutableArray *drawArray = [[[NSMutableArray alloc] init] autorelease];
// next, we get the top card, which is actually located in the
// indexArray (I use this for shuffling, pulling cards, etc.)
NSNumber *index = [[[indexArray objectAtIndex:0] retain] autorelease];
// now we get the card that the index corresponds to
// from the cards array
_Card *card = [cards objectAtIndex:[index integerValue]];
// now I remove the index that we
// got from the indexArray...don't worry,
// it might be put back in
[indexArray removeObject:index];
// if the card is supposed to discard after
// draw, we leave it out
if(!card.discard) {
int insertIndex = arc4random_uniform(indexArray.count);
// then I insert the card into the deck using the random, random
// number
[indexArray insertObject:index atIndex:insertIndex];
}
_Card *cardCopy = [card copy];
// we add the card to the array
// that we will return
[drawArray addObject:cardCopy];
// next, if the card is not the final card...
if(!card.final) {
// ...and the card has an
// additional draw specified...
if(card.force) {
// we keep drawing until we need to stop
[drawArray addObjectsFromArray:[self draw]];
}
}
return drawArray;
}
Is there anything that I may be doing wrong? If you need any more information, please let me know. Thanks in advance for any help that you can provide.
If I am understanding this correctly, the problem is that it is inserting the card at index 0 in indexArray?
Okay, have you tried something like this:
(don't use this line yet [indexArray removeObject:index];)
if(!card.discard)
{
int insertIndex = arc4random_uniform(indexArray.count);
id obj = [indexArray objectAtIndex:index];
[indexArray removeObjectAtIndex:index];
[indexArray insertObject:obj atIndex:insertIndex];
NSLog(#"insertIndex is %i and obj is %#", insertIndex, obj);
}
else
{
[indexArray removeObjectAtIndex:index];
}
Your code seems to be okay, I'm guessing it just doesn't like removing the object before... I added the log just so you could see if it really was inserting it at the top each time. Give me an update- if this doesn't work, I could take a look at your project file.
What does "more often than not" mean?
What you show here looks perfect....
Remember that this is random and it is quite possible (although rare) to get a particular number 10 times in a row. Over the long run you should get an even distribution though.
Run this routine 10,000,000 times or so, and check the number of times that you get each number (make sure that you have the same number of cards in the deck each time) before deciding that something is wrong.
Also, are you sure that your indexArray contains the correct values and you aren't duplicating 0 in there?
I have many different arrays with dictionaries of hotels in a data model class. For example, the array madridHotels would hold dictionaries of hotels in Madrid. To list these hotels in a tableview, I use a method called madridHotelsCount in my dataModel class:
-(int)madridHotelsCount {
return self.madridHotels.count;
}
In a tableview's numberOfRowsInSection method, I would put
- (NSInteger)tableView:(UITableView*)theTableView numberOfRowsInSection:(NSInteger)section {
return [self.dataModel madridHotelsCount];
}
in order to list the hotels in Madrid. This works just fine. But since I have about 20 cities, I have a feeling that having 20 different VC's and XIB's to make city-based tableviews is plain stupid and wasteful.
In my "Select City" tableview, each city has a key called cityString with the appropriate string meant for the "Hotels in City X" tableview. In Madrid's case, for example, the string would be madridHotelsCount. If I'm not mistaken there is a way to use this information when pushing the "Hotels in City X" tableview controller.
What I can't figure out is how to change the string madridHotelsCount in the tableview method with, for example, barcelonaHotelsCount depending on a key in a "Select City" tableview so that I can only use one VC and XIB.
Hope this makes sense...
You need to change it to something like this so you pass the hotel to each view.
-(int)hotelCountForCity:(NSString *)cityName
I would rewrite it so it was more generic as you shouldn't be repeating yourself like that.
although its not the best way, but if all that code is already there, you can..
NSString city = #"madrid";
SEL selector = selectorFromString([NSString stringWithFormat:#"%#HotelsCount", city]);
int count = 0;
if([self respondsToSelector:selector])
{
int *count = [self performSelector:selector];
}
Change the data structure in dataModel to use
#property (nonatomic, retain) NSDictionary *citiesHotels;
The cities are the keys and the values are the arrays of hotels.
An example might be like
NSArray *madrid = [[NSArray alloc] initWithObjects:#"madridHotelA", #"madridHotelB", nil];
NSArray *barcelona = [[NSArray alloc] initWithObjects:#"barcelonaHotelA", #"barcelonaHotelB", nil];
NSDictionary *citiesHotels = [[NSDictionary alloc] initWithObjectsAndKeys:madrid, #"Madrid", barcelona, #"barcelona", nil];
[madrid release]; madrid = nil;
[barcelona release]; barcelona = nil;
self.citiesHotels = citiesHotels;
[citiesHotels release]; citiesHotels= nil;
Add a method to the model like this
- (NSInteger)hotelCountForCity:(NSString *)city
{
int count = 0;
NSArray *hotels = [self.citiesHotels valueForKey:city];
if (hotels) {
count = [hotels count];
}
return count;
}
To get the hotel you would need something like this in your model
- (Hotel *)hotelAtIndex:(NSInteger)index forCity:(NSString *)city
{
NSArray * hotels = [self.citiesHotels valueForKey:city];
return [hotels objectAtIndex:index];
}
These are called like this
- (NSInteger)tableView:(UITableView*)theTableView numberOfRowsInSection:(NSInteger)section
{
return [self.dataModel hotelCountForCity:theSelectedCityName];
}
And in `cellForRowAtIndexPath you do
Hotel *hotel = [self.dataModel hotelAtIndex:indexPath.row forCity:theSelectedCity];
Hopefully I can make this clear, but I am new to Objective-C and to be honest not great with Arrays in the first place.
So, I have a Singleton class (called SingletonState) that I am using to pass variables around my app (please can we leave the whether I should use Singleton classes out of this - I will fix that later). In this class, I have an NSMutableArray (called arrayMyEvents). I have also created a class that I am going to store a list of events (called EventClass). When the user logs in, I call a web service and get back 3 strings. The 3rd string is a comma separated list of value. I parse the data and populate the custom class EventClass. I then add this class to the SingletonState.arrayMyEvents.
I have all of this working. I can go to another ViewController and access the "count" of items in arrayMyEvents.
PROBLEM: I now want to edit one of the ScheduledEventsClass"es" in my array. How do I get access to it and edit some of the properties and update the one in my SingletonState class?
Here is some of the code, that I've tried:
NSString *sWebServiceEvents = [[NSString alloc] initWithFormat:#"%#", [result objectAtIndex:2]];
if ( [ sWebServiceEvents isEqualToString:#"NULL" ] != true ) {
NSArray *arrayEvents = [sWebServiceEvents componentsSeparatedByString:#","];
// If the array has not been initialized they initialize it.
if (sharedState.arrayMyEvents == nil) {
sharedState.arrayMyEvents = [[NSMutableArray alloc ] init ];
}
for (NSString * sEvent in arrayEvents) {
// Set equal to the value of the array (the Event Number) at the same
// position as the row that we are being asked to return a cell/row for.
EventClass *eventClass = [[EventClass alloc] retain];
eventClass.sEvent = sEvent;
[ sharedState.arrayEvents addObject:eventClass ];
}
NSLog(#"LoginView - sharedState.arrayMyEvents Count: %d", [sharedState.arrayMyEvents count]);
}
Here is me trying to access it in another ViewController:
EventClass *eventClass =
[sharedState.arrayMyEvents objectAtIndex:row ];
NSLog(#"eventClass.sEventNumber: ", eventClass.sEventNumber);
eventClass.sLocation = #"Jason's Big Location";
You're going to have some memory leaks from the sEvent loop. [[EventClass alloc]retain] leaves you an uninitialized EventClass object with a reference count of 2. You'll need to change that to [[[EventClass alloc] init] autorelease] to keep it from leaking. The arrayEvents NSMutableArray will retain it during the addObject: call. (Shouldn't that be [sharedState.arrayMyEvents addObject: eventClass] in the loop?)
After that, all you have to do to edit the EventClass object in the second block of code is edit it. The eventClass variable is a pointer to an object in the array. Anything done to that object doesn't affect the pointer referencing it, it affects data referenced by it. The code you have in the second block should change the sLocation of the selected object as you intend.
You have a few more memory leaks in there, too. Use Cmd-Shift-A to build with the static analyzer and it'll tell you where.
Maybe the problem is that you put them in sharedState.arrayEvents but try to take them out of sharedState.arrayMyEvents. Different variables.
Also, lots of memory leaks.
Thanks John and St3fan, your answers and time are appreciated!!!
I think that I figured out my issue:
Basically, the class that I created (EventClass) had the properties setup like this:
#property (nonatomic, assign) NSString *sStudyNumber;
#property (nonatomic, assign) NSString *sTheater;
but, they should be (or at least I got it to work like this):
#property (nonatomic, retain) NSString *sStudyNumber;
#property (nonatomic, retain) NSString *sTheater;
Then, in my second view I was able to do this:
EventClass *eventClass = [sharedState.arrayMyEvents objectAtIndex:row ];
NSLog(#"MyEvents: %#", eventClass.sEventNumber);
eventClass.sLocation = #"Jason's Big Location";
I then checked it in another method of the view using this and it was still there:
EventClass *eventClass = [sharedState.arrayMyEvents objectAtIndex:row ];
NSLog(#"MyEvents: %#", eventClass.sEventNumber);
NSLog(#"MyEvents: %#", eventClass.sLocation);
I also, checked it in yet another view and the value was maintained in the SharedState.arrayMyEvents without issue. :)
In the end, I believe that I boiled down to the difference between "assign" and "retain".
Now, on to the memory leaks :(
Please, let me know if you see any other issues with this.
Thanks,
Jason
I am trying to condense my code for a method and was wondering how I would achieve the following:
I have a bunch of variables that differ only in their number such as:
int intVariable1
int intVariable2
UILabel Label1
UILabel Label2
BOOL bool1
BOOL bool2
etc.
So I want to call a method and pass in an int. That int would determine which ints, UILablels, and BOOLs get worked on. So if a 1 was passed in the method would work on these variables like this:
- (void) DyanamicMethod: (int) inputNumber {
//something that uses the inputNumber to act on the 1 variables
intVariable1 = someValue;
[Label1 setText:someText];
bool1 = YES;
}
Obviously if a 2 were passed in I would want the variables to be of the 2 type. I'm assuming you would do something with creating a string somehow, but I'm not sure how to adjust that to use it to become a variable name. Any insight would be appreciated. Thank you for your time.
Declare the variables as arrays:
int intVariable[2];
UILabel *Label[2];
BOOL bools[2];
Then the method would look like this:
intVariable[inputNumber] = someValue;
[Label[inputNumber] setText:#"someText"];
bools[inputNumber] = YES;
Keep in mind that array indexes are zero-based so in the arrays above, variable "1" is at index 0 and variable "2" is at index 1. Your method could also just take the inputNumber and subtract one from it to get the array index.
You can use key-value coding.
- (void) DyanamicMethod: (int) inputNumber {
//something that uses the inputNumber to act on the 1 variables
NSString* key = [NSString stingWithFormat:#"Label%i", number];
UILabel* label = [self valueForKey:key];
label.text = newString;
}
If you needed something like that you could keep an NSArray of all of the labels ...
Like
in the class interface section:
NSArray* labels ;
In init:(or loadView)
labels = [[NSArray alloc] initWithObjects:Label1, Label2,etc] ;
In dealloc:
[labels release] ;
in DyanamicMethod: (int) inputNumber
UILabel* label = [labels objectAtIndex:inputNumber] ;
Repeat for each type ... Or make a new class holding the label, int and the bool, and have the array be of that type.
Another way might be:
Make properties for each of your member variables.
Then in your function:
UILabel label1 = [self performSelector:sel_getUid([[NSString stringWithFormat:#"label%d", inputNumber] UTF8String])] ;
I think using an array of some sort is better for this situation, but its possible to do it this way. See NSObject Documentation for performSelector.
You could do this with properties (well, you don't NEED properties, but they'd make it simpler), though you'd have to switch to objects:
#property (nonatomic, readwrite, retain) NSNumber *variable1, *variable2;
#property (nonatomic, readwrite, retain) NSNumber *bool1, *bool2;
#property (nonatomic, readwrite, retain) UILabel *label1, *label2;
- (void) DyanamicMethod: (int) inputNumber {
[[self valueForKey: [NSString stringWithFormat: #"label%d", inputNumber] setText: someText];
[self setValue: [NSNumber numberWithInt: inputNumber]
forKey: [NSString stringWithFormat: #"variable%d", inputNumber]];
[self setValue: [NSNumber numberWithBool: YES]
forKey: [NSString stringWithFormat: #"bool%d", inputNumber]];
}
Do you have too many variables for a simple switch statement?
switch(inputNumber) {
case 1:
intVariable1 = someValue;
[Label1 setText:someText];
bool1 = YES;
break;
case 2:
intVariable2 = someValue;
[Label2 setText:someText];
bool2 = YES;
break;
// etc
}