Tuesday, October 8, 2013

Android Tutorial: Saving Key-Value Sets

Get a Handle to a SharedPreferences

You can create a new shared preference file or access an existing one by calling one of two methods:
  • getSharedPreferences() — Use this if you need multiple shared preference files identified by name, which you specify with the first parameter. You can call this from any Context in your app.
  • getPreferences() — Use this from an Activity if you need to use only one shared preference file for the activity. Because this retrieves a default shared preference file that belongs to the activity, you don't need to supply a name.

These two methods are very close to each other. But certain differences can be found:
  1. One Activity vs Multiple Activity. getPreferences() is used for one activity only, the information stored here will not be available for other activities. While, getSharedPreferences() can be used for different activities. It's really shared information for every activity who knows the name of the shared preference file.
  2. getSharedPreferences() could have information saved separated in multiple files, while  getPreferences() only has information available for the current activity in which it's called, it's an one file.

Tuesday, October 1, 2013

Grey Out UI with Animating Indicator

One of the function which most of code needs is blocking user out of any interaction when the app needs serious being alone for a few seconds. This function could be found in a lot of published iOS Application, especially when App needs to refresh, temporary short time download/upload, server synchronization, etc. At these moments, the developer would not expect user touch anything to break the app down until it's finished. So, here comes this demo to show you how to make a screen blocker with an spinning indicator.

Several Prerequisites needs to be confirmed: 

We have two components:

  • Blocker, color: grey. (Name: overlayView, Class: UIView)
  • Indicator, color: white. (Name: indicatorView, Class: UIActivityIndicatorView)
1st, What Area Needs to Be Blocked.
        What area should block covered. For this demo, we will use the whole screen as an example, which not just it's very simple, also it's very common to be used. 

    CGRect rect = [UIScreen mainScreen].bounds;
    rect.size.height = MAX(rect.size.height, rect.size.width);
    rect.size.width = MAX(rect.size.height, rect.size.width);
    self.overlayView = [[UIView alloc] initWithFrame:rect];

2nd, What is the Transparency this Blocker Will Be
        Using Transparency could help user be aware of the status of theirs: "Being blocked out, while it won't be long". This could make user feel less confusing about what the app is doing, or what's going on with the app, the user will be more patient if appropriate transparency is used. (Some programmer use real-time updated progress information to make user less confusing and more patient) Here, I'll use 50% transparency to cover our block area.

    self.overlayView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];

3rd, Starts the Animation of Indicator.
So, the code looks like:

- (void)startSpinning
    if (self.indicatorView) {
        CGRect rect = self.indicatorView.superview.bounds;
        self.indicatorView.center = CGPointMake(rect.size.width / 2, rect.size.height / 2);
        [self.indicatorView startAnimating];

- (void)stopSpinning
    if (self.indicatorView) {
        [self.indicatorView stopAnimating];

- (void)drawView
    CGRect rect = [UIScreen mainScreen].bounds;
    rect.size.height = MAX(rect.size.height, rect.size.width);
    rect.size.width = MAX(rect.size.height, rect.size.width);
    self.overlayView = [[UIView alloc] initWithFrame:rect];
    self.overlayView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
    self.indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
    [self.view addSubview:self.overlayView];
    [self.view addSubview:self.indicatorView];
    [self startSpinning];

- (void)removeView
    [self stopSpinning];
    [self.overlayView removeFromSuperview];
    self.indicatorView = nil;

We could start blocking by calling drawView, while stop it by calling removeView.

Here, one minor thing needs your attention, the center position of indicatorView.
Usually people believe they could use self.view.center as their new center position, no matter if screen's orientation is portrait or landscape. But it's a wrong assumption I have to say. My Testing data shows on iPad 6.0 simulator:  
Portrait: self.view Center (384.000 502.000), Bounds (768.000 1004.000)
Landscape: self.view Center (394.000 512.000), Bounds (1024.000 748.000)
Which is not exactly what we presume. So we need to calculate the center of screen every time before starting animation of indicator. This will be very useful when the app allows user switch orientation by rotating the iOS device around. So, every time the user rotate the screen when the indicator is still animating, the code needs to stop animation first, then recalculate the position of new screen center, starting the animation again at last.

Here is the code.
// This function is called when rotation is finished.
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
    NSLog(@"To (%.3f %.3f)", self.view.center.x, self.view.center.y);
    NSLog(@"To Bounds (%.3f %.3f)", self.view.bounds.size.width, self.view.bounds.size.height);
    [self startSpinning];

// This function is called when rotation is not started yet.
-(void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { NSLog(@"From (%.3f %.3f)", self.view.center.x, self.view.center.y); NSLog(@"From Bounds (%.3f %.3f)", self.view.bounds.size.width, self.view.bounds.size.height); [self stopSpinning]; }


Implementing Block with animating indicator could be:

  1. Init the Block View;
  2. Add Block View to the Should-Be-Covered Area.
  3. Init the Indicator;
  4. Add Indicator to the  Should-Be-Covered Area.
  5. Start Indicator.
  6. Stop Indicator if Task finished.
Key Points:
  • Take the Right Center Position for Indicator from Covered Area.
  • Recalculate the Center Position every time the Area dimension changed.
  • Re-animating the Indicator if it's center position is changed.

The clonable Git rep can be found here.