I am working on a small converter app for my school project, an app that won't be published in App Store, just for school grade. I am wondering if anyone knows how did this developer in this app
made connections for converting values? The simplest way i can imagine is with switch cases but that would make too much unnecessary code:
if (first column is distance) then if(second column is metres)
case0:third column is yards -> do something
case1:third column is kilometers ->do something
case2:third column is metros ->do nothing
etc....
Thanks for answering.
EDIT:
Thanks for fast answer, this is what i made so far, iOS simulator is displaying correctly three column picker in which second two are displayed depending on the row of the first one. Can u tell me is this any good? Maybe some things are uneccesary complicated?
#import "konverterViewController.h"
#interface konverterViewController ()
#end
#implementation konverterViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.fizVelicine = [[NSArray alloc] initWithObjects:#"distance",#"mass" , nil];
self.udaljenostJedinice = [[NSArray alloc] initWithObjects:#"meter", #"kilometer", #"yard", #"inch", nil];
self.masaJedinice = [[NSArray alloc] initWithObjects:#"kilogram",#"dekagram",#"gram",#"tone" , nil];
}
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
{
UILabel *retval = (id)view;
if (!retval) {
retval = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f,
[pickerView rowSizeForComponent:component].width,
[pickerView rowSizeForComponent:component].height)];
}
retval.opaque = NO;
retval.backgroundColor = [UIColor orangeColor];
retval.adjustsFontSizeToFitWidth = YES;
retval.font = [UIFont systemFontOfSize:12];
if (component == 0) {
retval.textAlignment = NSTextAlignmentCenter;
retval.text = [self.fizVelicine objectAtIndex:row];
}
else if ([self.odabir isEqualToString:#"distance"]){
retval.textAlignment = NSTextAlignmentCenter;
retval.text = [self.udaljenostJedinice objectAtIndex:row];
}
else {
retval.textAlignment = NSTextAlignmentCenter;
retval.text = [self.masaJedinice objectAtIndex:row];
}
return retval;
}
// returns the number of 'columns' to display.
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 3;
}
// returns the # of rows in each component..
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent: (NSInteger)component
{
if(component== 0)
{
return [self.fizVelicine count];
}
else
{
if([self.odabir isEqualToString:#"distance"]){ return [self.udaljenostJedinice count];}
else {return [self.masaJedinice count];}
}
}
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
if(component == 0)
{
return [self.fizVelicine objectAtIndex:row];
}
else {
if([self.odabir isEqualToString:#"distance]){
return [self.udaljenostJedinice objectAtIndex:row];}
else {
return [self.masaJedinice objectAtIndex:row];
}
}
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
if (component == 0) {
self.odabir=[[NSString alloc] initWithFormat:#"%#" , [self.fizVelicine objectAtIndex:row]];
[pickerView reloadAllComponents];}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
It sounds like you're puzzling over how to initialize the picker sections at the top. I think you'll need a data structure that is an array of arrays:
#[#"distance", #[#"meters", #"inches", #"cubits", #"whatevers"],
#"weight", #[#"grams", // etc
Tell the picker that you have three segments. The first segment can be manufactured (and cached, if you want) as the first elements of each sub-array. The second two segments get their values from the data structure, indexed by the value of the first section.
EDIT; You'll probably want a data structure that helps you compute the conversion. For each kind of measure, imagine an NxN table, where N is the number of units on the wheels. A row let's you look up the left wheel setting and the column lets you look up the right wheel. The table should contain what you need to do the calculation. If all of the measures linearly related, like inches-meters, the table can simply contain floats (wrapped as NSNumber) conversion factors.
// conversion factors for distance, units as the first value, conversion factors in a subarray
// meters = 1*meters, 39.37*inches, 2.19*cubits, etc
NSArray *conversions =
#[#"meters", #[#1.0, #39.37, #2.19, //etc
#"inches", // etc. conversions to meters inches cubits etc.
You might notice that this array contains some waste. Only half the table is needed, since inches->meters is just the reciprocal of meters->inches) but I think the space small enough to not worry about this. You might also notice that the table is good only for simple linear relationships between units. Centigrade to Fahrenheit will require a different treatment. The most general treatment for arbitrary conversions might be an array of blocks that carry out the computation. But I suggest starting more simply.
EDIT AGAIN:
Now, to get a conversion formula, get the current picker selections and look up a conversion factor:
NSInteger fromUnitsIndex = [self.picker selectedRowInComponent:1];
NSArray *fromArray = self.conversions[fromUnitsIndex];
// the second member of this array is an array of conversion factors
NSArray *conversionNumbers = fromArray[1];
NSInteger toUnitsIndex = [self.picker selectedRowInComponent:2];
NSNumber *conversionNumber = conversionNumbers[toUnitsIndex];
It sounds like a fun project. Good luck!
Related
Currently working on an app for spotting trains using CoreData. Basically, each train is stored with a corresponding set of sightings. When a user sees a train they log a sighting and that sighting is tied back to a train serial number (just basic data relationships)
I'm trying to populate a UIPickerView with serial numbers however I am running into some difficulty. I'm planning on using this specific PickerView multiple times, so it has its own class and is not implemented in the ViewController.
I have set the delegate and dataSource correctly, but the PickerView is never populated. From the NSLog and printf code that is in each function I can tell that titleForRow is never called, and neither is numberOfRowsInComponent Here is my code for the UIPickerView class:
-(id)init
{
//init super class.
self = [super init];
//get allocate array reader (TrainSightingReaderWriter) then set the array that this picker will be getting data from.
[self setArrayReader:[[TrainSightingReaderWriter alloc] init]];
[self setArray: [[self arrayReader] getTrainSerialArray]];
NSLog(#"INIT PickerView");
//return this object.
return self;
}
-(int)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
printf("At component counter\n");
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
printf("At counter. %d is output\n", _array.count);
return _array.count;
}
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
NSLog(#"Returning a value.");
return [self array][row];
}
ViewController code:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"We have made it to the AddSightVC.");
TrainPicker * mypicker;
mypicker = [[TrainPicker alloc]init];
//ISSUES
_trainPickerScroller.delegate = mypicker;
_trainPickerScroller.dataSource = mypicker;
[_trainPickerScroller reloadAllComponents];
[_trainPickerScroller reloadInputViews];
// Do any additional setup after loading the view.
}
TrainPicker needs to be an instance variable for the view controller so that it is not garbage collected (either in the .h as a property or in an interface section in the .m file). Creating a local variable in viewDidLoad for it will cause it to be garbage collected if you are using ARC.
I'm trying to build an app which works alongside a database of PDF's. What I need is for the app to look at all of the files (PDF's) which are stored and to pull the certain files out, check this against the database and then show a list of which PDF's are available on a UIPicker.
My first step would be that the app checks the files and creates a picker wheel which has been populated with the countries for which there is data in the directory.
To make this work I have created a standard format for the file names: 'Country-Airfield-Plate Name-Date.pdf', e.g. “UK-London Heathrow-ILS DME NDB 27-100214.pdf”. I've split this filename first at the full stop to get two strings: 'UK-London Heathrow-ILS DME NDB 27’ and ‘pdf’, and then we split the title at the hyphens to get ‘UK’, ‘London Heathrow’ and ‘ILS DME NDB 27’.
The app will check the first string for the country information, then the second for the airfield and will then display a list of the third strings for the appropriate airfield.
How can I First Fetch the data from database for a particular field for eg. country make a NSArray form that data and load that NSArray to UIPickerView, then use that array in UIPickerView Delegate and Datasource methods?
If you don't have any web service going and you are handling the documents yourself. You can hardcode a .plist file and put it on your website, download this small plist file every time the app opens, encrypt and save it to your documents directory.
The plist is basically a NSDictionary with objects and keys.
So your plist structure would look like this:
{
Countries = (
{
Airfields = (
{
Code = JNB;
Plates = (
{
Title = "Plate 1";
URL = "http://www.airport.com/za/airfield/plate1/document.pdf";
}
);
Title = "OR Tambo";
}
);
Title = "South Africa";
}
);
}
The above example only holds 1 object in each array in your case it would be more.
Then use the following UIPickerView delegate assuming button.tag is called to change pickerview.tag and display correct array for the pickerview:
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView;
{
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component;
{
return [[self returnCorrectArrayForButton:pickerView.tag] count];
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
return [[[self returnCorrectArrayForButton:pickerView.tag] objectAtIndex:row] valueForKey:#"Title"];
}
// Handle these errors where user can click on any button. Airfields will be empty if no country selected. Plates will be empty when no Airfields selected. After loading all arrays, pressing random buttons might give wrong information.
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
if (pickerView.tag == 1) {
_airfieldArray = [[_countryArray objectAtIndex:row] objectForKey:#"Airfields"];
[_countryButton setTitle:[[_countryArray objectAtIndex:row] valueForKey:#"Title"] forState:UIControlStateNormal];
} else if (pickerView.tag == 2) {
_plateArray = [[_airfieldArray objectAtIndex:row] objectForKey:#"Plates"];
[_airfieldButton setTitle:[[_airfieldArray objectAtIndex:row] valueForKey:#"Title"] forState:UIControlStateNormal];
} else if (pickerView.tag == 3) {
// Show PDF in webview for selected plate
[_plateButton setTitle:[[_plateArray objectAtIndex:row] valueForKey:#"Title"] forState:UIControlStateNormal];
NSString *urlString = [[_plateArray objectAtIndex:row] valueForKey:#"URL"];
NSLog(#"Selected Plate PDF URL : %#", urlString);
}
}
- (NSArray *)returnCorrectArrayForButton:(NSUInteger)buttonIndex
{
if (buttonIndex == 1) {
// Return Countries array
return _countryArray;
} else if (buttonIndex == 2) {
// Return Airfield array
return _airfieldArray;
} else if (buttonIndex == 3) {
// Return Plates array
return _plateArray;
}
return nil;
}
I have a UIPickerView, and it is showing up, but it just shows (null). Why is this? I have it conformed to the delegate's I need in.
EDIT: more detail.
Basically, my app is a flight logbook. You make planes, which you saves, and you can pick it when you make a new session. The planeNum key stores how many planes there are, and the key Plane%liTailNumber has the tail number for that plane.
This is my code:
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)thePickerView {
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)thePickerView numberOfRowsInComponent:(NSInteger)component {
return (long)[[NSUserDefaults standardUserDefaults]integerForKey:#"planeNum"];
}
NSMutableArray *tailPickerOptions;
- (NSString *)pickerView:(UIPickerView *)thePickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
NSInteger num = [[NSUserDefaults standardUserDefaults]integerForKey:#"planeNum"];
while (num > 0){
--num;
tailPickerOptions = [[NSMutableArray alloc]init];
NSString *dTailNumber = [NSString stringWithFormat:#"%#", [[NSUserDefaults standardUserDefaults]objectForKey:[NSString stringWithFormat:#"Plane%liTailNumber", (long)num]]];
[tailPickerOptions addObject:dTailNumber];
NSLog(#"%#", tailPickerOptions);
}
return tailPickerOptions[row];
}
You initialize a new array for each loop of the while. Your code also doesn't ensure you actually initialize the array - if num = 0, you'll never go in and declare the array. There should be a warning or compiler error for this. In the best case scenario because you keep declaring a new array in your loop, you'll return a max of 1 element ever.
Move your array declaration above the while loop.
I am looking to make a bus related app. I have a UIPickerView with two columns. The first column will list all of the buses. The second column will list all of the stops that correspond to the bus that is selected in the first column. It would not be wise to list every single stop no matter what bus is selected in the first column so I split up the stops into arrays (each bus corresponds to an array of stops).
So far I have gotten it to work with just one array of stops (stopsArray002) however I am not sure how to make it so it automatically populates the second column if a different bus is selected in the first column (the bus selection column).
At the moment I have three different stop arrays, each that should correspond to the bus number. I wish to set the bus label to corresponding bus and stop label to corresponding stop. As of now, my bus label displays what I want it to (the bus that is selected) and the stop label displays what 002 stop it is. The following is my code.
ViewController.h
#interface ViewController : UIViewController<UIPickerViewDelegate, UIPickerViewDataSource>
{
IBOutlet UILabel *busLabel;
IBOutlet UILabel *stopLabel;
IBOutlet UIPickerView *busPicker;
NSArray *busArray;
// STOP ARRAYS
NSArray *stopsArray002;
NSArray *stopsArray006;
NSArray *stopsArray007;
}
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
busArray = [[NSArray alloc] initWithObjects:
#"BUS",
#"002",
#"006",
#"007",
#"012",
#"014",
#"017",
#"101",
#"102",
#"103",
#"141",
#"142",
#"147",
#"181",
#"182",
#"189",
#"241",
#"300",
#"301",
#"302",
#"303",
#"304",
#"401",
#"402",
#"403",
#"500",
#"501",
#"502",
#"503",
#"640",
#"701",
#"702",
#"703",
#"704",
#"819",
#"940",
nil];
stopsArray002 = [[NSArray alloc] initWithObjects:
#"STOP",
#"002 1st",
#"002 2nd",
#"002 3rd",
#"002 4th",
#"002 5th",
#"002 6th",
#"002 7th",
nil];
stopsArray006 = [[NSArray alloc] initWithObjects:
#"STOP",
#"006 1st",
#"006 2nd",
#"006 3rd",
#"006 4th",
nil];
stopsArray007 = [[NSArray alloc] initWithObjects:
#"STOP",
#"006 1st",
#"006 2nd",
nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 2;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
if(component == 0) // bus column
return [busArray count];
else // stop column
{
return [stopsArray002 count];
}
}
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
if(component == 0)
busLabel.text = [busArray objectAtIndex:row];
else
stopLabel.text = [stopsArray002 objectAtIndex:row];
}
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
if(component == 0)
return [busArray objectAtIndex:row];
else
return [stopsArray002 objectAtIndex:row];
}
#end
I would use a dictionary here, add the arrays to the dictionary and make the key the value for your bus.
So when you want an array for a particular bus then you would get it like this
NSMutableDictionary busStops
[busStops setObject:stopArray007 forKey:#"007"]
[busStops setObject:stopArray002 forKey:#"002"]
[busStops objectForKey:#"002"] //Will return array
[busStops allKeys] //Will return all your keys as an array you can use for first picker
For one of my last school projects, I am creating an iPad/iPhone application. For some days now I've been working on an issue with a certain memory leak. My application starts out on a specific view-controller (VCMainStatistics_iPad). From there, I push another view-controller (VCSocialMedia_iPad). Afterwards, I go back to the first view-controller.
When I repeat this sequence, I notice (by using Instruments - Activity Monitor) that the memory usage of the app keeps increasing. By disabling parts of the code, I eventually found out it has something to do with the pickerView. This code gives no leaks:
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return 0;
}
However, when I increase the number of rows, leaks start emerging (roughly 0.07 MB per row). Obviously, this is why I believe the pickerView is the cause of the leaks. I've been trying to remove the subviews from the pickerView before deallocation, setting pickerView to nil, and lots of other things... nothing fixes the issue. To hopefully make things a bit clearer, I'll post some more code.
The header file:
#import "UniversalViewController.h"
#define DATATYPE_SOCIALMEDIA 0
#interface VCSocialMedia_iPad : UniversalViewController <UIPickerViewDataSource, UIPickerViewDelegate>
{
NSArray *lMediaTypes;
NSMutableArray *lMediaData;
__weak IBOutlet UIPickerView *pkSocialMedia;
__weak IBOutlet UILabel *lblGraph;
}
#end
PickerView delegate methods:
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
// Get key of requested row
NSString *title = [[lMediaTypes objectAtIndex:row] capitalizedString];
// Capitalize first letter
title = [title capitalizedString];
// Return
return title;
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
// Make or clear data lists
if( lGraphDayDataX[iSelectedServerIndex][DATATYPE_SOCIALMEDIA] == nil ){
lGraphDayDataX[iSelectedServerIndex][DATATYPE_SOCIALMEDIA] = [[NSMutableArray alloc] init];
}
else{
[lGraphDayDataX[iSelectedServerIndex][DATATYPE_SOCIALMEDIA] removeAllObjects];
}
if( lGraphDayDataY[iSelectedServerIndex][DATATYPE_SOCIALMEDIA] == nil ){
lGraphDayDataY[iSelectedServerIndex][DATATYPE_SOCIALMEDIA] = [[NSMutableArray alloc] init];
}
else{
[lGraphDayDataY[iSelectedServerIndex][DATATYPE_SOCIALMEDIA] removeAllObjects];
}
// Get required key
NSString *dictKey = [lMediaTypes objectAtIndex:row];
if( [dictKey isEqualToString:#"total_views"] ){
return;
}
// Adjust graph label
lblGraph.text = [NSString stringWithFormat:#"Stats from %#", dictKey];
// Get data of selected row
NSArray *mediaData = [lMediaData objectAtIndex:row];
// Add each day to data lists: inversed order
for( int day = [mediaData count]-1; day >= 0; day-- ){
NSDictionary *dayData = [mediaData objectAtIndex:day];
dictKey = #"wpsd_trends_date";
NSString *date = [dayData objectForKey:dictKey];
// Remove 00:00:00
date = [date stringByReplacingOccurrencesOfString:#" 00:00:00" withString:#""];
[lGraphDayDataX[iSelectedServerIndex][DATATYPE_SOCIALMEDIA] addObject:date];
dictKey = #"wpsd_trends_stats";
NSString *stats = [dayData objectForKey:dictKey];
[lGraphDayDataY[iSelectedServerIndex][DATATYPE_SOCIALMEDIA] addObject:stats];
}
// Update the graphs
[self updateGlobalScreen];
}
PickerView datasource methods:
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return [lMediaTypes count];
}
Deallocation:
- (void)dealloc
{
pkSocialMedia = nil;
lblGraph = nil;
lMediaData = nil;
lMediaTypes = nil;
}
I only recently converted the project to Objective-C ARC, so there is a good chance this issue has something to do with my lack of experience with the concept. Apart from that, this is also my first Xcode project ever. Hopefully, someone here can help out: please let me know if I need to post more code to clarify things.
Thanks in advance!
Try removing the -(void)dealloc method. It shouldn't be implemented when you're using ARC. If you aren't using ARC, it needs to call [super dealloc].
Never found the solution itself, so I used a workaround: by replacing the NSPickerView with a NSTableView component, the leak did not occur anymore. For everyone who noticed the issue and tried to find a solution: thank you for trying!
I'm having a similar issue. It only happens when the UIPickerView is outside the bounds. The way I fixed it is to never have the UIPickerView move out of bounds (simply fade in and fade out to unhide/hide the UIPickerView). Probably a bug in UIKit.