A neat way to get slides and notes loaded in control pad

Today I’ve been hacking on the slide show control pad, which leave me to the following problem: How to load slideshow previews and corresponding lecturer’s notes asynchronously (note that slide previews and notes won’t be sent in a predictable&reliable way and users might be switching between slides at any time)? Also, how can I handle the same problem when it comes to the grid view ? Like, I want to add a swipe-in side bar on the right so that whenever users swipe from right to left, a sidebar will show (like the Facebook navigation sidebar) and a table of slides will be shown. Users will be then able to click on one of the slides to “goto # slide”.

So here is what I want to do: find a way to store UIViews which are waiting for desired contents from server AND update desired contents so that the Views always loads the last requested content.

A load buffer?

That’s the first thing came to my mind. As users navigate through different slides, the load buffer will store their “need” whenever their desired content is not ready yet.

@property (nonatomic, strong) NSMutableDictionary* loadBuffer;

What should we store here? At first I thought of key-value pair like <UIView*, slideshowIndex>, so that whenever the content at slideshowIndex arrives, we can reload the content to the UIView*. Also, using UIView* as a key allows us to update the desired content index when users switch to another slide.

However, there is a problem: NSMutableDictionary’s method

- (void)setObject:(id)anObject forKey:(id < NSCopying >)aKey

Need to make a copy for the key, ….but wait, we are copying a pointer here! Not only the UIView pointed to can be victim of various dealloc reasons to become a nil pointer and the copy won’t get updated, but it can also be reallocated to whatever object, setting some content to a random pointer will definitely be disastrous…

So what can we do now?

UIView’s tag property

tag
_ An integer that you can use to identify view objects in your application.
_ @property(nonatomic) NSInteger tag

Discussion
_ The default value is 0. You can set the value of this tag and use that value to identify the view later.

Availability
_ Available in iOS 2.0 and later.

See Also
– viewWithTag:

We need a way to identify a view, not the UIView object actually instantiated… so how can we do that without messing everything with a copy of pointer to the view? Here is one of a few places where we can make use of UIView’s tag property without abusing it ( actually pointed out that in many cases, people are using it the wrong way, for more explanation, read here).

Our loadBuffer become thus a dictionary taking <tag, slideShowIndex>. Here is the code that I used to handle slideshow reload:


- (SlideShow *) init{
    self = [super init];
    self.imagesDictionary = [[NSMutableDictionary alloc] init];
    self.notesDictionary = [[NSMutableDictionary alloc] init];
    self.loadBuffer = [[NSMutableDictionary alloc] init];
    _size = 0;
    _currentSlide = 0;

    backgroundQueue = dispatch_queue_create("org.libreoffice.iosremote.bgqueue", NULL);
    NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];

    /**
     This observer waits for storage updates like new image added or notes received.
     It then checks in the loadBuffer to see if there is a view waiting for this update in loadBuffer, if yes, it loads it up and remove the waiting entry.
     loadBuffer stores key-value pair with viewTag as a key and slideIndex as value.
     For the same view, we only keep the last requested slide index on the waiting list.
     It is thus indispensable to identify each view with an unique tag in its view controller. Here we use 0-10 to indentify central vc views and 11-N for swipe-in tableViewController which allows direct slide number change.
     We handle lecturer's notes at the same time as an entry in the load buffer via an instrospection.
     */
    self.slideShowImageReadyObserver =[[NSNotificationCenter defaultCenter]
                                              addObserverForName:@"storage_update_ready"
                                                          object:nil
                                                           queue:mainQueue
                                                      usingBlock:^(NSNotification *note) {
                                                          if ([[self.loadBuffer allKeysForObject:[NSNumber numberWithInt:[[[note userInfo] objectForKey:@"index"] intValue]]] count]) {
                                                              NSArray * tagArray = [self.loadBuffer allKeysForObject:[NSNumber numberWithInt:[[[note userInfo] objectForKey:@"index"] intValue]]];
                                                              for (NSNumber *tag in tagArray) {
                                                                  UIView * view = [[self.delegate view] viewWithTag:[tag integerValue]];
                                                                  if ([view isKindOfClass:[UIImageView class]]){
                                                                      UIImage *image = [self.imagesDictionary objectForKey:[self.loadBuffer objectForKey:tag]];
                                                                      if (image) {
                                                                          [(UIImageView *)view setImage:image];
                                                                          [self.loadBuffer removeObjectForKey:tag];
                                                                      }
                                                                  }
                                                                  else if ([view isKindOfClass:[UIWebView class]]){
                                                                      NSLog(@"Async notes");
                                                                      NSString *note = [self.notesDictionary objectForKey:[self.loadBuffer objectForKey:tag]];
                                                                      if (note) {
                                                                          [(UIWebView *)view loadHTMLString:note baseURL:nil];
                                                                          [self.loadBuffer removeObjectForKey:tag];
                                                                      }
                                                                  }

                                                              }
                                                          }
                                                      }];
    return self;
}

An observer which waits for updates from server. Once we received something (an image for slideshow preview or a html string representing lecturer’s notes), it retrieves all associated view tags whose desired contents is on the newly received slide. Note that NSMutableDictionary has a method “allKeysForObject” which returns all tags associated with a slideShowIndex. That’s what we want here since we want to load all views waiting for this update.

Finally, we need to assign each view to an unique tag and we are done.

#define CURRENT_SLIDE_IMAGEVIEW 1
#define NEXT_SLIDE_IMAGEVIEW 2
#define CURRENT_SLIDE_NOTES 3
// Unique tag assignment. Don't use 0 as it's default. 0-10 for central VC
[self.slideView setTag:CURRENT_SLIDE_IMAGEVIEW];
[self.secondarySlideView setTag:NEXT_SLIDE_IMAGEVIEW];
[self.lecturer_notes setTag:CURRENT_SLIDE_NOTES];

Let me know if there is any better method to handle this!

That’s it for now,
Cheers

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s