I'm running a Parse program which grabs the data and creates objects from it using 'findObjectsInBackgroundWithBlock'. Once that is complete I update self and then call '[self.tableView setNeedsDisplay]', but nothing changes on my display and no new items are shown. Am I doing something wrong? and how do I fix it?
-(void)pullDown{
NSLog(#"Began PullDown");
NSMutableArray *bugs = [NSMutableArray arrayWithObjects: nil];
//NSMutableArray *bugs = [NSMutableArray arrayWithObjects: nil];
NSLog(#"Journal POSTS");
PFQuery *queryJournal = [PFQuery queryWithClassName:#"Post"];
[queryJournal whereKey:#"user" equalTo:[PFUser currentUser]];
NSLog(#"WHEN ARE YOU CALLED?");
[queryJournal findObjectsInBackgroundWithBlock:^(NSArray *posts, NSError *error) {
if (!error) {
// The find succeeded.
//NSLog(#"Successfully retrieved %# Posts.", posts);
// Do something with the found objects
for (PFObject *object in posts) {
int rating = object[#"Rating"];
NSLog(#"RATING object: %#; int: %i", object[#"Rating"], rating);
MSJournalerDoc *post = [[MSJournalerDoc alloc] initWithTitle:object[#"Title"] rating:rating thumbImage:object[#"imageFile"] fullImage:object[#"imageFile"]];
[bugs addObject:post];
}
NSLog(#"HELL YA");
NSLog(#"THE LIST: %#", bugs);
self.bugs = bugs;
NSLog(#"END OF LOOOOOOOOOOOOOOOPS %lu", bugs.count);
[self.tableView setNeedsDisplay];
NSLog(#"MOAR");
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
if ([PFUser currentUser]) {
NSLog(#"CURRENT USER");
[self pullDown];
NSLog(#"POST CURRENT USER");
}
else{
[self createUser];
[self createBug];
}
self.navigationItem.leftBarButtonItem = self.editButtonItem;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(insertNewObject:)];
self.navigationItem.rightBarButtonItem = addButton;
//Change the Title
self.title = #"Posts";
self.navigationItem.leftBarButtonItem = self.editButtonItem;
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self action:#selector(addTapped:)];
NSLog(#"Finished ViewDidLoad");
}
You have a couple of different issues.
First, calling setNeedsDisplay on a view causes it to be redrawn, but this isn't enough for a table view because none of its data has been updated. Instead, you need to call reloadData which will update the data and trigger a redraw automatically.
Secondly, you are trying to use images returned by parse, but parse never returns images (at least not directly). So, accessing object[#"imageFile"] is returning you a PFFile, not a UIImage. You need to call getDataInBackgroundWithBlock: to get the image data before you can use it.
Related
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];
}
}
I'm working on adding a CollectionView into a custom container that I've created. I've used the Container before to host UITableView's but never a CollectionView. Didn't it'd be so hard either. Just thought I had to switch some words around poof it would work! But sadly thats not the way things. Instead I just receive crash notices.
My program keeps crashing right here.
//0. Remove the current Detail View Controller showed
if(self.currentDetailViewController){
[self removeCurrentDetailViewController];
}
Can someone please help me understand why?
here is my code
#import "YourHome.h"
#import "HomePostTable.h"
#import "HomeStoryView.h"
#interface YourHome ()
#end
#implementation YourHome
-(void)queryParseMethod {
NSLog(#"start query");
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
// [query whereKey:#"photo" equalTo:[PFUser currentUser]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
UsersFilesArray = [[NSArray alloc] initWithArray:objects];
NSLog(#"%#", UsersFilesArray);
//PFObject *imageObject = [PFUser currentUser];
for (PFObject *imageObject in UsersFilesArray) {
// NSLog(#"%#", object.objectId);
PFFile *ImageFile = [imageObject objectForKey:#"photo"];
[ImageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
_ProfilePic.image = [UIImage imageWithData:data];
Background.image = _ProfilePic.image;
_ProfilePic.layer.cornerRadius = _ProfilePic.frame.size.width / 2;
_ProfilePic.clipsToBounds = YES;
_ProfilePic.layer.borderWidth = 2.0f;
_ProfilePic.layer.borderColor = [UIColor whiteColor].CGColor;
}
else {
NSLog(#"errrrrrr");
}
}];
}
}
}];
}
- (void)presentDetailController1:(UICollectionViewController*)detailVC{
//0. Remove the current Detail View Controller showed
if(self.currentDetailViewController){
[self removeCurrentDetailViewController];
}
//1. Add the detail controller as child of the container
[self addChildViewController:detailVC];
//2. Define the detail controller's view size
detailVC.view.frame = [self frameForDetailController];
//3. Add the Detail controller's view to the Container's detail view and save a reference to the detail View Controller
[self.detailView addSubview:detailVC.view];
self.currentDetailViewController = detailVC;
//4. Complete the add flow calling the function didMoveToParentViewController
[detailVC didMoveToParentViewController:self];
}
- (void)removeCurrentDetailViewController{
NSLog(#"Go");
//1. Call the willMoveToParentViewController with nil
// This is the last method where your detailViewController can perform some operations before neing removed
[self.currentDetailViewController willMoveToParentViewController:nil];
//2. Remove the DetailViewController's view from the Container
[self.currentDetailViewController.view removeFromSuperview];
//3. Update the hierarchy"
// Automatically the method didMoveToParentViewController: will be called on the detailViewController)
[self.currentDetailViewController removeFromParentViewController];
}
- (CGRect)frameForDetailController{
CGRect detailFrame = self.detailView.bounds;
return detailFrame;
}
- (void)viewDidLoad {
[self queryParseMethod];
HomeStoryView *detailOne = [[HomeStoryView alloc]init];
[self presentDetailController1:detailOne];
}
I'm new to iOS development and in my app I'm seeing some strange memory usage behavior.
I'm getting objects from server in such setupDataForPage method:
- (void)setupDataForPage:(int)page actionType:(NSString *)type success:(void (^)())callback
{
__weak MyTableViewController *weakSelf = self;
// clearing image cache because feed contains a lot of images
[[SDImageCache sharedImageCache] clearMemory];
[[SDImageCache sharedImageCache] clearDisk];
MyHTTPClient *API = [MyHTTPClient new];
[API feedFor:page success:^(AFHTTPRequestOperation *operation, id data) {
NSArray *data = [data objectForKey:#"data"];
if ([data count] > 0) {
// remove all objects to refresh with new ones
if ([type isEqualToString:#"pullToRefresh"]) {
[weakSelf.models removeAllObjects];
}
// populate data
NSMutableArray *result = [NSMutableArray new];
for (NSDictionary *modelData in data) {
MyModel *model = [[MyModel alloc] initWithDictionary:modelData];
[result addObject:model];
}
[weakSelf.models addObjectsFromArray:result];
[weakSelf.tableView reloadData];
}
callback();
} failure:nil];
}
it is used in viewDidLoad while getting initial request and also for pull refresh and infinite scrolling:
- (void)viewDidLoad {
[super viewDidLoad];
__block int page = 1;
__weak MyTableViewController *weakSelf = self;
// initial load
[self setupDataForPage:page actionType:#"initial" success:^{ page += 1; }];
// pull to refresh
[self.tableView addPullToRefreshWithActionHandler:^{
[weakSelf setupDataForPage:1 actionType:#"pullToRefresh" success:^{
[weakSelf.tableView.pullToRefreshView stopAnimating];
}];
}];
// infinite scrolling
[self.tableView addInfiniteScrollingWithActionHandler:^{
[weakSelf setupItemsForPage:page actionType:#"infiniteScroll" success:^{
page += 1;
[weakSelf.tableView.infiniteScrollingView stopAnimating];
}];
}];
}
I noticed that even after pull to refresh action which returns the same data (and I'm just removing all models and add them once more) my app's memory usage grows from nearly 19mb to 24mb..
I would like someone more experienced to look at this piece of code to determine whether it contains some possible memory leaks.. Should I somehow delete NSMutableArray *result variable after assigning it to models array?
Thanks!
First of all, use #autoreleasepool here:
#autoreleasepool {
NSArray *data = [data objectForKey:#"data"];
if ([data count] > 0) {
// remove all objects to refresh with new ones
if ([type isEqualToString:#"pullToRefresh"]) {
[weakSelf.models removeAllObjects];
}
// populate data
NSMutableArray *result = [NSMutableArray new];
for (NSDictionary *modelData in data) {
MyModel *model = [[MyModel alloc] initWithDictionary:modelData];
[result addObject:model];
}
[weakSelf.models addObjectsFromArray:result];
[weakSelf.tableView reloadData];
}
}
#autoreleasepool allows you to release every object allocated in that scope IMMEDIATELY.
This is perfect situation where use it ;)
I am showing an several images using an UIScrollView. however I would want it to refresh automatically.
I want to refresh it every time I show the view controller.
Using my code I have to push the refresh button every time which is just nonsense.
Here is the code : How can i do this ?
- (IBAction)refresh:(id)sender
{
NSLog(#"Showing Refresh HUD");
refreshHUD = [[MBProgressHUD alloc] initWithView:self.view];
[self.view addSubview:refreshHUD];
// Register for HUD callbacks so we can remove it from the window at the right time
refreshHUD.delegate = self;
// Show the HUD while the provided method executes in a new thread
[refreshHUD show:YES];
PFQuery *query = [PFQuery queryWithClassName:#"TPhoto"];
PFUser *user = [PFUser currentUser];
[query whereKey:#"user" equalTo:user];
[query orderByAscending:#"createdAt"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
if (refreshHUD) {
[refreshHUD hide:YES];
refreshHUD = [[MBProgressHUD alloc] initWithView:self.view];
[self.view addSubview:refreshHUD];
// The sample image is based on the work by http://www.pixelpressicons.com, http://creativecommons.org/licenses/by/2.5/ca/
// Make the customViews 37 by 37 pixels for best results (those are the bounds of the build-in progress indicators)
refreshHUD.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"37x-Checkmark.png"]];
// Set custom view mode
refreshHUD.mode = MBProgressHUDModeCustomView;
refreshHUD.delegate = self;
}
NSLog(#"Successfully retrieved %d photos.", objects.count);
// Retrieve existing objectIDs
NSMutableArray *oldCompareObjectIDArray = [NSMutableArray array];
for (UIView *view in [photoScrollView subviews]) {
if ([view isKindOfClass:[UIButton class]]) {
UIButton *eachButton = (UIButton *)view;
[oldCompareObjectIDArray addObject:[eachButton titleForState:UIControlStateReserved]];
}
}
NSMutableArray *oldCompareObjectIDArray2 = [NSMutableArray arrayWithArray:oldCompareObjectIDArray];
// If there are photos, we start extracting the data
// Save a list of object IDs while extracting this data
NSMutableArray *newObjectIDArray = [NSMutableArray array];
if (objects.count > 0) {
for (PFObject *eachObject in objects) {
[newObjectIDArray addObject:[eachObject objectId]];
}
}
// Compare the old and new object IDs
NSMutableArray *newCompareObjectIDArray = [NSMutableArray arrayWithArray:newObjectIDArray];
NSMutableArray *newCompareObjectIDArray2 = [NSMutableArray arrayWithArray:newObjectIDArray];
if (oldCompareObjectIDArray.count > 0) {
// New objects
[newCompareObjectIDArray removeObjectsInArray:oldCompareObjectIDArray];
// Remove old objects if you delete them using the web browser
[oldCompareObjectIDArray removeObjectsInArray:newCompareObjectIDArray2];
if (oldCompareObjectIDArray.count > 0) {
// Check the position in the objectIDArray and remove
NSMutableArray *listOfToRemove = [[NSMutableArray alloc] init];
for (NSString *objectID in oldCompareObjectIDArray){
int i = 0;
for (NSString *oldObjectID in oldCompareObjectIDArray2){
if ([objectID isEqualToString:oldObjectID]) {
// Make list of all that you want to remove and remove at the end
[listOfToRemove addObject:[NSNumber numberWithInt:i]];
}
i++;
}
}
// Remove from the back
NSSortDescriptor *highestToLowest = [NSSortDescriptor sortDescriptorWithKey:#"self" ascending:NO];
[listOfToRemove sortUsingDescriptors:[NSArray arrayWithObject:highestToLowest]];
for (NSNumber *index in listOfToRemove){
[allImages removeObjectAtIndex:[index intValue]];
}
}
}
// Add new objects
for (NSString *objectID in newCompareObjectIDArray){
for (PFObject *eachObject in objects){
if ([[eachObject objectId] isEqualToString:objectID]) {
NSMutableArray *selectedPhotoArray = [[NSMutableArray alloc] init];
[selectedPhotoArray addObject:eachObject];
if (selectedPhotoArray.count > 0) {
[allImages addObjectsFromArray:selectedPhotoArray];
}
}
}
}
// Remove and add from objects before this
[self setUpImages:allImages];
} else {
[refreshHUD hide:YES];
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
Call the refresh method from viewDidAppear in your view controller.
However, if that can happen often, you should consider keeping the images in a local cache and only refresh them if there is something new to show on the server.
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.