Thursday, December 27, 2012

Sencha Touch List Paging with Memory Store

Hello,

Recently I was working with Sencha Touch, Phonegap application. Where we have sqlite database as a local storage and we have number of lists where we displayed records from different tables. In some tables we were having more than 300 records and for the better performance and user experience we want to have paging in list.

Sencha list does support paging with list paging plugin. It's like you have to configure your list store with page size. So when you reach at end of the list store will be loaded again and it will again call url mentioned in your proxy and pass page number as param. But here in our case we have local database. So we were not having any ajax proxy where we can send page params and get next page data. Instead we have to write a db logic to get certain range of records. How to do that. First we extended list and added currentPage as config in it and it's initialized with 1.

Now the problem is we have to detect that user has scrolled till the end of the list so we can load next page data in list along with other data. This is the same logic used by list paging plugin. Check the following code.


'#listItemId':{
         initialize: function(list){
                var scroller = list.getScrollable().getScroller();
                scroller.on({
                        scrollend: function(scroller, x, y){
                                if (y >= scroller.maxPosition.y) {
                                       //scrolled till end.
                                       //logic to get next records from database.
                                }
                        },
                       scope: this
               });
        }
}

Above code you need to add in your contoller's this.control method. The logic is quite simple. We added scroll end listener for list scrollbar and checked it's postion. If scroller's y coordinate matches scrollbar's max postion, that means we are at end. So here we have to write our logic to get next set of data from database. 

Hope this helps you. 

Monday, December 10, 2012

Magento - Display Custom Option Quantity in Manage Product Grid

Hello,

I am back to blogging after long time. Past few months were really busy for me so did not get enough time for blogging.  So recently I was working on magento project, where we have used MageWrox Advanced Custom Option extension to have quantity and SKU for each custom option.

When you go to Catalog --> Manage Products, in grid only total qty is displayed. Our requirement was to display custom options and it's qty there. So how to do that? Here is the trick.

Copy following file


app/code/core/Mage/Adminhtml/Block/Catalog/Product/Grid.php

Now create following folder structure in side app/code/local 

Mage -->
          Adminhtml -->
                              Block -->
                                         Catalog -->
                                                       Product

And put grid.php file there. Now open grid.php file and find following code.

if (Mage::helper('catalog')->isModuleEnabled('Mage_CatalogInventory')) {
            $this->addColumn('qty',
                array(
                    'header'=> Mage::helper('catalog')->__('Qty'),
                    'width' => '100px',
                    'type'  => 'number',
                    'index' => 'qty',
            ));
        }

This is the qty column which displays total inventory quantity. We don't want this so remove it and put following code instead of it.

$this->addColumn('customqty',
            array(
                'header'=> Mage::helper('catalog')->__('Qty'),
                'width' => '200px',
                'index' => 'entity_id',
            'type' => 'text',
            'renderer' =>  'Mage_Adminhtml_Block_Catalog_Product_Renderer_CO'
        ));

Here we will use renderer to format the data we have to display. Now created Renderer folder in 

app/code/local/Mage/Adminhtml/Block/Catalog/Product/

and create co.php file there and add following code there.


class Mage_Adminhtml_Block_Catalog_Product_Renderer_CO extends Mage_Adminhtml_Block_Widget_Grid_Column_Renderer_Abstract
{
public function render(Varien_Object $row)
{
$value =  $row->getData($this->getColumn()->getIndex());
$product = Mage::getModel('catalog/product');
$product->load($value);
$customOptionHTML = '';
foreach ($product->getOptions() as $opt) {
$values = $opt->getValues();
foreach($values as $v){
$customOptionHTML.=$v['default_title'].' : '.$v['customoptions_qty'].'
';
}
}
return ''.$customOptionHTML.'';
}
}

?>
Here we are using product id to get product model and it's custom options. foreach loop is used to iterate through custom options and get custom option title and qty. Here you can make any change in format if you want. 

Please note that this code is tested in magento community version 1.7.0.2








Friday, October 12, 2012

How to add Tap Event to UIImageView in XCode?

Hello,

This blog post is about adding tap event to UIImageView dynamically.

Here we can use UITapGestureRecognizer class. Checkput the following code.


UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleImageTap:)];
        tap.cancelsTouchesInView = YES;
        tap.numberOfTapsRequired = 1;

Above code is to identify single tap on image. If you want to do it for double tap just increase the count by one in numberOfTapsRequired.

Now let's create our image view.

NSString *urlString = @"http://myurl.com/image.jpg";
NSURL *url = [NSURL URLWithString:urlString];
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData];
UIView* mainServiceView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
UIImageView* imageView = [[UIImageView alloc] initWithImage:image];
imageView.userInteractionEnabled =TRUE;

Here note the last line imageView.userInteractionEnabled =TRUE; This is necessary else it will not respond to any gesture. Now add a gesture to image

[imagView addGestureRecognizer:tap];

Also add handleImageTap function to your view controller file.

- (void) handleImageTap:(UIGestureRecognizer *)gestureRecognizer {
    UIView* view = gestureRecognizer.view;
    //object of view which invoked this 
}



That's it now user can tap on your image.

Hope this helps you.





XCode- Send data and Close Popover from Master View Controller

Hello,

Recently I was working on the app, where there was a button in toolbar which opens the Pop over with a table view. After selecting table cell, we need to pass some information to master view controller and close the Popover.

First you need a reference in your master view controller. Add following to your View controller header file.


@property (nonatomic, strong) UIPopoverController *myPopOver;

Synthesize it in .m file.

@implementation masterViewController

@synthesize myPopOver; 
@end

Now add prepareForSegue method.

-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
   
    if([segue.identifier isEqualToString:@"popOverSegue"]){
        UIStoryboardPopoverSegue *popoverSegue = (UIStoryboardPopoverSegue *)segue;
        self.myPopOver = popoverSegue.popoverController;
        [segue.destinationViewController setDelegate:self];
    }
    
}


Also add one call call back method in your master view controller, which will be invoked when user selects a cell in pop over table view.

-(void)myCallBack{
}

Now in pop over table view controller import your master view controller and set delegate as follow.

#import
#import "masterViewController.h"

@interface myPopOverController : UITableViewController 
@property (nonatomic, weakmasterViewController* delegate;
@end

Add following code to .m file

#import "myPopOverController.h"
#import "masterViewController.h"

@interface  myPopOverController ()<UITableViewDelegate>

@end

@implementation iPadCategoriesPopOver
@synthesize categoriesArray,categoriesTableView,delegate;

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    
        [[self delegatemyCallBack];
    
}


@end
Check above code carefully, here we have delegate of type mater view controller and using it to invoke our call back function, Above code is executed when some one selects a row from table view. Now add following code to myCallBack function to close the pop over.

[self. myPopOver dismissPopoverAnimated:YES];

Also note that you can pass any parameter in callback if you want.

Hope this helps you.











Parse JSON data in Objective C (iOS 5)

Hello,

Recently I was working on native iPAD app where we were having certain APIs which returns JSON data. This blog is about parsing JSON data in Objective C.

Following is the code to send request.


NSString *serviceURI = @"http://myapiturl.com";
    serviceURI = [serviceURI stringByAppendingString:sort];
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    [request setURL:[NSURL URLWithString:serviceURI]];
    [request setHTTPMethod:@"GET"];
    NSString *contentType = [NSString stringWithFormat:@"application/json"];
    [request addValue:contentType forHTTPHeaderField: @"Content-Type"];
    [request addValue:@"application/json" forHTTPHeaderField: @"Accept"];

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
    dispatch_async(queue, ^{
        NSURLResponse *response = nil;
        NSError *error = nil;
        
        NSData *receivedData = [NSURLConnection sendSynchronousRequest:request
                                                     returningResponse:&response
                                                                 error:&error];
        if(receivedData==nil){
            [SerenaNotifications showNetworkNotifcations];
        }else{
        }
});

Please note that here we are using GCD (grand central dispatch) I will explain this in other blog post. We will get our JSON data in receivedData variable. iOS 5 gives native classes for JSON serialization. Following code will go in else loop.

NSError *myError = nil;
            NSDictionary *res = [NSJSONSerialization JSONObjectWithData:receivedData options:NSJSONReadingMutableLeaves error:&myError];
            NSArray *resultArray = [res objectForKey:@"results"];

Normally in JSON request, results are added with results key. In your case if key is different, replace it in place of results.

This will give you all the results. If you have a single value in key you can access it as follow.

NSString* value = [object valueForKey:@"key"];

If you want to convert it to integer value. Use following code.

NSInteger intValue = [[object valueForKey:@"value"] intValue];



If you want to convert it to boolean value, use following code.

bool boolValue = [[object valueForKey:@"value"] boolValue];

Now you have all the results in resultArray. How to iterate through it and get a single object? Check the following code.

 NSEnumerator *e = [resultArray objectEnumerator];
            
            NSDictionary *object;
            while (object = [e nextObject]) {
             }

Object is the NSDictionary object having your single result object. Again you can use objectForKey and valueForKey methods of NSDictionary class in case you have nested JSON structure.

Hope this post helps you.





iOS Pass Values to Native App from JavaScript in Webviews (Xcode)

Hello,

Recently I was working on a native iOS application for iPAD. There are several web views in app which loads some sencha touch forms in web views. Web views are in modal window. Requirement was to notify native app to close web views upon certain user actions.  So how to do that?  Here is the trick

Add following to your modal view controller header file.



#import

@interface iPadCatalogFormMoalController : UIViewController{
    IBOutlet UIWebView *webView;
}
@property (nonatomic, retain) UIWebView *webView;
@property (nonatomic) IBOutlet UIActivityIndicatorView  *webviewLoadingIndicator;
@end

Now synthesize this properties in .m file

@synthesize webView,webviewLoadingIndicator;

Following code should be added in viewDidLoad method.

    NSString *urlAddress = @"http://myappurl.com";
    
    NSURL *url = [NSURL URLWithString:urlAddress];
    
    NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];

    webView.delegate= self;
    [self.webView loadRequest:requestObj];

Now add few delegate functions for web views.

-(void)webViewDidStartLoad:(UIWebView *)webView{
    [webviewLoadingIndicator startAnimating];
}

-(void)webViewDidFinishLoad:(UIWebView *)webView{
    [webviewLoadingIndicator stopAnimating];
}

- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
    NSURL *URL = [request URL]; 
    if ([[URL scheme] isEqualToString:@"closeWebView"]) {
        // parse the rest of the URL object and execute functions
        [self dismissModalViewControllerAnimated: YES];
        return NO;
    } 
    return YES;
}

Most important function for is the third one. This is called when web view url is assigned. Here we can check url schemes and do the necessary action. 

Now in your JS code when you want to notify native app. Just add following code.

window.location.href = 'closeWebView://param=value';

Here when you add this it will again fire shouldStartLoadWithRequest delegate function of web view and there we are checking the the URL scheme. 

Also you can pass ay number of params to native app if needed. Please note that this isn't the most efficient method. But it works in major scenarios.

Hope this helps you.





Tuesday, September 25, 2012

Cordova (Phonegap) 2.0 In Android 4.1 Jelly Bean XMLHttpRequest Could not Load Error

Hello,

This is short and quick blog on cordova and android Jalley Bean issue. Have you ever encountered error "XMLHttpRequest Could not Load" while working with Android 4.1 Jalley Bean? Complete error is XMLHttpRequest Could not Load, origin null is not supported.

First when I get this error, I thought it's a cross domain issue, but it's not. When you are using cordova all your JavaScripts are loaded by file:/// protocol. So it really does not matter if we use Ajax or JsonP request. Because there is nothing like domain here. So what really is the issue?

While debugging for this error I noticed in my chrome developer tools that, it's trying to fire an XMLHttpRequest on my local machine and that XMLHttpRequest was initiated by some cordova functions. and that was using localhost url which does not exists. So it was breaking the whole app.

How to get rid of this? While debugging I found that this XMLHttpRequest was initiated by polling function of cordova. Cordova has polling mechanism to get response from native API calls. In my app, there wasn't any native API call so I disabled the polling. Find the following line in your cordova file.

UsePolling:true,

and set it to false as follow.

UsePolling:false,

That's it and there will not error on XMLHttpRequest.

Hope this helps you.

Thursday, September 13, 2012

Sencha Touch - Add callback function to animateActiveItem

Hello,

Recently I was working on a sencha touch project and faced an interesting issue. There was a requirement to adjust the layout after animation is completed. When we do animateActiveItem, you might not have noticed this but animateActiveItem is asynchronous. By default sencha touch has animation duration of 250ms. So  when you write following statement.

this.getMain().animateActiveItem(item,{ type: 'slide', direction: 'right'});
this.getMain().query('#element').hide();

You will notice that your element is hidden before animation is completed. You may increase the duration to see this.


this.getMain().animateActiveItem(item,{ type: 'slide', direction: 'right', duration:2000});
this.getMain().query('#element').hide();


So sometimes you might have annoying effects like, some of your UI jumps. To avoid this and have smooth transition, callback is necessary so UI changes can be adjusted after animation is completed. So how to do that? Here is the trick

Override animateActiveItem function of Ext.container


Ext.override(Ext.Container, {
    animateActiveItem: function(activeItem, animation, callback) {
        var layout = this.getLayout(),
            defaultAnimation;
     
        if (this.activeItemAnimation) {
            this.activeItemAnimation.destroy();
        }
        this.activeItemAnimation = animation = new Ext.fx.layout.Card(animation);
        if (animation && layout.isCard) {          
            animation.setLayout(layout);
            defaultAnimation = layout.getAnimation();          
            if (defaultAnimation) {
                defaultAnimation.disable();
                animation.on('animationend', function() {                  
                    defaultAnimation.enable();
                    animation.destroy();

                    if(callback){
                          callback();  
                    }
                }, this);
            }else{
                animation.on('animationend', function() {                  
                    animation.destroy();
                    if(callback){
                          callback();  
                    }
                }, this);
            }
        }
        return this.setActiveItem(activeItem);
    }
});

See the following code carefully.


if(callback){
     callback();  
}

I have added callback function as a parameter in method and that will be invoked when application end. I added this in both if and else part so it will be invoked if you have specified the animation or you are using default animation.

Now your animateActiveItem function call is changed. See the code below.

this.getMain().animateActiveItem(item,{ type: 'slide', direction: 'right', duration:2000},function(){
//your callback function code.
});

See the last parameter, that is your callback function.

Hope this posts helps you.

Saturday, September 1, 2012

Send HTTP Request and retrive HTTP response in iPhone, iPad Application

Hello,

Recently I was working on an iPhone application where requirement was to send HTTP request and retrieve the response. This was not a JSON or restful web service. Checkout the following code.


 NSMutableString* url = [NSMutableString string];
    [url appendString:@"http://www.example.com"];
    NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:url]
                                              cachePolicy:NSURLRequestUseProtocolCachePolicy
                                          timeoutInterval:60.0];
    NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
    if (theConnection) {
        
        
    } else {
        UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:@"Failed" message:@"Check your networking configuration." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alertView show];
    }

Above code will initiate the connection. If it's successful it will go in if part else it will go in else part. On successful connection we have to retrive the data so we will add few delegates functions for it.

- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response {
    NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
       if ([httpResponse statusCode] >= 400) {
        // do error handling here
           UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:@"Failed" message:@"Check your networking configuration." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
           [alertView show];
    } else {
        // start recieving data
    }
}

This function will be invoked when we have response. After we get response we will start fetching data. There is a separate delegate function for it.

-   (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)theData{
    NSString *response = [[NSString alloc] initWithData:theData encoding:NSUTF8StringEncoding];

Above function will be invoked when we will have real data. Here response will give us data in string format.

Hope this helps.

Wednesday, August 15, 2012

iOS, iPhone, iPad, XCode resize view on rotation

Hello,

This is quick blog on how to resize view in iPhone, iPad application. Normally when we use story board all the views we add are of portrait mode and if your app supports rotation, in landscape mode you need to resize your views. So how to do that.

Go to your ViewController file and find the following function.


- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation

Normally function will look as following.

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

That means it will only support Portrait orientation. So to allow rotation in your app change this function as follow.

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return YES;
}

Now it will allow rotation in application. Now add one more function to your view controller file

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{
    if (fromInterfaceOrientation == UIInterfaceOrientationPortrait) {
           //
    }
    else{
    }
}

This function will be invoked automatically when orientation of phone changes. fromInterfaceOrientation will give you the last orientation. That means if you are in porttrait mode and you are now changing to landscape mode, value of fromInterfaceOrientation would be UIInterfaceOrientationPortrait. So in above function you need to reize your view in if else loop.

Hope this helps you.




Friday, August 10, 2012

Sencha Touch Get Yahoo News RSS using JsonP request

Hello,

Recently I was working on project where requirement was to display rss feeds from yahoo news in sencha touch application. For that we have to use JsonP request as it will be cross domain request. If we use simple Ajax request, we will get error of cross domain access and if we use JsonP request, we can not parse the response as normally RSS feed response is in XML format. So what is the solution?

Solution is to use YQL (Yahoo Query Language). YQL is the is the SQL like language that lets you search, filter data across yahoo services. We can write a select statement just like we use select statement for a table in database. We can also use joins to combine more than one results. Checkout following example.


Ext.data.JsonP.request({
            url: 'http://query.yahooapis.com/v1/public/yql',
            params: {
                q: 'select * from rss where url ="http://news.search.yahoo.com/rss?ei=UTF-8&p=USD"',
                format: 'json'
            },
            success: function (response) {
                console.log(response);
            },
            failure: function () {

            }
});

That's it and you have rss feeds in json format using cross domain jsonp request for sencha touch. Above example will return all the news related to USD currency published on yahoo news. This way you can query for any other topic.

Please note that yahoo will not return results if you try to access yql frequently from single IP. This is for security reasons. If you need uninterrupted results, you need to sign up for api and get authentication key and pass it along with the your request. Above example if of JsonP request. Same way you can use it in Ext.data.Store along with JsonP proxy. See the following code.


 Ext.define('MyApp.store.MyStore',{
       extend: 'Ext.data.Store',
       config:{
           model:'MyApp.model.MyModel',
           autoLoad: true,
            proxy : {
                type : 'jsonp',
               url: 'http://query.yahooapis.com/v1/public/yql',
                 reader: {
                  type: 'json',
                  rootProperty: 'query.results.item',
                }
            }
       }
});


Ext.getStore('MyStore').getProxy().setExtraParams({
                   q:'select * from rss where url ="http://news.search.yahoo.com/rss?ei=UTF-8&p=USD",          
                   format: 'json'
              });
Ext.getStore('MyStore').load();


This is the example of dynamic loading of store.


Hope this help you.

Sunday, July 22, 2012

Hide browser address bar in Sencha Touch 2 app

Hello,

This is quick blog on how to hide browser address bar in sencha touch 2 app.

You need to specify autoMaximize propery of view port in the application. Checkout following code.


Ext.application({
                            name: 'MyApp',
                            icon: 'resources/images/icon.png',
                            tabletStartupScreen: 'resources/images/tablet_startup.png',
                            phoneStartupScreen: 'resources/images/phone_startup.png',
                            glossOnIcon: false,
                            viewport: {
                                autoMaximize: true
                            },
                            models: ['MyModel'],
                            stores: ['MyStore'],
                            views: ['MyView'],
                            controllers: ['MyController'],
                         
                            launch: function () {

                                   Ext.Viewport.add({
                                                        xclass: ' MyApp .view. MyView'
                                  });
                           }
                });

That's it and open the application in any mobile browser, it will hide the browser address bar. Please note that this trick will only work for mobile browsers.

Tuesday, May 29, 2012

Sencha Touch fieldset, title wrap to new line

Hello,

This is quick blog on one issue I faced recently. I was working on an application where I was having unwanted horizontal scroll bar while working with form panel and field sets. That was causing bit annoying effect for end user because when they try to scroll sometimes it scrolls horizontally sometime it scrolls vertically. After searching a lot I found that it was due to title of fieldset. When title goes beyond some characters it does not warp to next line but it increases the width of container and because of that horizontal scroll bar becomes visible. To prevent this I made following CSS changes.

If you check sencha touch debug css you will find following class in it.


.x-form-fieldset-title {
       text-shadow: white 0 1px 1px;
       color:  #333;
       margin: 1em 0.7em 0.3em;
       color:  #333;
       font-weight: bold;
       white-space: nowrap;
}


Check the last property white-space: nowrap; because of this it does not wrap to next line. So you need to override this class with following definition.


.x-form-fieldset-title {
       text-shadow: white 0 1px 1px;
       color:  #333;
       margin: 1em 0.7em 0.3em;
       color:  #333;
       font-weight: bold;
       white-space: normal;
}


You have to add this class in any CSS file which you load after sencha touch css and it will make the difference. This trick can also be applied for form panel labels.

Hope this helps you.


Wednesday, May 2, 2012

Align Sencha Touch Overlay in Center

Hello,

Recently I was working on a project where requirement was to show overlay panel with some controls and align it to center.  We know that sencha provide function showBy where we can pass an element to display overlay.

Something like this panel.showBy(element)

Overlay will be displayed adjacent to the element you pass with a triangle arrow displayed towards control. See the image below.


So to make it center I did some tweaks. See the code below.


myPanel.showBy(ele);

var viewPortHeight = Ext.Viewport.windowHeight;
var viewPortWidth = Ext.Viewport.windowWidth;

var windowWidth = viewPortWidth / 2.5;
var windowHeight = viewPortHeight / 3.0;

myPanel.setWidth(windowWidth);
myPanel.setHeight(windowHeight);

 myPanel.setLeft((viewPortWidth - windowWidth) / 2);
 myPanel.setTop((viewPortHeight - windowHeight) / 2);

Following code is to remove that black triangle if you want.

if (discountPanel.element.dom.childNodes[1]) {
         myPanel.element.dom.childNodes[1].parentNode.removeChild(myPanel.element.dom.childNodes[1]);
        }        

That's it and your overlay is aligned to center.

Monday, April 9, 2012

How to create Splash screen for Sencha touch application?

Hello,

This is blog post is all about creating splash screen for sencha touch application. We all know that Sencha touch displays startup image while app is busy in loading necessary JavaScript files. However this image will only be displayed for few seconds. What if you want to display splash screen for users to have a feel that application is loading necessary data and will start in some time.

Following is the code to accomplish this. You have to write this code in app.js file


launch: function () {
        Ext.Viewport.setMasked({
            xtype: 'loadmask',
            message: 'My Message'
        });
        new Ext.util.DelayedTask(function () {
            Ext.Viewport.setMasked(false);
            Ext.Viewport.add({
                xclass: 'MyApp.view.Main'
            });

        }).delay(5000);
    }

Above code will display load mask for 5 seconds and than it will add your main view to viewport. You can also override Ext.LoadMask if you want to display your brand logo or images etc. See the following code


Ext.override(Ext.LoadMask, {
getTemplate: function() {
        var prefix = Ext.baseCSSPrefix;

        return [
            {
                //it needs an inner so it can be centered within the mask, and have a background
                reference: 'innerElement',
                cls: prefix + 'mask-inner',
                children: [
                //the elements required for the CSS loading {@link #indicator}
                    {
                        html: '<img src="images/MyLogo.png"/>'
                    },
                    {
                        reference: 'indicatorElement',
                        cls: prefix + 'loading-spinner-outer',
                        children: [
                            {
                                cls: prefix + 'loading-spinner',
                                children: [
                                    { tag: 'span', cls: prefix + 'loading-top' },
                                    { tag: 'span', cls: prefix + 'loading-right' },
                                    { tag: 'span', cls: prefix + 'loading-bottom' },
                                    { tag: 'span', cls: prefix + 'loading-left' }
                                ]
                            }
                        ]
                    },
                    //the element used to display the {@link #message}
                    {
                        reference: 'messageElement'
                    }
                ]
            }
        ];
    }
});

Check this code


{
      html: '<img src="images/MyLogo.png"/>'
}

This will add an extra logo to your loading screen. This is how you can add any other extra content to your splash screen.

Hope this helps you.




Tuesday, April 3, 2012

Sencha touch Spinner field - Change Plus/Minus button icons

Hello,

Recently I was working on a project where I had to change plus/minus button icons on sencha touch spinner fields. See the image below.

First I tried to change it with traditional sencha touch approach. By using sass files but there are no variables or mixins defined for it. You can check this from Sencha Touch Theme  

After that I checked the source code of spinner field and found that plus and minus signs are added by just specifying it in html of element so we can override it and change it the way we want.

Here are steps for it. Create an overides.js file and attach it after sencha touch js and before app.js. Add following code to it.



Ext.override(Ext.field.Spinner, {
    updateComponent: function (newComponent) {
        this.callParent(arguments);

        var innerElement = this.innerElement,
            cls = this.getCls();

        if (newComponent) {
            this.spinDownButton = Ext.Element.create({
                cls: ' minusButton',
                html: '<img src="resources/icons/minus.png"/>'
            });

            this.spinUpButton = Ext.Element.create({
                cls:' plusButton',
                html: '<img src="resources/icons/plus.png"/>'
            });

            this.downRepeater = this.createRepeater(this.spinDownButton, this.onSpinDown);
            this.upRepeater = this.createRepeater(this.spinUpButton, this.onSpinUp);
        }
    }
});

Check this code

html: '<img src="resources/icons/minus.png"/>' 
html: '<img src="resources/icons/pluss.png"/>'

This does the trick and will add your custom icon to it and you will get ui as displayed in above image.

Hope this helps you. Please note that above code is tested for Sencha touch 2.0 gpl version. It may be different for other older versions or may change in future version.


Friday, March 23, 2012

Create GIF image from image sequences using Photoshop

Hello,

This is my first blog on graphics designing. Although I am not a designer, I tried my hands on Photoshop to create gif image using image sequences. So my designers friends, please excuse me if I am wrong somewhere. Please note that I have used Adobe Photoshop CS4 to make gif image mentioned in the blog. First open a Adobe Photoshop CS 4.



After that Go to File-> Scripts -> Load Files into Stack.


It will open a window where you can browse images you want to add as a sequence. Select the images you want and click on ok.


After that photoshop will arrange all the images as a layer now open an animation window from Window -> Animations menu.


It will open animation window at bottom where all your images are available as a layer. Now click on layer and drag it to create a new frame as shown in image below.  Please see the yellow rectangle in below image.


This will create a new frame.  Now remove earlier layer from it. See the image below. Mark the red circle. Here there must be an icon looks like an eye. When you click it it will remove the layer.

This is how create frame for each layer one bye one you can set interval for each image using time selector in  each frame. See the image below. I marked it as a red circle.


Now got to File-> Save for Web and Devices.


This will open following window. Select GIF and and Save it with name.


That's it and you have your gif image ready. Hope this helps you. Do put your suggestions in comment. 

Saturday, March 17, 2012

Get device token for apple push notifications in sencha touch and phone gap

Hello,

Recently I was working on Apple push notifications with sencha touch and phonegap project. Requirement was to get device token in JavaScript file so that it can be sent to server for getting push notifications. For that first thing we need to do is register device to get Push notifications.

Please make sure that you have real apple device like iPad, iPhone or iPod touch to test this. This will not work in Simulator.

To register device for push notifications add following in your AppDelegate.m file.  Find method


- (BOOL) application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions

Also add following line to define token variable.


@synthesize token;


and add following code at end of the method.


NSLog(@"Registering for push notifications...");    
    [[UIApplication sharedApplication] 
     registerForRemoteNotificationTypes:
     (UIRemoteNotificationTypeAlert | 
      UIRemoteNotificationTypeBadge | 
      UIRemoteNotificationTypeSound)];

This will register device for getting push notifications. Please make sure that you have added provisioning profile which configured with non wild card app id and app is configured to receive  push notifications in your development portal. Also you need valid developer certificate.

After that you need to add following methods at end of the file.

- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { 

    
    self.token = [[[[deviceToken description]
                    stringByReplacingOccurrencesOfString: @"<" withString: @""]
                   stringByReplacingOccurrencesOfString: @">" withString: @""]
                  stringByReplacingOccurrencesOfString: @" " withString: @""];
    
    NSLog(@"My token is: %@", self.token);
}

- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err { 
    
    NSString *str = [NSString stringWithFormat: @"Error: %@", err];
    NSLog(str);    
    
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    
    for (id key in userInfo) {
        NSLog(@"key: %@, value: %@", key, [userInfo objectForKey:key]);
    }    
    
}


This will get device token and store it to token. Now to send this token to JavaScript we will create a phonegap plugin. Please make sure that this code will work with Phonegap version 1.5.0. They named this version as Cardova.

First create folder inside plugins folder of your project folder and name it as PushToken and add two files to it PushToken.m and PushToken.h

Open PushToken.h file and add following code to it.



#import <Foundation/Foundation.h>
#import <Cordova/CDVPlugin.h>

@interface PushToken : CDVPlugin{
    
    NSString* callbackID;  
}

@property (nonatomic, copy) NSString* callbackID;

- (void) getToken:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;

@end



Now open PushToken.m file and add following code to it.

#import "PushToken.h"
#import "AppDelegate.h"

@implementation PushToken

@synthesize callbackID;

-(void)getToken:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options  {
    self.callbackID = [arguments pop];
    
    NSString *token = ((AppDelegate *)[[UIApplication sharedApplication] delegate]).token;
    
    CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[token stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
    
    if(token.length != 0)
    {
        [self writeJavascript: [pluginResult toSuccessCallbackString:self.callbackID]];
    }else {    
        [self writeJavascript: [pluginResult toErrorCallbackString:self.callbackID]];
    }
}

@end

Now we have to map this plugin. For that open Cordova.plist file and following key value pair to it.

PushToken : PushToken

After that open AppDelegate.h file and add following line to it.

@property (retain, nonatomic) NSString* token;

That's it of phonegap side now we have to create JavaScript file form where we will execute this plugin code. Create JavaScript file with name PushToken.js and add it to www folder. Add following code to it.

var PushToken = {
    getToken: function(types, success, fail) {
        return Cordova.exec(success, fail, "PushToken", "getToken", types);
    }
};

Add link of this JavaScript file to your index.html page after phonegap JavaScript file. To get device token wherever you want use following code.


PushToken.getToken(     
                     ["getToken"] ,           
                     function(token) {
                              global.token = token; 
                     },
                     function(error) {
                              console.log("Error : \r\n"+error);      
                     }
          );

That's it and you have your device token.




Wednesday, March 7, 2012

Whitelist rejection error in Xcode for Sencha touch 2 and Phonegap

Hello,

If you have developed iPhone application using sencha touch 2 and phonegap and if your application is using Ajax request or JsonP request you might have above error. That's not the issue of cross domain because when you convert your sencha touch application to native app using phonegap, it loads index.html and other js files in browsers using file uris. Something like below

file://..
So there is no issue of cross domain. But still if you get above error. That's because of  iPhone security restriction. You must add your domain to whitelist. For that open the file phonegap.plist and you should see following.


See the external host. Here you have to add your domain something like below

TEST.MYDOAMIN.COM
Make sure that you don't add http or other protocols. Just a domain name and it should work.

Thursday, March 1, 2012

Sencha touch 2 : Dynamically set title of Navigation bar

Hello,

If you are using Sencha touch 2 beta 3, you might have encounter this issue. If you have a view that you pushing in navigation view you can set title there. This title will be used as a title of navigation bar when this view is activated.  See the following code.


config: {
        autoDestroy: false,
        title: 'Bill'
}

What if you have requirement of setting this dynamically. For example you have a list from which you selected a category.

-Category 1
-Category 2
-Category 3

Based on selected record you will load sub category in next view and you want to display Category 1 as a title in next screen. To load next view you will create it first and push it.


if (!this.menuSubCategory) {
                this. menuSubCategory = Ext.create('Application.view.SubCategory');
}
this.getMain().push(this. menuSubCategory);

Here you can set title if you want when you are first creating item. After that you can set it as you already have view. Either you can destroy view every time and create new one else you apply following trick.

This is how next view is loaded. Now if you have set title in config of above view. It will be applied by default. So to dynamically set it you need to do some tweak. First you need to override a base function. See the following code.


Ext.override(Ext.navigation.Bar, {
    getNavigationBarProxyProperties: function () {
        return {
            title: {
                left: this.proxy.titleComponent.renderElement.getLeft()
            },
            backButton: {
                left: this.proxy.backButton.renderElement.getLeft(),
                width: this.proxy.backButton.renderElement.getWidth()
            }
        };
    }
});



Create an overrides file and add it after sencha touch js file. This is necessary else title width will be less compared to title

Now add the following code after you pushed your view to navigation view.

this.getMain().query('#navBar')[0].titleComponent.setWidth(100);
this.getMain().query('#navBar')[0].titleComponent.setTitle('categoryName');  

That's it and you have dynamic title in navigation view. Please note this trick you need to apply if you are using Sencha touch 2 beta 3 version.

Friday, February 24, 2012

Sencha touch Error - TypeError: 'undefined' is not an object (evaluating 'o.id')

Hello,

If you are working with Sencha touch 2 or earlier versions you might had below mentioned error while working with list or selectfield

TypeError: 'undefined' is not an object (evaluating 'o.id')


Normally this error displayed when you are trying to select some old records in list or selectefield. For example you have a list on the form or a panel to select some records which you are saving in database and in case of edit you want to show last selected records. So we normally add code to select records on refresh. Something like below.


myList.on('refresh', function () {
                for (var i = 0; i < userSelectedRecords.getCount(); i++) {
                    var record =  userSelectedRecords .getAt(i);
                        var recordIndex =  myList .store.find('idField',  record.get('idField'));
                         myList .getSelectionModel().select(recordIndex, true, false);
                }
        },
        this,
        { single: true }
);


In above code last selected records are stored in userSelectedRecords and we are fetching records from it and selecting respective record in list. Now above refresh method fired two times. First when the list container like panel is rendered and second time when list store loaded with data. And as we all know sencha touch store load is asyc method so if you simply adding following line just above the refresh event handler this error is likely to come. Because by the time refresh event fired store load is still going on. Particularly this happens if you are loading store using Ajax proxy. Some time this error breaks your layout and freezes the panel. You don't have other option than reloading the app. To prevent this you can simply add store count check in refresh so first time when panel is rendered if store load it not complete, it will not try to select data. It will select it when store load completes.

Following is the code.


myList.on('refresh', function () {
               if(myList.store.getCount()>0)
                       for (var i = 0; i < userSelectedRecords.getCount(); i++) {
                            var record =  userSelectedRecords .getAt(i);
                             var recordIndex =  myList .store.find('idField',  record.get('idField'));
                             myList .getSelectionModel().select(recordIndex, true, false);
                       }
                }
        },
        this,
        { single: true }
);

Please note above code will work for sencha touch 1.1 and earlier version. For later versions you can do that check in controllers action.

Hope this helps you.