I must be doing something wrong here. My SQLite database is working. However, I cannot seem to populate the tname into a picker view while the picked populates three text fields tname, latitude, and longitude.
Info: Two picker views that are independent from each other, they both read the same sqlite db.
#import "ViewController.h"
#interface ViewController ()
{
NSMutableArray *array1;
sqlite3 *towerDB;
NSString *dbPathString;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//UIPickerView *pickerview1;
//array1 = [[NSMutableArray alloc]init];
[[self pickerview1]setDelegate:self];
[[self pickerview1]setDataSource:self];
[self displayTower];
}
- (void)displayTower
{
array1 = [[NSMutableArray alloc] init];
sqlite3_stmt *statement;
if (sqlite3_open([dbPathString UTF8String], &towerDB)==SQLITE_OK) {
[array1 removeAllObjects];
NSString *querySql = [NSString stringWithFormat:#"SELECT * FROM TOWERS"];
const char* query_sql = [querySql UTF8String];
// sqlite3_clear_bindings(statement);
// sqlite3_reset(statement);
if (sqlite3_prepare(towerDB, query_sql, -1, &statement, NULL)==SQLITE_OK) // NOT OK
//This Code works in a TableView!!
//Will NOT work in a PickerView
{
while (sqlite3_step(statement)==SQLITE_ROW)
{
NSString *tname = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 1)];
NSString *latitude = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 2)];
NSString *longitude = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 3)];
// NSString *ds2 = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 4)];
NSString *PickerTower = [[NSString alloc] initWithFormat:#"%#", tname];
NSString *lat1 = [[NSString alloc] initWithFormat:#"%#", latitude];
NSString *long1 = [[NSString alloc] initWithFormat:#"%#", longitude];
NSArray *array = [[NSArray alloc] initWithObjects:PickerTower, nil];
[array1 addObject:array];
NSLog(#"Lat/Long %# / %#", lat1, long1);
}
}
}
}
#pragma mark Picker Data Source Methods
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
//ONE Colume
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
//data view how many items?
return [array1 count];
// return [_array2 count];
}
#pragma mark Picker Delegate Methods
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
return [array1 objectAtIndex:row];
//return [self->array1 objectAtIndex:row];
// array1 = [[NSMutableArray alloc] init];
}
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
long select = row;
NSLog(#"PickerView %ld", select);
}
#end
You have to changed this code in the method name displayTower and code lines -
[array1 addObject:array];
NSLog(#"Lat/Long %# / %#", lat1, long1);
with -
[array1 addObject:array];
NSLog(#"Lat/Long %# / %#", lat1, long1);
[[self pickerview1] reloadAllComponents];
may be it will help you, or feel free.
Related
This is my code for my test app where I want to show users on the next view controller but when I run and try to retrieve the values it is giving me repeated values. I have used NSMutableDictionary and NSMutableArray.
#import "DBManager.h"
static DBManager *sharedInstance = nil;
static sqlite3 *database = nil;
static sqlite3_stmt *statement = nil;
#implementation DBManager
-(void) getsaveData:(NSString *)Username
{
const char *dbpath = [newFileAtPath UTF8String];
NSMutableArray *userArray = [[NSMutableArray alloc]init];
if (sqlite3_open(dbpath, &database)==SQLITE_OK) {
NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];
NSString *getSQL = [NSString stringWithFormat:#"SELECT * FROM USERS WHERE USERNAME != \"%#\"",Username];
const char *retrievestmt = [getSQL UTF8String];
if(sqlite3_prepare_v2(database, retrievestmt, -1, &statement, NULL)==SQLITE_OK)
{
while(sqlite3_step(statement)==SQLITE_ROW)//in this while loop i am getting repeated values
{
NSString *User_ID = [[NSString alloc] initWithUTF8String: (const char *) sqlite3_column_text(statement, 0)];
[userInfo setObject:User_ID forKey:#"ID"];
//[userArray addObject:User_ID];
NSString *User_Email = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 1)];
[userInfo setObject:User_Email forKey:#"Email"];
//[userArray addObject:User_Email];
NSString *Password = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 2)];
[userInfo setObject:Password forKey:#"Email"];
//[userArray addObject:Password];
NSString *User_name = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 3)];
[userInfo setObject:User_name forKey:#"Username"];
//[userArray addObject:User_name];
NSString *User_Avatar = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 4)];
[userInfo setObject:User_Avatar forKey:#"Avatar"];
//[userArray addObject:User_Avatar];
[userArray addObject:userInfo];
NSLog(#"userArray %#",userArray);
}
sqlite3_reset(statement);
}
}
}
#end
In the while loop I am geeting the error.
I tried multiple times with multiple solutions please help me fix this.
The problem is simple - you are incorrectly reusing the userInfo dictionary. You need to create a new instance in each loop iteration.
Move the line:
NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];
to just after the while loop line.
You also have many other issues with your code:
You never close the database.
You never finalize the prepared statement.
You needlessly reset the prepared statement.
You have insufficient error checking/logging.
You are not using standard naming conventions.
You are not using standard whitespacing which makes your code harder to read.
You don't use consistent curly brace placement. Pick a style and use it everywhere.
You are using static variables when you should not be.
Never build SQL statements using stringWithFormat:. Properly bind values to the query.
Never use SELECT * in a query. Explicitly list out the columns to ensure you get consistent and expected column values.
Here's is your code updated for these issues:
#import "DBManager.h"
static DBManager *sharedInstance = nil;
#implementation DBManager
- (void)getsaveData:(NSString *)username {
const char *dbpath = [newFileAtPath UTF8String];
NSMutableArray *userArray = [[NSMutableArray alloc] init];
sqlite3 *database;
if (sqlite3_open(dbpath, &database) == SQLITE_OK) {
const char *retrievestmt = "SELECT USER_ID, USER_EMAIL, PASSWORD, USER_NAME, USER_AVATAR FROM USERS WHERE USERNAME != ?"; // replace * with actual column names
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, retrievestmt, -1, &statement, NULL) == SQLITE_OK) {
sqlite3_bind_text(statement, 1, [username UTF8String], -1, SQLITE_TRANSIENT);
while (sqlite3_step(statement) == SQLITE_ROW) {
NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];
NSString *userID = [[NSString alloc] initWithUTF8String:(const char *)sqlite3_column_text(statement, 0)];
userInfo[#"ID"] = userID;
NSString *userEmail = [[NSString alloc] initWithUTF8String:(const char *)sqlite3_column_text(statement, 1)];
userInfo[#"Email"] = userEmail;
NSString *password = [[NSString alloc] initWithUTF8String:(const char *)sqlite3_column_text(statement, 2)];
userInfo[#"Password"] = password;
NSString *username = [[NSString alloc] initWithUTF8String:(const char *)sqlite3_column_text(statement, 3)];
userInfo[#"Username"] = username;
NSString *userAvatar = [[NSString alloc] initWithUTF8String:(const char *)sqlite3_column_text(statement, 4)];
userInfo[#"Avatar"] = userAvatar;
[userArray addObject:userInfo];
}
sqlite3_finalize(statement);
NSLog(#"userArray %#",userArray);
} else {
NSLog(#"Unable to prepare the statement at %s: %s", retrievestmt, sqlite3_errmsg(database));
}
sqlite3_close(database);
} else {
NSLog(#"Unable to open the database at %#: %s", dbpath, sqlite3_errmsg(database));
}
}
#end
In my iOS app I've a class that performs a web request to get an array of informations located in mySQL DB. In this class I've a method that do this taking as input a mySQL query:
- (NSMutableArray *) myreq:(NSString *)query{
// Create NSData object
NSData *dataQuery = [query
dataUsingEncoding:NSUTF8StringEncoding];
// Get NSString from NSData object in Base64
NSString *base64EncodedQuery = [dataQuery base64EncodedStringWithOptions:0];
// Print the Base64 encoded string
NSLog(#"Encoded: %#", base64EncodedQuery);
NSMutableString *strURL = [NSMutableString stringWithFormat:#"http://…=%#“,base64EncodedQuery];
[strURL setString:[strURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSData *dataURL = [NSData dataWithContentsOfURL:[NSURL URLWithString:strURL]];
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:dataURL
options:kNilOptions
error:&error];
NSMutableArray *results = [[NSMutableArray alloc] init];
int numRow = 0;
for (NSArray *arrow in json) {
[results addObjectsFromArray:arrow];
numRow++;
}
return results;
}
This method send a query to a php script that perform immediately this query to MySQL DB and get a json with results. I translate the json in this method and finally return an array with results.
I call myreq in a method
- (void)downloadScope{
_arrID = [[NSMutableArray alloc] init];
_arrIDUsers = [[NSMutableArray alloc] init];
_arrUsernames = [[NSMutableArray alloc] init];
_arrPictures = [[NSMutableArray alloc] init];
[myQueue addOperation:[NSBlockOperation blockOperationWithBlock: ^{
query = #"SELECT ID FROM mytable”;
[_arrID addObjectsFromArray:[self myreq:query]];
for (int i = 0; i < [_arrID count]; i++) {
NSArray *tempArray = [[NSArray alloc] initWithArray:[self myreq:[NSString stringWithFormat:#"SELECT IDUsr,usrn, pictureaddress FROM mytable WHERE ID = %#",_arrID[i]]]];
[_arrIDUsers insertObject:tempArray[0] atIndex:i];
[_arrUsernames insertObject:tempArray[2] atIndex:i];
[_arrPictures insertObject:tempArray[2] atIndex:i];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self.tableView reloadData];
}];
}
}]];
[myQueue setSuspended:NO];
}
In tableView I create cells in this way (using SDWebImage):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Identificatore di cella
NSString *identifier = #“cellmy”;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
cell.backgroundColor = nil;
if ( cell == nil ) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
NSString *username = [self.arrUsernames objectAtIndex:indexPath.row];
UILabel *cellLabelUsername = (UILabel *)[cell viewWithTag:2];
cellLabelUsername.text = [username uppercaseString];
UIImageView *cellImageProfileSnap = (UIImageView *)[cell viewWithTag:5];
[cellImageProfileSnap sd_setImageWithURL:[NSURL URLWithString:[_arrPictures objectAtIndex:indexPath.row]] placeholderImage:[UIImage imageNamed:#“…”]];
}
In viewDidLoad I initialize my NSOperationQueue (defined in my interface):
myQueue = [[NSOperationQueue alloc] init];
[myQueue setMaxConcurrentOperationCount:100];
[myQueue setName:#"com.sada"];
My goal is to make everything faster because the loading in tableView is slow and I think that is not dependent on SDWebImage. Please help me
I'm trying to do a pickerView but I'm getting bad acess:
here is my code
-(void) viewWillAppear:(BOOL)animated {
list = [[NSArray alloc]init];
[self populateList]
}
-(void) populateList {
NSString *path = [[NSBundle mainBundle] pathForResource:#"nameoffile" ofType:#"txt"];
NSString *file = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
list = [file componentsSeparatedByString:#"\n"];
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
return (NSString *)[list objectAtIndex:row]; //here I'm getting bad acces
}
The error is: "Thread 1: EXC_BAD_ACCESS(code=1, address=0xa001cc65)"
NSArray returned by componentsSeparatedByString: is autoreleased value so you need to retain it.
You should remove:
list = [[NSArray alloc]init];
and add retain to:
list = [[file componentsSeparatedByString:#"\n"] retain];
I would like to annotations in a Mapview, using data fetched Json via a URL.
As might do?
NSMutableArray *annotations = [[NSMutableArray alloc]init];
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://example.com/LData.php"]];
//Data: Longitud/Latitud/Country;.....
NSString *str = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSLog(str);
[foo removeAllObjects];
NSArray *foo2 =[str componentsSeparatedByString: #";"];
int i=0;
for(i=0;i<[foo2 count]; i++){
[foo insertObject:[foo2 objectAtIndex:i] atIndex:i];
NSArray *foo3 =[foo2 componentsSeparatedByString: #"/"];
So far so good
¿As I Lay to introduce the variables [i]?
CLLocationCoordinate2D theCoordinate[i];
theCoordinate1.latitude[i] = [foo3 objectAtIndex:1]);//Longitud
theCoordinate1.longitude[i] = [foo3 objectAtIndex:2]);//Latitud
MyAnnotation* myAnnotation[i]=[[MyAnnotation alloc] init];
myAnnotation[i].coordinate=theCoordinate[i];
myAnnotation[i].title=#""+[foo3 objectAtIndex:3];
myAnnotation[i].subtitle=#"in the city";
[mapView addAnnotation:myAnnotation[i]];
[annotations addObject:myAnnotation[i]];
}
how I can solve this problem?
Why you are using different CLLocationCoordinate2D variables?
theCoordinate1.latitude[i] = [foo3 objectAtIndex:1]);//Longitud
theCoordinate1.longitude[i] = [foo3 objectAtIndex:2]);//Latitud
and then
myAnnotation[i].coordinate=theCoordinate[i];
I think you need to adjust you code and some problems can resolved by themselves.
NSMutableArray *annotations = [[NSMutableArray alloc]init];
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://example.com/LData.php"]];
//Data: Longitud/Latitud/Country;.....
NSString *str = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSLog(str);
[foo removeAllObjects];
NSArray *foo2 =[str componentsSeparatedByString: #";"];
int i=0;
for(i=0;i<[foo2 count]; i++){
[foo insertObject:[foo2 objectAtIndex:i] atIndex:i];
NSArray *foo3 =[foo2 componentsSeparatedByString: #"/"];
float realLatitude = [[foo3 objectAtIndex:1] floatValue];//Longitud
float realLongitude = [[foo3 objectAtIndex:0] floatValue];//Latitud
MyAnnotation* myAnnotation = [[MyAnnotation alloc] init];
CLLocationCoordinate2D theCoordinate;
theCoordinate.latitude = realLatitude;
theCoordinate.longitude = realLongitude;
myAnnotation.coordinate = theCoordinate;
myAnnotation.title = [foo3 objectAtIndex:3];
//myAnnotation.subtitle = [note objectForKey:#"stationAddressKey"];
[_mapView setDelegate:self];
[_mapView addAnnotation:myAnnotation];
[myAnnotation release];
}
-(MKAnnotationView *)_mapView:(MKMapView *)aMapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MyAnnotation class]])
{
static NSString *reuseId = #"customAnn";
MKAnnotationView *customAnnotationView = [aMapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
if (customAnnotationView == nil)
{
customAnnotationView = [[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId] autorelease];
UIImage *pinImage = [UIImage imageNamed:#"pin-green.png"];
[customAnnotationView setImage:pinImage];
customAnnotationView.canShowCallout = YES;
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
customAnnotationView.rightCalloutAccessoryView = rightButton;
}
// NSString *iconFilename = #"";
// MyAnnotation *myAnn = (MyAnnotation *)annotation;
// if ([myAnn.stationIdKey isEqualToString:#"BP"])
iconFilename = #"bp-logo.png";
// else
// if ([myAnn.stationIdKey isEqualToString:#"Caltex"])
// iconFilename = #"caltex.png";
// else
// if ([myAnn.stationIdKey isEqualToString:#"Shell"])
// iconFilename = #"shell.png";
// UIImageView *leftIconView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:iconFilename]] autorelease];
customAnnotationView.leftCalloutAccessoryView = leftIconView;
customAnnotationView.annotation = annotation;
return customAnnotationView;
}
return nil;
}
-(void)_mapView:(MKMapView *)_mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{ if ([view.annotation isKindOfClass:[MyAnnotation class]])
{
MyAnnotation *myAnn = (MyAnnotation *)view.annotation;
NSLog(#"callout button tapped for station id %#", myAnn.stationIdKey);
}
else
{
NSLog(#"callout button tapped for annotation %#", view.annotation);
} }
Is correct this?
I'm adding self.notes array to a UIPickerView. This is how I'm setting the array:
NSMutableArray *notesArray = [[NSMutableArray alloc] init];
[notesArray addObject:#"-"];
[notesArray addObjectsFromArray:[dbManager getTableValues:#"Notes"]];
self.notes = notesArray;
[notesArray release];
The info for the UIPickerView is taken from the database in this method:
-(NSMutableArray *)getTableValues:(NSString *)table
{
NSMutableArray *valuesArray = [[NSMutableArray alloc] init];
if (sqlite3_open([self.databasePath UTF8String], &database) != SQLITE_OK)
{
sqlite3_close(database);
NSAssert(0, #"Failed to open database");
}
else
{
NSString *query = [[NSString alloc] initWithFormat:#"SELECT value FROM %#", table];
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW) {
NSString *value =[NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 0)];
[valuesArray addObject:value];
[value release];
}
sqlite3_reset(statement);
}
[query release];
sqlite3_finalize(statement);
sqlite3_close(database);
}
return valuesArray;
}
But I keep getting memory leaks in Instruments for these lines:
NSMutableArray *valuesArray = [[NSMutableArray alloc] init];
and
[valuesArray addObject:value];
What am I doing wrong here?
Thanks for your help!
Instead of NSMutableArray *valuesArray = [[NSMutableArray alloc] init]; use this line:
NSMutableArray *valuesArray = [NSMutableArray array];
As for the [valuesArray addObject:value]; leak, change your code to this:
[valuesArray addObject:value];
value = nil;
You are returning valuesArray from the getTableValues which is allocated but not released. You can not release it in the method as the caller still needs it. The correct way is to autorelease it.
return [valuesArray autorelease];
And also you don't need to release value string after adding it to valuesArray as it is created with a convenient constructor.