0

I'm trying to make a simple bar chart showing the relative durations of sequentially recorded activities, and I can't for the life of me get it to display properly. It's based on simple UIView animations, and parts of it seem to work, namely the drop shadow under the bar. However, the bars themselves never appear.

I'm doubly flummoxed, because I modeled the code very closely on another bar chart within my app that DOES work. The only real difference is the driving data and the orientation of the bars.

Here's the code, followed by the console readout:

-(void) displayDayChart
{

    NSTimeInterval timeFrame = -86400; // 24 hours for this test, selectable in real life
    NSDate *dayStart = [NSDate dateWithTimeIntervalSinceNow:timeFrame];
    NSDate *ratNow = [NSDate date];
    int y = (self.chartView.frame.size.height) - 5;

    NSLog(@"DayStart is %@",dayStart);

    // Data source

    // Predicate to retrieve activities that stopped after the beginning of the timeframe, plus the currently running one

    actPredicate = [NSPredicate predicateWithFormat:@"(startTime >= %@ AND stopTime <= %@) OR ((startTime <= %@ AND stopTime >= %@) OR (startTime <= %@ AND stopTime == NULL))",dayStart,ratNow,dayStart,dayStart,ratNow];

    // Fetch the activities
    NSFetchedResultsController *dayActivityFRC = [TimedActivity MR_fetchAllSortedBy:@"startTime" ascending:NO withPredicate:actPredicate groupBy:nil delegate:nil];


//    int taCount = [TimedActivity MR_countOfEntities];
//    NSLog(@"total of TimedActivities  is %d",taCount);

    int grabbedActivities = dayActivityFRC.fetchedObjects.count;
    NSLog(@"total of grabbedActivities  is %d",grabbedActivities);
    NSLog(@"Number of TimedActivities in dayActivityFRC is %d",dayActivityFRC.fetchedObjects.count);


    DayChartBar *thisBar;

    TimedActivity *thisItem;

    for(int i=0; i<(dayActivityFRC.fetchedObjects.count); i++)
    {
        thisBar = [[DayChartBar alloc] initWithFrame:CGRectZero];
        thisBar.tag = i+1;
        [self.chartView addSubview:thisBar];
        thisItem = [dayActivityFRC.fetchedObjects objectAtIndex:i];
        // Increment vertical location of bar

        y = y - 20;

        // Calculate top (currently timing) activity's duration

        if (thisItem.stopTime == nil)
        {

            NSNumber *n = ([NSNumber numberWithDouble: abs([thisItem.startTime timeIntervalSinceDate:[NSDate date]])]);

            thisItem.duration = n;

        }

        // Calculate bottom activity's duration

        if (thisItem.startTime < dayStart && thisItem.stopTime > dayStart)
        {
            NSNumber *n = ([NSNumber numberWithDouble: abs([thisItem.stopTime timeIntervalSinceDate:dayStart])]);

            thisItem.duration = n;

//            NSLog(@"%@ is the bottom item",thisItem.name);

        }

        // Calculate middle activities' duration

        if (thisItem.startTime > dayStart && thisItem.stopTime < ratNow)
        {

            NSNumber *n = ([NSNumber numberWithDouble: abs([thisItem.stopTime timeIntervalSinceDate:thisItem.startTime])]);

            thisItem.duration = n;

        }


        NSLog(@"Check loop %@",thisItem.name);
        NSLog(@"startTime is %@",thisItem.startTime);
        NSLog(@"stopTime is %@",thisItem.stopTime);
        NSLog(@"duration is %@\n",thisItem.duration);



        // Width of view = 280

        // Calculate width of bar (proportionate to length of activity vs timeframe, relative to available space in chart view)

        int w = ((280) * ([thisItem.duration doubleValue])) / (abs(timeFrame));


        NSLog(@"w = %d",w);


        // Make an animated bar of the appropriate color and size

        [UIView animateWithDuration:.3
                              delay:.2
                            options: UIViewAnimationCurveEaseOut // Deprecated, but still works
                         animations:^
         {
             // Starting state

             thisBar.frame = CGRectMake(20, y, 0, 15);
             thisBar.backgroundColor = [UIColor blackColor];

             // End state

             thisBar.frame = CGRectMake(20, y, w, 15);
             NSLog(@"thisBar.frame is %@",NSStringFromCGRect (thisBar.frame));

             thisBar.backgroundColor = thisItem.color;

             thisBar.layer.shadowColor = [[UIColor blackColor] CGColor];
             thisBar.layer.shadowOpacity = 0.7;
             thisBar.layer.shadowRadius = 4.0;
             thisBar.layer.shadowOffset = CGSizeMake(5.0f, 5.0f);
             thisBar.layer.shadowPath = [UIBezierPath bezierPathWithRect:thisBar.bounds].CGPath;

             NSLog(@"Bar created!");
         }
                         completion:^(BOOL finished)
         {
             // Reserved for creating a name label at the end of each bar

             NSLog(@"Bar completed!");
         }];


    }
}

And the Console readout:

2014-06-20 11:05:17.910 WMDGx[46607:a0b] Number of activities is 2
2014-06-20 11:05:22.793 WMDGx[46607:a0b] DayStart is 2014-06-19 18:05:22 +0000
2014-06-20 11:05:22.794 WMDGx[46607:a0b] total of grabbedActivities  is 8
2014-06-20 11:05:22.795 WMDGx[46607:a0b] Number of TimedActivities in dayActivityFRC is 8
2014-06-20 11:05:22.796 WMDGx[46607:a0b] Check loop Dusting
2014-06-20 11:05:22.796 WMDGx[46607:a0b] startTime is 2014-06-20 17:29:04 +0000
2014-06-20 11:05:22.797 WMDGx[46607:a0b] stopTime is (null)
2014-06-20 11:05:22.797 WMDGx[46607:a0b] duration is 0
2014-06-20 11:05:22.798 WMDGx[46607:a0b] w = 0
2014-06-20 11:05:22.798 WMDGx[46607:a0b] thisBar.frame is {{20, 261}, {0, 15}}
2014-06-20 11:05:22.799 WMDGx[46607:a0b] Bar created!
2014-06-20 11:05:22.800 WMDGx[46607:a0b] Check loop Test 1
2014-06-20 11:05:22.800 WMDGx[46607:a0b] startTime is 2014-06-20 17:19:22 +0000
2014-06-20 11:05:22.801 WMDGx[46607:a0b] stopTime is 2014-06-20 17:29:04 +0000
2014-06-20 11:05:22.801 WMDGx[46607:a0b] duration is 581
2014-06-20 11:05:22.802 WMDGx[46607:a0b] w = 1
2014-06-20 11:05:22.803 WMDGx[46607:a0b] thisBar.frame is {{20, 241}, {1, 15}}
2014-06-20 11:05:22.803 WMDGx[46607:a0b] Bar created!
2014-06-20 11:05:22.804 WMDGx[46607:a0b] Check loop Dusting
2014-06-20 11:05:22.804 WMDGx[46607:a0b] startTime is 2014-06-20 16:52:10 +0000
2014-06-20 11:05:22.805 WMDGx[46607:a0b] stopTime is 2014-06-20 17:19:22 +0000
2014-06-20 11:05:22.805 WMDGx[46607:a0b] duration is 1632
2014-06-20 11:05:22.806 WMDGx[46607:a0b] w = 5
2014-06-20 11:05:22.808 WMDGx[46607:a0b] thisBar.frame is {{20, 221}, {5, 15}}
2014-06-20 11:05:22.809 WMDGx[46607:a0b] Bar created!
2014-06-20 11:05:22.810 WMDGx[46607:a0b] Check loop Test 1
2014-06-20 11:05:22.811 WMDGx[46607:a0b] startTime is 2014-06-20 16:52:04 +0000
2014-06-20 11:05:22.811 WMDGx[46607:a0b] stopTime is 2014-06-20 16:52:10 +0000
2014-06-20 11:05:22.812 WMDGx[46607:a0b] duration is 6
2014-06-20 11:05:22.812 WMDGx[46607:a0b] w = 0
2014-06-20 11:05:22.813 WMDGx[46607:a0b] thisBar.frame is {{20, 201}, {0, 15}}
2014-06-20 11:05:22.813 WMDGx[46607:a0b] Bar created!
2014-06-20 11:05:22.814 WMDGx[46607:a0b] Check loop Timer Sleeping
2014-06-20 11:05:22.814 WMDGx[46607:a0b] startTime is 2014-06-20 16:29:06 +0000
2014-06-20 11:05:22.816 WMDGx[46607:a0b] stopTime is 2014-06-20 16:52:04 +0000
2014-06-20 11:05:22.816 WMDGx[46607:a0b] duration is 1378
2014-06-20 11:05:22.817 WMDGx[46607:a0b] w = 4
2014-06-20 11:05:22.818 WMDGx[46607:a0b] thisBar.frame is {{20, 181}, {4, 15}}
2014-06-20 11:05:22.818 WMDGx[46607:a0b] Bar created!
2014-06-20 11:05:22.820 WMDGx[46607:a0b] Check loop Dusting
2014-06-20 11:05:22.821 WMDGx[46607:a0b] startTime is 2014-06-20 16:28:53 +0000
2014-06-20 11:05:22.821 WMDGx[46607:a0b] stopTime is 2014-06-20 16:29:06 +0000
2014-06-20 11:05:22.822 WMDGx[46607:a0b] duration is 12
2014-06-20 11:05:22.823 WMDGx[46607:a0b] w = 0
2014-06-20 11:05:22.824 WMDGx[46607:a0b] thisBar.frame is {{20, 161}, {0, 15}}
2014-06-20 11:05:22.825 WMDGx[46607:a0b] Bar created!
2014-06-20 11:05:22.826 WMDGx[46607:a0b] Check loop Test 1
2014-06-20 11:05:22.827 WMDGx[46607:a0b] startTime is 2014-06-20 16:28:38 +0000
2014-06-20 11:05:22.827 WMDGx[46607:a0b] stopTime is 2014-06-20 16:28:53 +0000
2014-06-20 11:05:22.827 WMDGx[46607:a0b] duration is 14
2014-06-20 11:05:22.828 WMDGx[46607:a0b] w = 0
2014-06-20 11:05:22.828 WMDGx[46607:a0b] thisBar.frame is {{20, 141}, {0, 15}}
2014-06-20 11:05:22.829 WMDGx[46607:a0b] Bar created!
2014-06-20 11:05:22.829 WMDGx[46607:a0b] Check loop Timer Sleeping
2014-06-20 11:05:22.829 WMDGx[46607:a0b] startTime is 2014-06-20 16:25:35 +0000
2014-06-20 11:05:22.830 WMDGx[46607:a0b] stopTime is 2014-06-20 16:28:38 +0000
2014-06-20 11:05:22.831 WMDGx[46607:a0b] duration is 183
2014-06-20 11:05:22.832 WMDGx[46607:a0b] w = 0
2014-06-20 11:05:22.834 WMDGx[46607:a0b] thisBar.frame is {{20, 121}, {0, 15}}
2014-06-20 11:05:22.834 WMDGx[46607:a0b] Bar created!
2014-06-20 11:05:23.348 WMDGx[46607:a0b] Bar completed!
2014-06-20 11:05:23.350 WMDGx[46607:a0b] Bar completed!
2014-06-20 11:05:23.351 WMDGx[46607:a0b] Bar completed!
2014-06-20 11:05:23.352 WMDGx[46607:a0b] Bar completed!
2014-06-20 11:05:23.352 WMDGx[46607:a0b] Bar completed!
2014-06-20 11:05:23.353 WMDGx[46607:a0b] Bar completed!
2014-06-20 11:05:23.354 WMDGx[46607:a0b] Bar completed!
2014-06-20 11:05:23.355 WMDGx[46607:a0b] Bar completed!

I hope this is just a dumbass case of not seeing the forest for the trees, but I've been beating my brains out for 3 days before bringing it to SO. I know it's not a proofreading site.

Here's a screenshot showing the drop shadows (and the absence of the bars themselves). The drop shadows appear to be in the right location and to be about the correct size based on the data:

enter image description here

**Edit*************

Following the comments by rdelmar and @gro below, I changed the line:

         thisBar.backgroundColor = thisItem.color;

to:

         thisBar.backgroundColor = [UIColor redColor];

Which produced this screenshot:

enter image description here

So, believing the problem to have been identified, I went back and changed this:

        thisBar = [[DayChartBar alloc] initWithFrame:CGRectZero];
        thisBar.tag = i+1;
        [self.chartView addSubview:thisBar];
        thisItem = [dayActivityFRC.fetchedObjects objectAtIndex:i];

to this

    thisBar = [[DayChartBar alloc] initWithFrame:CGRectZero];
    thisBar.tag = i+1;
    [self.chartView addSubview:thisBar];
    thisItem = [dayActivityFRC.fetchedObjects objectAtIndex:i];
    thisBar.endColor = thisItem.color;

and this (in the animation block):

         thisBar.backgroundColor = thisItem.color;

to this:

         thisBar.backgroundColor = thisBar.endColor;

And now the bars have disappeared once again.

**Second edit*****************

I've had to do quite a bit of backtracking, but now the bars are displaying, albeit a bit strangely. The problem (the primary one at least) was that the colors assigned to each item through it's associated category, which is then passed on to the TimedActivity, which are represented by the bars. Yeah, I know, kinda convoluted. I need to look into cleaning it up.

In any case, this is what it looks like now. As I said, the behavior is a little weird--for example, the top bar only has a drop shadow and no bar, but at least I'm seeing some colored bars. However, the drop shadows, inexplicably, are being created before the bars themselves. Or so it would appear.

enter image description here

rattletrap99
  • 1,469
  • 2
  • 17
  • 36
  • I made a simplified version of you code, by taking out all the data side stuff, and replacing it with an array to provide the widths of the bars. I also made the bars UIViews instead of DayChartBar. So the drawing code was all the same, and the bars appeared correctly. So the problem seems to be with the data driving the bars, but I haven't spotted it yet. – rdelmar Jun 20 '14 at 19:05
  • Thanks for looking! I should have mentioned that I spotted an anomaly in the reporting of the durations earlier on, but believed I had fixed that by using the predicate as you see it. The previous predicate was simpler, but appeared somehow to introduce a problem. The current predicate has seemed to work correctly for quite a while in the code I modeled this method on. – rattletrap99 Jun 20 '14 at 19:11
  • Incidentally, I'm sure you saw the readouts of the duration data in the console, yes? – rattletrap99 Jun 20 '14 at 19:13
  • is this what you wanted here? thisBar.backgroundColor = thisItem.color; – gro Jun 20 '14 at 19:14
  • Yes. thisItem has a .color property, from the associated category. – rattletrap99 Jun 20 '14 at 19:19
  • Please see edit above! – rattletrap99 Jun 20 '14 at 22:15
  • What do thisItem.color and thisItem.endColor return? Are you getting valid colors from either of those? – rdelmar Jun 21 '14 at 00:19
  • No, I'm getting (null). I've been working trying to figure out why, and I think it has to do with extracting this information from core data, where the colors are stored as a transformable attribute. I went so far as to create a new attribute directly on the TimedActivity entity. Should have done this before, but...In any case, things still aren't working correctly. – rattletrap99 Jun 21 '14 at 00:53
  • Please see second edit above for new info! – rattletrap99 Jun 21 '14 at 01:38
  • I don't know what's wrong with your data from core data, but the problem with the shadows drawing full scale initially, is because the path of a layer is not implicitly animatable. You can fix that by just eliminating the line, thisBar.layer.shadowPath = ... – rdelmar Jun 21 '14 at 03:59
  • Indeed it did fix the shadow! Thanks! I've got the core data issue sorted, and now that I've got bars of some description, I'm confident I can work the rest of it out, despite rampant randomness which I'm sure has to do with my calculations. If you'd like to consolidate your very helpful suggestions into an answer, I'll be pleased to check it and up vote. Thanks again for your time and patience! – rattletrap99 Jun 21 '14 at 05:08

1 Answers1

0

I thought I'd wrap this up, since no one has contributed anything for 24 hours, and the problem as stated has been resolved.

The color of each bar was intended to be set by a sort of round-about Core Data scheme of relationships. Once I straightened that out, the colored bars appeared as expected.

The other (unstated) problem is that there is some apparent randomness to the lengths of the bars relative to the available space. At this point I view it as an arithmetic issue.

Thanks to @rdelmar and @gro, both of whose comments pointed me to the Core Data problem.

rattletrap99
  • 1,469
  • 2
  • 17
  • 36