0
votes

I have a UIView that acts like a schedule (iPad app written using XCode 4.6, iOS 6.2 and Storyboards). I actually have two (2) UIVIews, one in the top half of the window, the other in the bottom half. There is a calendar in the top half; and a grid of times below that. When the user taps on a day that has appointments, they are drawn on the bottom UIView. The user can tap any day with appointments, and the old is removed, the new data is drawn on the bottom view.

This works great, unless there are no appointments for that day that was tapped, in which case the old data remains, rather than being somehow deleted. I have tried [self setNeedsDisplay], but since nothing has changed, nothing is drawn. Is there a way to invalidate the contents of the UIView to force it to be redrawn with no data?

UPDATE: here is the code that draws the appointments:

- (void)drawRect:(CGRect)rect  {

//  load dictionary files from plists
NSString *path = [[NSBundle mainBundle] bundlePath];
//    NSLog(@"\n\npath %@",path);
//
NSString *timesFinalPath = [path stringByAppendingPathComponent:@"startingTimes.plist"];  //  do starting times
NSDictionary *startDict = [NSDictionary dictionaryWithContentsOfFile:timesFinalPath];

NSString *namesFinalPath = [path stringByAppendingPathComponent:@"techNames.plist"];  //  do tech names
NSDictionary *techDict = [NSDictionary dictionaryWithContentsOfFile:namesFinalPath];
//    NSLog(@"\nPath: %@\ntechDict.count: %d\nstartDict.count %d", path, techDict.count, startDict.count);

//    NSLog(@"nbr of recognizers: %lu", (unsigned long)self.gestureRecognizers.count);


//  find out how many appointments we have for this day
SingletonAppointments *sharedInstance = [SingletonAppointments sharedInstance];  //  initialize

if(sharedInstance.globalApptList.count > 0) {

    for(int i = 0; i < sharedInstance.globalApptList.count; i++)  {  //  get customer apointments for this day

        AppointmentInfo *apptObject = [sharedInstance.globalApptList objectAtIndex:i];

        //NSLog(@"apptObject.aApptKey: %@", apptObject.aApptKey);

        //  using apptObject.aApptKey, get the client's name
        NSPredicate *predicate =  ([NSPredicate predicateWithFormat:@"(aClientKey = %@)", apptObject.aApptKey]);
        clientInfo = [ClientInfo MR_findAllWithPredicate: predicate];
        NSString *cliInfo;
        for (ClientInfo *cli in clientInfo) {
            cliInfo = cli.aClientName;  //  found client name
        }

        //  settings for name drawing
        const float nameFontSize = 14;
        UIFont *hourfont=[UIFont systemFontOfSize: nameFontSize];

        //  now compute the duration
        NSDateFormatter *df = [[NSDateFormatter alloc] init];
        [df setDateFormat:@"HH:mm"];
        NSTimeInterval interval = [apptObject.aEndTime timeIntervalSinceDate:apptObject.aStartTime];
        int hours = (int)interval / 3600;             // integer division to get the hours part
        int minutes = (interval - (hours*3600)) / 60; // interval minus hours part (in seconds) divided by 60 yields minutes
        //            NSString *duration = [NSString stringWithFormat:@"%d:%02d", hours, minutes];

        //  localize time
        NSDate *sTime = [self localizeDate: apptObject.aStartTime];
        //            NSDate *eTime = [self localizeDate: apptObject.aEndTime];
        //            NSLog(@"\n\nsTime: %@    eTime: %@", sTime, eTime);
        //            NSLog(@"\nlocalized sTime: %@\nlocalized eTime %@", sTime, eTime);

        //  determine time slot  (dictiionary starts at 0900... must accomodate for that)
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]];  //Optionally for time zone converstions
        [formatter setDateFormat:@"HH:mm"];

        //  get time shop opens and closes
        PreferenceData *prefDataFound = [PreferenceData MR_findFirst];
        if(prefDataFound)  {  //  fill the times
            startHour = prefDataFound.aShopOpens;
            endHour = prefDataFound.aShopCloses;
        }
        //            NSLog(@"startHour: %@    endHour: %@",startHour, endHour);

        //  now, get first time slot in dictionary
        NSArray *ts = [startDict allKeysForObject:[NSNumber numberWithInt:5]];  //  comes back with one entry: 9:00

        //  clean it (remove colon)
        NSString *dictValue = ts[0];
        NSString *cleanDictValue = [dictValue stringByReplacingOccurrencesOfString:@":" withString:@""];

        //  convert to int
        int dictStartTime = [cleanDictValue intValue];
        int intStartHour = [startHour intValue];

        //  compute gridTimeOffset
        float gridTimeOffset = dictStartTime - intStartHour;

        NSString *stringFromDate = [formatter stringFromDate:sTime];
        //            NSLog(@"\n\nstringFromDate: %@",stringFromDate);
        //            NSLog(@"\nserviceTech: %@", apptObject.aServiceTech);

        //  find the correct column by tech name
        NSNumber *techColumn = [techDict objectForKey:apptObject.aServiceTech];
        //            NSLog(@"\nserviceTech: %@, techColumn: %@", apptObject.aServiceTech, techColumn);

        //  set the context
        CGContextRef context = UIGraphicsGetCurrentContext();


        //  draw the name...  (x refers to column; y refers to time slot)
        [[UIColor blueColor] set];  //  sets color of customer name
        NSNumber *timeSlot = [startDict objectForKey:stringFromDate];
        float correctedSlot = [timeSlot floatValue] + gridTimeOffset;  //  corrected slot info for name and rectangle
        [cliInfo drawAtPoint:CGPointMake([techColumn floatValue], correctedSlot) withFont:hourfont];
        //            NSLog(@"\ntimeSlot: %@    adjusted: %@", [timeSlot floatValue], ([timeSlot floatValue] - gridTimeOffset));


        //  make some settings for the blocking
        UIGraphicsBeginImageContext(self.bounds.size);
        //            CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
        //            CGContextSetLineWidth(context, 1.0);

        //  get the CGContextRef for drawing the rectangle
        CGContextSetFillColorWithColor(context, [[[UIColor grayColor] colorWithAlphaComponent: 0.3] CGColor]);

        //            NSNumber *startPos = [startDict objectForKey:stringFromDate];
        int shadeHeight = ((hours * 4) + (minutes / 15)) * 25;  //  compute starting y value

        //            CGContextFillRect(context, CGRectMake([techColumn floatValue], [startPos floatValue], 218, (float)shadeHeight));
        CGContextFillRect(context, CGRectMake([techColumn floatValue], correctedSlot, 218, (float)shadeHeight));

        UIGraphicsEndImageContext();  //  end the context so the other appointments will draw
    }

}

}

1
Could you show the code that populates the bottom view. - Mike D
The fact that nothing has changed should not, in and of itself, prevent the view from being redrawn when you call setNeedsDisplay. @MikeD is right. We need to see some code to figure out what's really going wrong. - Aaron Golden

1 Answers

1
votes

This should be handled in the callback of the view controller of that controls the bottom view. (If you have one view controller that controls both top and bottom, the same applies.) In its update method (which should be called when a day is selected, including empty days) there should be something like this.

-(void)update {
    if (selectedDay.events && selectedDay.events.count) {
       // do your normal update
    }
    else {
       // empty the view
    }
}

Note that if the bottom view is or has a table view, you reset the data array or set (above referenced as selectedDay.events) and call

[_tableView reloadData]; 

If you have some other scheme you will have to empty it "manually".

EDIT

As spokane-dud pointed out, there was a flaw with the data source singleton. The data has to be explicitly set to nothing when appropriate.

if(appointmentInfo.count > 0) { 
    for (AppointmentInfo *appt in appointmentInfo) { 
      sharedInstance.globalApptList = appointmentInfo; // move data to sharedInstance 
    } 
} 
else { 
    sharedInstance.globalApptList = nil; 
}