I have a screen which shows favourite locations and if a user wants to add new location he can search using uisearchcontroller and add it to favourite location's list.
The problem is that once i make the api call for autocomplete the list of searched locations is visible but i cannot select or scroll the table.
I know that the problem is where i set the uisearchcontroller. but i do not know the right way to do it.
I am newbie in iOS so ur suggestions will be welcome.
the following is the GithubRepository for the project(incase ul want to try out the app and make suggestions)
and heres the related code where i beleive the problem exists.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
favList = [[NSMutableArray alloc] init];
searchList = [[NSMutableArray alloc]init];
self.table.dataSource = self;
self.table.delegate = self;
[self initSearch];
}
#pragma mark UI Search methods
-(void) initSearch
{
FavouriteViewController *searchResultsController = [[FavouriteViewController alloc] init];
searchResultsController.table.dataSource = self;
searchResultsController.table.delegate = self;
searchResultsController.table.allowsSelectionDuringEditing = YES;
searchResultsController.table.allowsSelection = YES;
self.search = [[UISearchController alloc] initWithSearchResultsController:searchResultsController];
self.definesPresentationContext = NO;
self.table.tableHeaderView = self.search.searchBar;
self.search.searchResultsUpdater = self;
self.search.searchBar.delegate = self;
self.search.dimsBackgroundDuringPresentation = NO;
}
didSelectRowAtIndex method
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.search.active) {
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObjectModel *place = [NSEntityDescription insertNewObjectForEntityForName:#"Place" inManagedObjectContext:context];
NSString *places =[searchList[indexPath.row] valueForKey:#"name"];
if (![favList containsObject:places])
{
NSString *lati = [searchList[indexPath.row] valueForKey:#"lat"];
NSString *longi = [searchList[indexPath.row] valueForKey:#"lon"];
if (places != NULL && lati != NULL && longi != NULL)
{
[place setValue:places forKey:#"placeName"];
[place setValue:lati forKey:#"latitude"];
[place setValue:longi forKey:#"longitude"];
}
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
}
[self.search setActive:NO];
filtered = NO;
[self getalldata];
[self.table reloadData];
}
else
{
NSManagedObject *device = [favList objectAtIndex:indexPath.row];
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.SJI.Weather-App"];
[defaults setObject:[device valueForKey:#"placeName"] forKey:#"favSet"];
[[self navigationController] popToRootViewControllerAnimated:YES];
}
}
Related
Hello i am using UISerchbar in tableview but is there any easy way to create custom searchbar in tableview headerview?
i already check below link but i am not understand.
Custom UISearchBar with UISearchController
How do I use the UISearchBar and UISearchDisplayController
I have added searchDisplayController : :
And This is my code for searchController,
self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
self.searchController.searchResultsUpdater = self;
self.searchController.dimsBackgroundDuringPresentation = NO;
self.searchController.searchBar.delegate = self;
self.definesPresentationContext = YES;
[self.searchController.searchBar setBarTintColor:[UIColor darkGrayColor]];
self.tableView.tableHeaderView = self.searchController.searchBar;
I have set delegate for UISearchBarDelegate,UISearchResultsUpdating as well,
and implemented their method ,
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
if (_searchController.isActive && _searchController.searchBar.text.length >0){
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"SELF.name contains[cd] %#",
searchText];
abcd = [sortedEventArray filteredArrayUsingPredicate:resultPredicate];
NSLog(#"%#",searchResults);
}
}
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController1
{
NSString *searchString = searchController1.searchBar.text;
[self filterContentForSearchText:searchString scope:#"abc"];
//[self filterContentForSearchText:searchString :#"abc"];
[self.tableView reloadData];
}
cancel button event
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
abcd=[sortedEventArray mutableCopy];
[_tableView reloadData];
}
-(void)delete{
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest * allMovies = [[NSFetchRequest alloc] init];
[allMovies setEntity:[NSEntityDescription entityForName:#"Data" inManagedObjectContext:context]];
[allMovies setIncludesPropertyValues:NO]; //only fetch the managedObjectID
NSError * error = nil;
NSArray * movies = [context executeFetchRequest:allMovies error:&error];
//error handling goes here
for (NSManagedObject * movie in movies) {
[context deleteObject:movie];
}
NSError *saveError = nil;
[context save:&saveError];
}
Still Its not working
Thanks in advance :)
There is everythings is complete but there is two little mistakes by me.
1) delete the search bar from Storyboard.
2) Add [self.searchController.searchBar sizeToFit];
in viewDidLoad
Thanks :))
I have a method in ViewController.m called getData which is called inside viewDidLoad:
-(void)getData {
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:#"WorkoutHasExercise" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
request.resultType = NSDictionaryResultType;
request.propertiesToFetch = [NSArray arrayWithObjects:#"exerciseName", #"reps", #"sets", nil];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(workoutName = %#)", _workoutName];
[request setPredicate:pred];
NSManagedObject *matches = nil;
NSError *error;
NSArray *objects = [context executeFetchRequest:request error:&error];
if ([objects count] == 0) {
} else {
[_exercises removeAllObjects];
for (int x = 0; x < [objects count]; x++) {
matches = objects[x];
[_exercises addObject:[matches valueForKey:#"exerciseName"]];
[_totalSets addObject:[matches valueForKey:#"sets"]];
[_totalReps addObject:[matches valueForKey:#"reps"]];
[_currentSets addObject:[NSNumber numberWithInteger:0]];
}
}
[_exercisesTableView reloadData];
}
I also have a custom UITableViewCell with two buttons initiated in cellForRowAtIndexPath:
ActiveWorkoutCell *cell = (ActiveWorkoutCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ActiveWorkoutCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
cell.increaseButton.tag = indexPath.row;
cell.decreaseButton.tag = indexPath.row;
In ActiveWorkoutCell.m I have 2 IBActions for the buttons:
- (IBAction)decreaseSets:(id)sender {
ActiveWorkoutViewController *vc = [[ActiveWorkoutViewController alloc] init];
[vc decreaseSets:[sender tag]];
}
- (IBAction)increaseSets:(id)sender {
ActiveWorkoutViewController *vc = [[ActiveWorkoutViewController alloc] init];
[vc increaseSets:[sender tag]];
}
The IBActions call these 2 methods back in ViewController.m
-(void)increaseSets:(NSInteger)row {
[self getData];
//There will be code here to increase the value of currentSets[row]
}
-(void)decreaseSets:(NSInteger)row {
[self getData]
//Code to decrease value...
}
PROBLEM:
When getData is called from viewDidLoad, it works fine.
The problem occurs when returning to ViewController.m from the IBAction in ActiveWorkoutCell.m.
When I call [self getData] in increaseSets the fetch request returns an empty array. This is what is confusing me - the code works fine when it is first called but not at all when called the second time after the custom cell Action has been triggered.
Here is my viewDidLoad if it helps:
- (void)viewDidLoad {
[super viewDidLoad];
_exercises = [NSMutableArray array];
_totalSets = [NSMutableArray array];
_currentSets = [NSMutableArray array];
_totalReps = [NSMutableArray array];
_titleLabel.text = _workoutName;
_exercisesTableView.allowsSelection = NO;
[self getData];
}
_workoutName is given a value in prepareForSegue in the previous view controller.
I think I found the issue. You are instantiating the "ActivityWorkOutViewController" when the IBAction methods called and it will be a new instance and then in those methods you are calling [self getData] which pointing to the new instance which has no variables instantiated or viewDidLoad happened, so your mutable arrays are not allocated and hence they are empty.
Just use the old instance of the class to get the data.
I am not sure how you are referencing those classes. I am just in a confusion about that. But, you might check the allocations and calling the right class to get the data
My program:
I have view parentView and childView. childView has a UITextField. This UITextField is completed by the user. Then UITextField.text is passed as a NSString to parentView via a NSNotificationCenter. The NSLog in the Notification method shows that the value has been passes successfully to parentView.
Now the issue.... when I try to access _guideDescription (that contains the value being passed) in the saveIntoExisting my app crashes. I dont understand why is crashing when it was able to retrieve the value in the notification method.
There is no error just (llbs).
Anyone has an idea why is this happening?
- (void)viewWillAppear:(BOOL)animated {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(notificationSelectedDescription:)
name:#"selectedDescription"
object:nil];
}
- (void) notificationSelectedDescription:(NSNotification *)notification {
_guideDescription = [[notification userInfo] valueForKey:#"selected"];
NSLog(#"Show me: %#",[_guideDescription description]);
}
- (IBAction)createExercise:(UIButton *)sender {
if (![self fetch]) {
NSLog(#"It doesnt exist already");
[self save];
} else {
NSLog(#"It does exist already");
/*
* ADD CODE ON HOW TO ACT WHEN EXISTS
*/
[self saveIntoExisting];
}
}
- (void) saveIntoExisting {
NSLog(#"saveintoExisting 1");
NSLog(#"saveintoExisting show me: %#",[_guideDescription description]);
NSFetchRequest *fetchExercise = [[NSFetchRequest alloc] init];
NSEntityDescription *entityItem = [NSEntityDescription entityForName:#"Exercise" inManagedObjectContext:context];
[fetchExercise setEntity:entityItem];
[fetchExercise setPredicate:[NSPredicate predicateWithFormat:#"exerciseName == %#",_originalExerciseName]];
[fetchExercise setRelationshipKeyPathsForPrefetching:[NSArray arrayWithObjects:#"User", nil]];
fetchExercise.predicate = [NSPredicate predicateWithFormat:#"user.email LIKE %#",_appDelegate.currentUser];
NSError *error = nil;
NSArray* oldExercise = [[context executeFetchRequest:fetchExercise error:&error] mutableCopy];
Exercise *newExercise = (Exercise*) oldExercise[0];
newExercise.exerciseName = _exerciseName.text;
newExercise.exercisePic = _selectedImage;
if (![_guideDescription isEqualToString:#""]) {
newExercise.exerciseDescription =_guideDescription;
}
if (_youTubeLink){
newExercise.youtube =_youTubeLink;
}
if (![_selectRoutine.titleLabel.text isEqualToString:#"add to routine"]) {
newExercise.routine = [[self getCurrentRoutine] exercise];
[[self getCurrentUser] addRoutines:[[self getCurrentRoutine] exercise]];
}
[[self getCurrentUser] addExercisesObject:newExercise];
error = nil;
[context save:&error ];
}
The problem is probably that that _guideDescription string already is deallocated when you try to access it in -saveIntoExisted. You are now doing
_guideDescription = [[NSString alloc] initWithString:[[notification userInfo] valueForKey:#"selected"]];
and that works, but I would recommend:
[_guideDescription retain];
That makes sure the system doesn't deallocate the string.
I am developing an apps for data management system for iOS 6. This is my code:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.navigationItem.title = #"Detail";
if (self.student.thumbnailImage != nil) {
[self.imageButton setImage:[UIImage imageWithData:self.student.thumbnailImage] forState:UIControlStateNormal];
} else {
[self.imageButton setImage:[UIImage imageNamed:#"Default_photo3.png"] forState:UIControlStateNormal];
}
NSLog(#"Student First = %#", self.student.firstname);
self.firstNameTF.text = self.student.firstname;
NSLog(#"Student Last = %#", self.student.lastname);
self.lastNameTF.text = self.student.lastname;
NSLog(#"Student IC = %#", self.student.ic);
self.icTF.text = self.student.ic;
NSLog(#"Student Form = %#", self.student.form);
self.formTF.text = self.student.form;
NSLog(#"Student Gender = %#", self.student.gender);
self.genderTF.text = self.student.gender;
NSLog(#"Student Birthday = %#", self.student.birthday);
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd/MM/yyyy"];
NSString *date = [dateFormatter stringFromDate:self.student.birthday];
self.birthdateTF.text = date;;
self.dateButton.titleLabel.text =date;
NSLog(#"Address = %#", self.student.address.street);
NSLog(#"Zip = %#", self.student.address.zip);
NSLog(#"City = %#", self.student.address.city);
NSLog(#"State = %#", self.student.address.state);
[self updateImageButton];
//Create a mutable array for guardians
NSSortDescriptor *sortDEscriptors = [[NSSortDescriptor alloc]initWithKey:#"firstname" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDEscriptors, nil];
NSMutableArray *sortedGuardians = [[NSMutableArray alloc] initWithArray:[self.student.guardians allObjects]];
[sortedGuardians sortUsingDescriptors:sortDescriptors];
self.guardiansArray= sortedGuardians;
[self.tableView reloadData];
}
I found out that my tableview didn't scroll at the beginning. But when i remove the [self.tableView reloadData] in this part, it did scroll normally. Does anyone know why? I need to put the [self.tableView reloadData] here so the data is updated after editing.
Put the reloadData in ViewDidAppear method, so you will set your data before the view appear and request the reload after it appears.
You can set offset as it was before data reload
CGPoint offset = self.tableView.contentOffset;
[self.tableView reloadData];
[self.view layoutIfNeeded];
[self.tableView setContentOffset:offset animated:NO];
Hope this will help someone
I am creating a multi-user iPhone app, and I am trying to finish up the coding for the user login in process. I can successfully create an account, and store the data the user inputs into the Core Data DB, and the pin (password) into the Keychain, so now I am trying to complete the login process. The following code listed below is what I have so far, and I am wondering what I need to do to complete the login process.
- (IBAction)processLogin:(id)sender {
// hide keyboard
[_textFieldUsername resignFirstResponder];
[_textFieldPin resignFirstResponder];
// First - make activity indicator visible, then start animating, then turn of wrong user / pin label
_welcomeActivityIndicator.hidden = FALSE;
[_welcomeActivityIndicator startAnimating];
[_wrongUserPin setHidden:YES];
// check if username and pin text fields are populated
if ([_textFieldUsername.text length ] == 0 && [_textFieldPin.text length ] == 0)
{
[_welcomeActivityIndicator stopAnimating];
[_wrongUserPin setHidden:NO];
}
// CORE DATA
NSFetchRequest *request= [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Account" inManagedObjectContext:_managedObjectContext];
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"username=%#",self.textFieldUsername.text];
//check pin
Account *pinAccount = [[Account alloc] init];
[pinAccount.password isEqualToString:_textFieldPin.text];
[request setEntity:entity];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *array = [_managedObjectContext executeFetchRequest:request error:&error];
if (array != nil) {
NSUInteger count = [array count]; // may be 0 if the object has been deleted.
NSLog(#"Username may exist, %#",count);
}
else {
NSLog(#"Username does not exist.");
}
// TODO - put this in proper place - play audio bell if user logs in correctly
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef soundFileURLRef;
soundFileURLRef = CFBundleCopyResourceURL(mainBundle, (CFStringRef) #"Glass", CFSTR("aiff"), NULL);
UInt32 soundID;
AudioServicesCreateSystemSoundID(soundFileURLRef, &soundID);
AudioServicesPlaySystemSound(soundID);
// TODO - put this in proper place - Load ViewControllerHome
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewControllerHome *home = (ViewControllerHome *)[storyboard instantiateViewControllerWithIdentifier:#"Home"];
[self presentModalViewController:home animated:YES];
}
The Account and AccountBase class files m and h look like the following:
Account.h http://pastie.org/4149299
Account.m http://pastie.org/4149296
AccountBase.h http://pastie.org/4149301
AccountBase.m http://pastie.org/4149302
I would appreciate any ideas or thoughts, and thanks for reading.
I was able to complete the login process with the following code.
- (IBAction)processLogin:(id)sender {
// hide keyboard
[_textFieldUsername resignFirstResponder];
[_textFieldPin resignFirstResponder];
// First - make activity indicator visible, then start animating, then turn of wrong user / pin label
_welcomeActivityIndicator.hidden = FALSE;
[_welcomeActivityIndicator startAnimating];
[_wrongUserPin setHidden:YES];
// check if username and pin text fields are populated
if ([_textFieldUsername.text length ] == 0 && [_textFieldPin.text length ] == 0)
{
[_welcomeActivityIndicator stopAnimating];
[_wrongUserPin setHidden:NO];
}
// CORE DATA
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Account" inManagedObjectContext:_managedObjectContext];
// set entity for request
[request setEntity:entity];
// filter results using a predicate
NSPredicate *pred =[NSPredicate predicateWithFormat:(#"username = %#"), _textFieldUsername.text];
// set predicate for the request
[request setPredicate:pred];
NSError *error = nil;
// store DB usernames in results array
NSArray *results = [_managedObjectContext executeFetchRequest:request error:&error];
NSLog(#"The returned results are %#",results);
// check text field against results stored in DB
for (Account *anAccount in results) {
if ([anAccount.username isEqualToString:_textFieldUsername.text]){
NSLog(#"Your username exists");
if ([anAccount.password isEqualToString:_textFieldPin.text]){
NSLog(#"Your pin is correct");
// TODO - put this in proper place - play audio bell if user logs in correctly
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef soundFileURLRef;
soundFileURLRef = CFBundleCopyResourceURL(mainBundle, (CFStringRef) #"Glass", CFSTR("aiff"), NULL);
UInt32 soundID;
AudioServicesCreateSystemSoundID(soundFileURLRef, &soundID);
AudioServicesPlaySystemSound(soundID);
// TODO - put this in proper place - Load ViewController(Root)Home
if([anAccount.username isEqualToString:#"root"])
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewControllerRootHome *roothome = (ViewControllerRootHome *)[storyboard instantiateViewControllerWithIdentifier:#"rootHome"];
[self presentModalViewController:roothome animated:YES];
}
else {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewControllerHome *home = (ViewControllerHome *)[storyboard instantiateViewControllerWithIdentifier:#"Home"];
[self presentModalViewController:home animated:YES];
}
}
else {
NSLog(#"Your pin is wrong");
[_welcomeActivityIndicator stopAnimating];
[_wrongUserPin setHidden:NO];
}
}
else {
NSLog(#"Your username was not found");
[_welcomeActivityIndicator stopAnimating];
[_wrongUserPin setHidden:NO];
}
}
}
I believe your mis-using core data. It is not meant to be used like sqlite, always running always query-able. (http://cocoawithlove.com/2010/02/differences-between-core-data-and.html)
You are trying to keep a database of user but your also over complicating it. While in memory the app can use an NSMutableArray of your own objects to keep track of usernames, tabs
So you would have your class:
interface Alcoholic : NSObject
#property(nonatomic,retain)NSString * username;
#property(nonatomic,retain)NSString * pin;
#property(nonatomic,retain)NSString * tab;
#end
In the section of code that adds these it would have a mutable array
NSMutableArray * alcoholics = [[NSMutableArray alloc]init];
- (IBAction)processLogin:(id)sender {
int i = 0;
while (i<alcoholics.count)
{
Aloholic = [alcoholics objectAtIndex:i];
if(self.textFieldUsername.text == Aloholic.username)
NSLog(#"Username may exist, %#",count);
}
else {
NSLog(#"Username does not exist.");
}
}
etc...
Since Im sure your next question is how to make it persistent and thats where core data comes into play.
in your app delegate get the alcoholics array and save that to core data when the app opens and closes
-(void)save
{
//use core data to save needed data persitently
self.Values = [NSEntityDescription
insertNewObjectForEntityForName:#"processLoginClass"
inManagedObjectContext:[self managedObjectContext]];
self.Values.alcoholics = self.processLoginClass.alcoholics;
}
NSError *error;
if (![[self managedObjectContext] save:&error])
{
NSLog(#"Couldn't save state information: %#", [error localizedDescription]);
}
}
-(void)load
{
//use core data to load needed data
NSError *error;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Values"
inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
for (processLoginClass *info in fetchedObjects)
{
[self.processLoginClass setAlcholics:info.alcholics];
}
}