0

For my MatchCenterViewController, I have it set to run the MatchCenter function in viewDidAppear, after a 2 second time interval. This function populates the UITableView with text and images. The issue is, when a user tries to either scroll through the UITableView or tap on any cell from the moment MatchCenterViewController appears until the MatchCenter function is done loading, the app crashes.

It seems to be happening because if a user scrolls or taps on a cell to open up the model 'WebViewSegue', it conflicts with the MatchCenter function which is attempting to populate/update the table. What I want to happen is, if a user starts to scroll through the table or taps on a cell, then the MatchCenter function in viewDidAppear exits.

How would I form this syntax, and once I do, will I have to put this in both 'didSelectRowAtIndexPath' and a separate function, to take into account scrolling as well?

MatchCenterViewController.m:

#import "MatchCenterViewController.h"
#import <UIKit/UIKit.h>

@interface MatchCenterViewController () <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, strong) UITableView *matchCenter;
@end

@implementation MatchCenterViewController


- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
    }
    return self;
}


- (void)viewDidLoad
{
    [super viewDidLoad];

    self.matchCenter = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewCellStyleSubtitle];
    self.matchCenter.frame = CGRectMake(0,50,320,self.view.frame.size.height-100);
    _matchCenter.dataSource = self;
    _matchCenter.delegate = self;
    [self.view addSubview:self.matchCenter];

    _matchCenterArray = [[NSArray alloc] init];
}

- (void)viewDidAppear:(BOOL)animated
{
    self.matchCenterArray = [[NSArray alloc] init];

    // Delay to allow MatchCenter item enough time to be added before pinging ebay
    [NSThread sleepForTimeInterval:2];

    [PFCloud callFunctionInBackground:@"MatchCenter"
                       withParameters:@{}
                                block:^(NSArray *result, NSError *error) {

                                    if (!error) {
                                        _matchCenterArray = result;
                                        [_matchCenter reloadData];

                                        NSLog(@"Result: '%@'", result);
                                    }
                                }];
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return _matchCenterArray.count;
}

//the part where i setup sections and the deleting of said sections

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    return 21.0f;
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
    return 0.01f;
}


- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 21)];
    headerView.backgroundColor = [UIColor lightGrayColor];

    _searchTerm = [[[[_matchCenterArray  objectAtIndex:section] objectForKey:@"Top 3"] objectAtIndex:3]objectForKey:@"Search Term"];

    UILabel *headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(8, 0, 250, 21)];
    headerLabel.text = [NSString stringWithFormat:@"%@", _searchTerm];
    headerLabel.font = [UIFont boldSystemFontOfSize:[UIFont systemFontSize]];
    headerLabel.textColor = [UIColor whiteColor];
    headerLabel.backgroundColor = [UIColor lightGrayColor];
    [headerView addSubview:headerLabel];


    UIButton *deleteButton = [UIButton buttonWithType:UIButtonTypeCustom];
    deleteButton.tag = section;
    deleteButton.frame = CGRectMake(300, 2, 17, 17);
    [deleteButton setImage:[UIImage imageNamed:@"xbutton.png"] forState:UIControlStateNormal];
    [deleteButton addTarget:self action:@selector(deleteButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
    [headerView addSubview:deleteButton];
    return headerView;

}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 3;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Initialize cell
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (!cell) {
        // if no cell could be dequeued create a new one
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }

    // No cell seperators = clean design
    tableView.separatorColor = [UIColor clearColor];

    // title of the item
    cell.textLabel.text = _matchCenterArray[indexPath.section][@"Top 3"][indexPath.row][@"Title"];
    cell.textLabel.font = [UIFont boldSystemFontOfSize:14];

    // price of the item
    cell.detailTextLabel.text = [NSString stringWithFormat:@"$%@", _matchCenterArray[indexPath.section][@"Top 3"][indexPath.row][@"Price"]];
    cell.detailTextLabel.textColor = [UIColor colorWithRed:0/255.0f green:127/255.0f blue:31/255.0f alpha:1.0f];

    // image of the item
    NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:_matchCenterArray[indexPath.section][@"Top 3"][indexPath.row][@"Image URL"]]];
    [[cell imageView] setImage:[UIImage imageWithData:imageData]];

    return cell;

}


- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 65;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{ 
    self.itemURL = _matchCenterArray[indexPath.section][@"Top 3"][indexPath.row][@"Item URL"];
    [self performSegueWithIdentifier:@"WebViewSegue" sender:self];
}

- (void)deleteButtonPressed:(id)sender
{
    // links button
    UIButton *deleteButton = (UIButton *)sender;

    // Define the sections title
    NSString *sectionName = _searchTerm = [[[[_matchCenterArray  objectAtIndex:deleteButton.tag] objectForKey:@"Top 3"] objectAtIndex:3]objectForKey:@"Search Term"];

    // Run delete function with respective section header as parameter
    [PFCloud callFunctionInBackground:@"deleteFromMatchCenter"
                       withParameters:
                      @{@"searchTerm": sectionName,}
                                block:^(NSDictionary *result, NSError *error) {
                                   if (!error) {
                                       [PFCloud callFunctionInBackground:@"MatchCenter"
                                                          withParameters:@{
                                                                           @"test": @"Hi",
                                                                           }
                                                                   block:^(NSArray *result, NSError *error) {

                                                                       if (!error) {
                                                                           _matchCenterArray = result;
                                                                           [_matchCenter reloadData];

                                                                           NSLog(@"Result: '%@'", result);
                                                                       }
                                                                   }];

                                   }
                                }];
}



- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}



 #pragma mark - Navigation

 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
 {
     WebViewController *controller = (WebViewController *) segue.destinationViewController;
     controller.itemURL = self.itemURL;
 }


@end
Ghobs
  • 839
  • 2
  • 14
  • 32
  • well I wouldnt sleep main thread for 2 seconds in any occassion, also if you want to load some images into your table, try not to make synchronous requests - this is just too slow if you don't have the perfect device with fast internet on it, instead look up for GCD solutions. and btw -> if you want to reload table or do anything else that supposed to be runned on the main thread, do it there – deathhorse Sep 19 '14 at 20:06
  • except the things i mentioned before, your biggest issue is that you're telling in delegate method that you already have 3 cells. well meanwhile your matcharray is still empty and when you tap on any of those 3 cells, you'll invoke didSelectRow:atIndexPath: delegate method -> which will certainly crash, because you're trying to get object at index that doesn't exist in your empty array. – deathhorse Sep 19 '14 at 20:10

1 Answers1

1

You are blocking on the main thread! Not good! See this question: NSThread sleepfortimeinterval blocks main thread

A much better way to have a delay is to use GCD. Try this instead:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [PFCloud callFunctionInBackground:@"MatchCenter"
                       withParameters:@{}
                                block:^(NSArray *result, NSError *error) {
                                    if (!error) {
                                        // Might not be necessary, but it would be good to  check to make
                                        // sure _matchCenterArray and _matchCenter still exist.
                                        if (_matchCenterArray && _matchCenter) {
                                            // I am not sure if this block is invoked on the main thread
                                            dispatch_async(dispatch_get_main_queue(), ^{
                                                _matchCenterArray = result;
                                                [_matchCenter reloadData];

                                                NSLog(@"Result: '%@'", result);
                                            })
                                        }
                                    }
                                }];
});
Community
  • 1
  • 1
Justin Moser
  • 2,005
  • 3
  • 22
  • 28
  • Ahh, makes sense, thanks for the tip. Any idea how I can solve the problem in my main question? – Ghobs Sep 19 '14 at 20:04
  • Sure! Just add a BOOL to your viewController (as a property if you would prefer) and set it to YES when the block should be disabled. Then add an if statement at the beginning of the PFCloud block and check the value of the BOOL. If it is YES then return. – Justin Moser Sep 19 '14 at 20:13
  • Gotcha. I've used a bool to tell the code in `didSelectRowAtIndexPath` to only run if that bool == YES, it is set to yes when `MatchCenter` finishes. Tapping a row while `MatchCenter` is still running doesn't cause a crash anymore, but scrolling still does. You think you could show me the syntax to use this bool method to have the function return if I start scrolling? I'm a bit new to Obj-C :) – Ghobs Sep 19 '14 at 22:11