Saturday, December 22, 2012

Feel the difference between HTML touch and Native touch on UIWebView

Posting something on sunday is never been easy ;).Ok let me start with next article,will brief you about HTML touch and native touch on UIWebView.So many iOS developers will find it difficult if they try to do their own,because most of the developers were not familiar in javascript.Hold on i am here to save your time :)

HTML Side:

Well i am not an expert at HTML/Javascript,but i will share the things which i know,and with which i achieved the success.I will take one simple html with one button. You're not going to distinguish the HTML and UIWebView touch by using onlick event in the html,so importantly you have to use the on "ontouchstart" event for the button tap,because when you tap on any button/image etc with onClick(); event,your UIWebView takes it both HTML and native touch and note that HTML touch will take the priority over native touch.

<button type="button" autofocus="autofocus" ontouchstart ="button1_click();">Click Me!</button>function button1_click()
{
htmltouch=1;
}


In the script will declare a global variable called "htmltouch",whenever i tap on any html events,i will make htmltouch=1.

<html>

<head>

<meta http-equiv="Content-type" content="text/html" charset="utf-8" />


<meta name="apple-mobile-web-app-capable" content="yes">


<meta name="viewport" content="width=768, height=1024, user-scalable=yes">

<script type="text/javascript">

 var htmlTouch = 0;

//function which we will call from oObjective-c
function toCallFromiOS() 
{

if( htmlTouch  == 1 ) 

alert("its a html touch");
return 'touched';
}    

}  

function resetTheHTMLtouch() 
{
htmlTouch = 0;
}     

function button1_click()
{
htmlTouch =1;
}

</script>

</head>

<body>

<button type="button" autofocus="autofocus" ontouchstart ="button1_click();">Click Me!</button>

</body>
<html>

//Wondering why i used toCallFromiOS and resetTheHTMLtouch function?.Those are the golden functions which will call from native.HTML side is completed,now bring it on!

Native Side:

Create UIWebview to load the above html (well i am doing it in local now).
self.webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] 
                                pathForResource:@"touch" ofType:@"html"] isDirectory:NO]]];

Now add the gesture delegate to the whole webview.
UITapGestureRecognizer *tapGestureDown = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture)];
tapGestureDown.numberOfTapsRequired = 1;
tapGestureDown.delegate = self;
[self.webView addGestureRecognizer:tapGestureDown];




//handleTapGesture is a native method,in the sense "On detecting if its a native touch,what you want perform?"
-(void)
handleTapGesture
{
NSLog(@"Touch is native");
}

Now we are all set.Next step is to add the delegate called  "-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer"

On detecting the touch event on the webview,the added delegate function gets called.

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
Now how do we proceed?.If you add the above code just like that,on tapping the webview the above delegate gets called N number of times(sometimes 8,9,13 etc).Only solution is we should be able to know the state of the touch(whether its end or start),to reset the touch even for the next call.

 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
NSString *javastr=[self.webView stringByEvaluatingJavaScriptFromString:@"toCallFromiOS();"];
NSLog(@"%@",javastr);

 if((otherGestureRecognizer.state==UIGestureRecognizerStateEnded && [javastr hasPrefix:@"touched"]))
{
       
javastr= [self.webView stringByEvaluatingJavaScriptFromString:@"resetTheHTMLtouch();"];
     
return NO;
}
    
if([javastr hasPrefix:@"touched"])
{
return NO;
}
    
return YES;

}

If the 
javastr returns any value its a HTML touch or else its a native touch,"handleTapGesture" gets called.


This is all about HTML touch and Native touch.Hope you guys enjoyed doing this.Good luck.:)

Thursday, December 20, 2012

PageCurl bug from iOS 5.1 to 6.0

After bleeding too much of blood to solve crashing issue while trying to flip the first page(left side) and last page(right side) , finally i have come up with following solution. :)

OLD CODE(iOS 5.1) : when returning nil on the first and last page you will experience the app crash.It works fine in iOS 5.1,but in iOS 6 it wont.


- (UIViewController *)pageViewController:
(UIPageViewController *)pageViewController viewControllerBeforeViewController:
(UIViewController *)viewController
{
    for (UIGestureRecognizer *recognizer in pageController.gestureRecognizers) {
        if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
            recognizer.enabled = NO;
        }
    }
    NSUInteger index = [self indexOfViewController:
                        (MainViewController *)viewController];
    if ((index == 0) || (index == NSNotFound)) {
        return nil;
    }
    
    index--;
    return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:
(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
    for (UIGestureRecognizer *recognizer in pageController.gestureRecognizers) {
        if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
            recognizer.enabled = NO;
        }
    }
    NSUInteger index = [self indexOfViewController:
                        (MainViewController *)viewController];
    if (index == NSNotFound) {
        return nil;
    }
}

SOLUTION(iOS 6) : After adding the gesture effect to the superview,just call the delegate called -(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer. What i did is quiet simple, computing the speed of the user flipping the first page and last page (i mean using the gesture recognizer) , i denied the swiping.All you need to do is just paste the following code,and you are DONE!.

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    if (pageNum==0) {
        
        if ([(UIPanGestureRecognizer*)gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] &&
            [(UIPanGestureRecognizer*)gestureRecognizer velocityInView:gestureRecognizer.view].x > 0.0f) {
            //NSLog(@"Swiping to left on 1st page is denied");
            return NO;
        }
        if ([(UITapGestureRecognizer*)gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] &&
            [(UITapGestureRecognizer*)gestureRecognizer locationInView:gestureRecognizer.view].x < self.view.frame.size.width/2) {
         //NSLog(@"tapping to left on 1st page is denied");
            return NO;
        }
    }
    
    else if(pageNum ==totalNoOfFiles-1)
    {
        
        if ([(UIPanGestureRecognizer*)gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] &&
            [(UIPanGestureRecognizer*)gestureRecognizer velocityInView:gestureRecognizer.view].x < 0.0f) {
            //NSLog(@"Swiping to right on 1st page is denied");
            return NO;
        }
        if ([(UITapGestureRecognizer*)gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] &&
            [(UITapGestureRecognizer*)gestureRecognizer locationInView:gestureRecognizer.view].x > self.view.frame.size.width/2) {
          //NSLog(@"Tapping to right on 1st page is denied");
            return NO;
        }
    }
    
    return YES;
}


- (UIViewController *)pageViewController:(UIPageViewController*) pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
 {
  int index = [self indexOfViewController:(ChildViewController *)viewController];
                index--;
        
    return [self viewControllerAtIndex:index];
}


- (UIViewController *)pageViewController:
(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
    int index = [self indexOfViewController:(ChildViewController *)viewController];
          
            index++;
     return [self viewControllerAtIndex:index];
}



Hope your life is saved :)

Downloading images asynchronously in background thread.

Don't you think,downloading the images in a direct way will block you main thread.Yes ofcourse ,so i come up with the simple class library called ImageDownloader,you just need to pass the url and imageview frame and its all done.

Lets have a quick look at it,first get the library from  here. And include ImageDownloader class into your project.

Few lines of coding goes here.


 NSURL *url=[NSURL URLWithString:@"ur image url"];
    
    ImageDownloader *imageView = [[ImageDownloader alloc] initWithFrame:CGRectMake(10,    5,120, 100)];
    //imageView.contentMode = UIViewContentModeScaleAspectFill;
    imageView.clipsToBounds = YES;
    imageView.tag = IMAGE_VIEW_TAG;
   
   [[ImageLoader sharedLoader] cancelLoadingURL:imageView.imageURL];
    
    //load the image
    imageView.imageURL = url;
     [urView addSubview:imageView];

The same concept i used in the "Talking to JSON"  sample code :)

Talking to JSON

Isn't it interesting to do so?. And i love the way it is. In the following steps i will explain you the steps reading json file.


my sample json file is here sample.json

{
"books":
[
     { 
     "name" : "A Bend in the river"
     "author" : "V.S. Naipaul"
     "id" : "01"
      "coverurl" :"http://myiosapps.comuf.com/mySampleProjects/jsonTutorial/1.jpeg"
      },
     { 
     "name" : "Baburnama"
     "author" : "Babur"
     "id" : "02"
      "coverurl":"http://myiosapps.comuf.com/mySampleProjects/jsonTutorial/2.jpeg"
      },
     { 
     "name" : "The secret"
     "author" : "Acharya Rajneesh"
     "id" : "03"
      "coverurl" :"http://myiosapps.comuf.com/mySampleProjects/jsonTutorial/3.jpeg"
      },
     "name" : "Bermuda Triangle"
     "author" : "Charles Berlitz"
     "id" : "04"
      "coverurl" :"http://myiosapps.comuf.com/mySampleProjects/jsonTutorial/4.jpeg"
      },

     "name" : "Eye of the Storm"
     "author" : "Patrick White"
     "id" : "05"
      "coverurl" :"http://myiosapps.comuf.com/mySampleProjects/jsonTutorial/5.jpeg"
      }

  ]
}
Step 1: First thing,make sure you have the json library,if not get it from here JSON. and drag all the classes into your project.

Step 2: In the header just include the JSON.h.Now lets get into the coding part.

Step 3: In the viewdidload,lets download the sample.json file,which is kept in the server
NSData* tmpData = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"Ur json file url goes here"]];
    
    NSString* cacheDir = CacheDirectory;
    NSString* cachePath = [cacheDir stringByAppendingPathComponent:@"sample.json"];
    
    if (!tmpData){
        // attempt to get the data from the cache
        tmpData = [NSData dataWithContentsOfFile:cachePath];
    }
     
    [tmpData writeToFile:cachePath atomically:YES];
    NSString *jsonString = [[NSString alloc] initWithData:tmpData encoding:NSASCIIStringEncoding];
    NSDictionary *results = [jsonString JSONValue];
    NSArray * json_issues = [NSArray arrayWithArray: [results objectForKey:@"books"]];
    
    books = [[NSArray alloc] initWithArray:json_issues];

books has all values retrieved from json file.Now thats done,all we need to do is,just need to extract value for each key element from the dictionary object.


-(NSDictionary *)bookAtIndex:(NSInteger)index {
    return [books objectAtIndex:index];
}
-(NSString *)authorNameAtIndex:(NSInteger)index {
    return [[self bookAtIndex:index] objectForKey:@"name"];
}
-(NSString *)bookUrlAtIndex:(NSInteger)index {
    return [[self bookAtIndex:index] objectForKey:@"coverurl"];
}

-(NSString *)bookNameAtIndex:(NSInteger)index {
    return [[self bookAtIndex:index] objectForKey:@"author"];
}


Step 4: Now passing those strings to UITableView cell is not a big deal.Note that you need include UITableViewDataSource and UITableViewDelegates.
- (UITableViewCell *)tableView:(UITableView *)tableView2 cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"bookCell";
    
    UITableViewCell *cell = [tableView2 dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellSelectionStyleNone reuseIdentifier:CellIdentifier];
    }
    //cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    NSURL *url=[NSURL URLWithString:[self bookUrlAtIndex:indexPath.row]];
  
 //Need to include 
ImageDownloader class
    ImageDownloader *imageView = [[ImageDownloader alloc] initWithFrame:CGRectMake(10, 5,120, 100)];
    //imageView.contentMode = UIViewContentModeScaleAspectFill;
    imageView.clipsToBounds = YES;
    imageView.tag = IMAGE_VIEW_TAG;
   
   [[ImageLoader sharedLoader] cancelLoadingURL:imageView.imageURL];
    
    //load the image
    imageView.imageURL = url;
     [cell addSubview:imageView];
    UILabel *authorLabel=[[UILabel alloc]initWithFrame:CGRectMake(140, 30, 200, 20)];
    authorLabel.text=[self authorNameAtIndex:indexPath.row];
    authorLabel.textColor=[UIColor blackColor];
    authorLabel.textAlignment=NSTextAlignmentLeft;
    

    [cell addSubview:authorLabel];
    UILabel *bookLabel=[[UILabel alloc]initWithFrame:CGRectMake(140, 60, 200, 20)];
    bookLabel.text=[self bookNameAtIndex:indexPath.row];
    bookLabel.textColor=[UIColor blackColor];
    bookLabel.textAlignment= NSTextAlignmentLeft;
    bookLabel.font=[UIFont fontWithName:@"Helvetica" size:12];
    [cell addSubview:bookLabel];
    return cell;
}





Well this is it about reading a simple JSON file. Hope it helps you.You can download the source code here .