Showing posts with label Xcode. Show all posts
Showing posts with label Xcode. Show all posts

Saturday, January 21, 2017

Xcode 8.2 Simulator Crash When Save Screen Shot - Alternate way to take Screenshot of iPhone simulator

I don't know what went wrong with my Xcode. Recently I was publishing an app in iTunes connect and for that I needed iPhone 7 screen shot. I opened simulator and run and app and tried to capture screenshot with Command + S and it crashed the simulator with following error and screen shot file was empty.



It shows some error related to some library of SwiftFoundation. I was not sure about this error. So first thing what I did is report it to apple and then tried few things like. Restarting simulator couple of times and restarting Xcode couple of times. But it didn't work. So may be it's related to SDK update. I updated the latest SDK but still it was not working. So at last I give it to Apple to solve the problem but I needed that screen shot. So here is alternate way to take Screenshot of iPhone simulator.

With simulator running. Select Go to Edit menu and Select Copy Screen.



This will copy current screen of simulator. Now open the preview and go to File and Select New From Clipboard.



And it will give you new image with copied screen of your simulator, now save it and use it with Preview. Hope this helps you.

Saturday, January 14, 2017

AVCaptureVideoPreviewLayer Black Screen. AVfoundation Black Screen on Record

Recently in one of my project we used AVFoundation to record video. In some of the iOS devices, we were getting issue that on start recording, it shows black screen and video is not recorded. After some investigation I found out that it's because user has manually revoked camera access from settings so it was not woking. So to solve this issue, you must check if the permission is there or not. If not first request permission and if it's denied, so message to user.

So here is the function you should use. You should call this function, before you start recording and check if there is necessary permission.

- (void)requestCameraPermissionsIfNeeded {
   
    NSLog(@"requestCameraPermissionsIfNeeded");
    // check camera authorization status
    AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    switch (authStatus) {
        case AVAuthorizationStatusAuthorized: { // camera authorized
            NSLog(@"requestCameraPermissionsIfNeeded camera authorized");
            // do camera intensive stuff
        }
            break;
        case AVAuthorizationStatusNotDetermined: { // request authorization
            NSLog(@"requestCameraPermissionsIfNeeded have to ask user again");
            [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
                dispatch_async(dispatch_get_main_queue(), ^{
                   
                    if(granted) {
                        // do camera intensive stuff
                    } else {
                       
                        NSLog(@"STOP RECORDING");
                        WeAreRecording = NO;
                        ShareVideo = YES;
                        [MovieFileOutput stopRecording];
                        //Prompt message to user.
                    }
                });
            }];
        }
            break;
        case AVAuthorizationStatusRestricted:{
            NSLog(@"STOP RECORDING");
            WeAreRecording = NO;
            ShareVideo = YES;
            [MovieFileOutput stopRecording];
            //Prompt message to user.
        }
           
        case AVAuthorizationStatusDenied: {
            NSLog(@"STOP RECORDING");
            WeAreRecording = NO;
            ShareVideo = YES;
            [MovieFileOutput stopRecording];
            //Prompt message to user.
            dispatch_async(dispatch_get_main_queue(), ^{
            });
        }
            break;
        default:
            break;
    }
}


This will help you in identifying issue and display proper message to user.

Monday, December 19, 2016

Objective C - Record Video With AVCaptureSession

Hello,

In this blog I am going to explain how to record video with AVCaptureSession in your iOS application.

First of all add following import statements in your view controller header file.

#import <Foundation/Foundation.h>
#import <CoreMedia/CoreMedia.h>
#import <AVFoundation/AVFoundation.h>
#import <AVKit/AVKit.h>
#import <AVFoundation/AVFoundation.h>
#import <AssetsLibrary/AssetsLibrary.h>

Now we will have to set preview layer for the recording in our view and also we will need input device and output file location. Also we need to add AVCaptureFileOutputRecordingDelegate to have notifications of events like recording stop.

Implement this delegate in your header file.

@interface MainViewController : CDVViewController
{
    BOOL WeAreRecording;
    BOOL ShareVideo;
    AVCaptureSession *CaptureSession;
    AVCaptureMovieFileOutput *MovieFileOutput;
    AVCaptureDeviceInput *VideoInputDevice;
}

Now we will set preview layer and init AVCaptureSession in viewDidLoad and set input and output.

CaptureSession = [[AVCaptureSession alloc] init];
AVCaptureDevice *VideoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
AVCaptureDevice *audioCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
NSError *error = nil;
AVCaptureDeviceInput *audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioCaptureDevice error:&error];
if (audioInput)
{
[CaptureSession addInput:audioInput];
}

[self setPreviewLayer:[[AVCaptureVideoPreviewLayer alloc] initWithSession:CaptureSession]];
PreviewLayer.orientation = AVCaptureVideoOrientationLandscapeRight;
[[self PreviewLayer] setVideoGravity:AVLayerVideoGravityResizeAspectFill];

Now we will setup output file and video recording settings and image quality.

MovieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
Float64 TotalSeconds = 60;
int32_t preferredTimeScale = 30;
CMTime maxDuration = CMTimeMakeWithSeconds(TotalSeconds, preferredTimeScale);
MovieFileOutput.maxRecordedDuration = maxDuration;
MovieFileOutput.minFreeDiskSpaceLimit = 1024 * 1024;
   
if ([CaptureSession canAddOutput:MovieFileOutput])
    [CaptureSession addOutput:MovieFileOutput];
   
[self CameraSetOutputProperties];

[CaptureSession setSessionPreset:AVCaptureSessionPresetMedium];
if ([CaptureSession canSetSessionPreset:AVCaptureSessionPreset640x480])
    [CaptureSession setSessionPreset:AVCaptureSessionPreset640x480];
   
CGRect layerRect = [[[self view] layer] bounds];
CGRect viewBoundsPreview = [self.webView bounds];
viewBoundsPreview.origin.y = 20;
viewBoundsPreview.size.height = viewBoundsPreview.size.height - 40;
[PreviewLayer setBounds:viewBoundsPreview];
[PreviewLayer setPosition:CGPointMake(CGRectGetMidX(layerRect),
 CGRectGetMidY(layerRect))];

UIView *CameraView = [[UIView alloc] init];
[[self view] addSubview:CameraView];
[self.view sendSubviewToBack:CameraView];
[[CameraView layer] addSublayer:PreviewLayer];
[CaptureSession startRunning];

Now capture session is running, we have to start and stop recording.

To start recording, add following code to your handler.

NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970];
NSNumber *timeStampObj = [NSNumber numberWithInteger:timeStamp];
NSString* fileName = [timeStampObj stringValue];
fileName = [fileName stringByAppendingString:@".mov"];
NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), fileName];
NSURL *outputURL = [[NSURL alloc] initFileURLWithPath:outputPath];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:outputPath])
{
NSError *error;
if ([fileManager removeItemAtPath:outputPath error:&error] == NO)
{
//Error - handle
}
}
//Start recording
[MovieFileOutput startRecordingToOutputFileURL:outputURL recordingDelegate:self];


Above code will start recording. Add following code to stop recording.

[MovieFileOutput stopRecording];

This will stop recording and save video to Photos library.

Friday, December 16, 2016

Objective C - Play Video From Application Temp Folder

Recently in one of my iOS project , there was requirement to play Video stored in temporary folder of Application data. After some hours of struggle I managed to get it working.

So the problem I was facing is I have absolute URL of the video that I was trying to play in MPMoviePlayerController but it was not working as the player was displayed for couple of seconds and it's dismissed automatically and there was a black screen.

So after sometime I found out that MPMoviePlayerController is deprecated, instead of it we shall use AVPlayer and that too was not working if I give absolute path to initialize a player. So first of all I just extracted file name fro the absolute path will following code.

NSRange range = [filePath rangeOfString:@"/" options:NSBackwardsSearch];
NSUInteger index = range.location;      
filename = [filePath substringFromIndex:index+1];

Now we will initialize player. Please note you have to import AVKIt first in your header file.

#import <AVKit/AVKit.h>

Now initialize player.

NSString *outputPath = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
AVAsset *asset = [AVAsset assetWithURL:[NSURL fileURLWithPath:outputPath]];
AVPlayer *_avPlayer = [[AVPlayer alloc]initWithPlayerItem:[[AVPlayerItem alloc]initWithAsset:asset]];

movieLayer = [AVPlayerLayer playerLayerWithPlayer:_avPlayer];
movieLayer.frame = self.view.bounds;
[self.view.layer addSublayer:movieLayer];

So player will be added as sublayer on the view so we have to dismiss it when video finished playing.

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(videoDidFinish:)
name:AVPlayerItemDidPlayToEndTimeNotification
  object:[_avPlayer currentItem]];

And add callback function to remove player layer.

- (void)videoDidFinish:(id)notification
{
    NSLog(@"finsihed");
    [movieLayer removeFromSuperlayer];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}


That's it. Hope this will help you.

Saturday, December 10, 2016

Cordova Application Hanging During Startup on iOS 10

Hello,

If you have any cordova application in iTunes, you may have faced this issue since launch of iOS 10,. Either your app hangs at Start up or it will hang in case when there is a use of any plugin, like camera or location or any other native features. 

This is because of content security policy. iOS 10 needs content security policy where you have to mention what types of content you will allow to load.

As you have notice cordova plugins are invoked gap:// and in iOS 10 it's not allowed by default so you have to mention this in content security policy. 

Add following line in head section of your index.html file.

<meta http-equiv="Content-Security-Policy" content="media-src *; img-src * data:; font-src * data:; default-src  * gap:; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'">


As you can see we have added gap: in allowed content src along with other source, now your app will work normally in iOS 10.

Hope this helps you.

Saturday, January 2, 2016

Resolve App Transport Security Exceptions in iOS 9 and OSX 10.11

Hello,

Recently I was working on an old iOS application for my client. This app was developed on iOS 7 and we were adding few updates, I was using iOS 9 SDK on my Xcode. While development I found that none of the web services were working on app. In short app was not able to get data from remote URLs. I show the logs and there was App Transport Security Exception.

Let's first understand what is App Transport Security (ATS).

At WWDC 2015, Apple announced “App Transport Security” for iOS 9 and OSX 10.11 El Capitan. The “What’s New in iOS” guide for iOS 9 explains:

App Transport Security (ATS) lets an app add a declaration to its Info.plist file that specifies the domains with which it needs secure communication. ATS prevents accidental disclosure, provides secure default behavior, and is easy to adopt. You should adopt ATS as soon as possible, regardless of whether you’re creating a new app or updating an existing one.

If you’re developing a new app, you should use HTTPS exclusively. If you have an existing app, you should use HTTPS as much as you can right now, and create a plan for migrating the rest of your app as soon as possible.

In simple terms, this means that if your application attempts to connect to any HTTP server (in this example, yourserver.com) that doesn’t support the latest SSL technology (TLSv1.2), your connections will fail with an error like this:

CFNetwork SSLHandshake failed (-9801)
Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo=0x7fb080442170 {NSURLErrorFailingURLPeerTrustErrorKey=, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorCodeKey=-9802, NSUnderlyingError=0x7fb08055bc00 "The operation couldn’t be completed. (kCFErrorDomainCFNetwork error -1200.)", NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://yourserver.com, NSErrorFailingURLStringKey=https://yourserver.com, _kCFStreamErrorDomainKey=3}

In short app should have all the remote calls with Https protocol. How ever in my case it was not possible as my client refused to install SSL certificate. So I have to bypass App Transport Security.

So here how to do this. Open your project in Xcode and open info.plist file and add following key.

App Transport Security Settings



Now add one more key under that key which you added above. Following is the name of key.

Allow Arbitrary Loads and set it's value to YES. After adding both keys your info.plist should look as below.



That's it and now all your services should work with http protocols.



Sunday, May 3, 2015

Add iOS in App Purchase to Your Cordova Application

Hello,

Recently I was working on cordova application where we have to add in app purchase in iOS. In this blog I am going to explain how to add in app purchase to cordova based application.

First of all open your MainViewController.m file and un comment following function.


- (BOOL) webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType

As we are going to use above function to pass product id to native code from JavaScript with use of this function. Here is how to do this. From your JavaScript file add following code.

window.location.href = 'http://buyproduct.com?productId='+sku.toLowerCase();

This will invoke shouldStartLoadWithRequest delegate. Now in that delegate add following code.

NSURL *url = [request URL];
if([[url hostisEqual: @"buyproduct.com"]){
        NSString *queryString = url.query;
        NSArray* queryStringValues = [queryString componentsSeparatedByString: @"&"];
        NSString* productId = [[[queryStringValues objectAtIndex:0] componentsSeparatedByString: @"="] objectAtIndex:1];
       return NO;
}

This way we get product id in native code and since we returned NO in that delegate, webview will not invoke this url.

Now let's add required library to support in App Purchase. First select project from project explorer and select build phases tab. At bottom where we have linked libraries click on + sign and search for storekit. It will show following framework. Add this to project.


Now open MainViewController.h file and add necessary import statements and delegates. Copy following code.

#import
#import
#import
#import

@interface MainViewController : CDVViewController <SKProductsRequestDelegate, UIAlertViewDelegate, SKPaymentTransactionObserver>
@property (retain, nonatomic) SKProduct* fetchedProduct;
@end
@interface MainCommandDelegate : CDVCommandDelegateImpl
@end

@interface MainCommandQueue : CDVCommandQueue
@end

Now open MainViewController.m file and add necessary callbacks.

#pragma mark -
#pragma mark SKProductsRequestDelegate methods

- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
    
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    SKPayment * payment = [SKPayment paymentWithProduct:fetchedProduct];
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    for (SKPaymentTransaction * transaction in transactions) {
        switch (transaction.transactionState)
        {
            case SKPaymentTransactionStatePurchased:
                [self completeTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed:
                [self failedTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored:
                [self restoreTransaction:transaction];
            default:
                break;
        }
    };
}

- (void)completeTransaction:(SKPaymentTransaction *)transaction {
    
}

- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
    NSLog(@"restoreTransaction...");
    //call javascript function to consume product only
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}

- (void)failedTransaction:(SKPaymentTransaction *)transaction {
    
    NSLog(@"failedTransaction...");
    if (transaction.error.code != SKErrorPaymentCancelled)
    {
        NSLog(@"Transaction error: %@", transaction.error.localizedDescription);
    }
    
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}

This are necessary functions to support transactions and product request. Now lets first request a product information. Go back to shouldStartLoadWithRequest and add following code at the end.

BOOL productPurchased = [[NSUserDefaults standardUserDefaults] boolForKey:productId];
        if (productPurchased) {
            //call javascript function to consume product
            [self.webView stringByEvaluatingJavaScriptFromString:@"consumePurchasedProduct();"];
        }else{
            SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
            productsRequest.delegate = self;
            [productsRequest start];
        }

Here we are checking if product already purchased. If already purchased simply call JavaScript function to consume it else start product request. After we get product information we have to show it to user.  Add following code to productRequest delegate.

NSArray *products = response.products;
    fetchedProduct = [products count] == 1 ? [products firstObject] : nil;
    if (fetchedProduct)
    {
        NSLog(@"Product title: %@" , fetchedProduct.localizedTitle);
        NSLog(@"Product description: %@" , fetchedProduct.localizedDescription);
        NSLog(@"Product price: %@" , fetchedProduct.price);
        NSLog(@"Product id: %@" , fetchedProduct.productIdentifier);
        
        NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
        [formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
        [formatter setLocale:[NSLocale currentLocale]];
        NSString *localizedMoneyString = [formatter stringFromNumber:fetchedProduct.price];

        NSString *productPrice = @"Price : ";
        productPrice = [productPrice stringByAppendingString:localizedMoneyString];
        NSString* alertViewContent = fetchedProduct.localizedDescription;
        alertViewContent = [alertViewContent stringByAppendingString:@"\n \n"];
        alertViewContent = [alertViewContent stringByAppendingString:productPrice];
        UIAlertView * alert = [[UIAlertView alloc] initWithTitle:fetchedProduct.localizedTitle message:alertViewContent delegate:self cancelButtonTitle:@"Buy" otherButtonTitles:nil];
        [alert show];
    }

Above function will show alert like this with product information.


As you can see we have a buy button there. When user clicks on Buy it will call clickedButtonAtIndex function added in above code and it will start payment process. One payment is done it will call completeTransaction delegate. Add following code to it.

NSLog(@"completeTransaction...");
    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:fetchedProduct.productIdentifier];
    //call javascript function to consume product
    [self.webView stringByEvaluatingJavaScriptFromString:@"consumePurchasedProduct();"];
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

Here we are adding product to user defaults in case network got disconnected before user can consume product. In case of transaction failure other functions will be called.

Tuesday, March 3, 2015

Cocoa OSX NSTextField Allow Only Integer Value

Hello,

Recently I was working on MAC OSX application where we have a view with some textfields. Where in few textfields where only numeric values are allowed. In this blog I will explain how to do this.

I used NSNumberFormatter for that. First you have to create a class which extends NSNumberFormatter.

Go to your .m file and add new interface.

@interface OnlyIntegerValueFormatter : NSNumberFormatter


@end

Now implement this interface in same file.

@implementation OnlyIntegerValueFormatter

- (BOOL)isPartialStringValid:(NSString*)partialString newEditingString:(NSString**)newString errorDescription:(NSString**)error
{
    if([partialString length] == 0) {
        return YES;
    }
    
    NSScanner* scanner = [NSScanner scannerWithString:partialString];
    
    if(!([scanner scanInt:0] && [scanner isAtEnd])) {
        NSBeep();
        return NO;
    }
    
    return YES;
}

@end

That's it. Now create instance of OnlyIntegerValueFormatter and assign it to NSTextField. 

OnlyIntegerValueFormatter *formatter = [[OnlyIntegerValueFormatter alloc] init];
[self.onlyIntegerTextField setFormatter:formatter];

That's it. Now if you try to type characters in the textfield, it won't allow it. 

Saturday, February 1, 2014

Create Simple MAC OSX application with WebView and Load URL in it

Hello,

In this blog post we will see how to create a simple MAC OSX application with webview, which loads a URL in webview. This types of application are very useful when you have nicely designed web application and you want to convert it to MAC OSX app. Using this approach, you don't have to create separate OSX application. To create this application , you will need Xcode. Open the Xcode and select "Create New xCode project". Select OS X -> Application - > Cocoa Application. It will show following window.

Enter product name, Organization Name, Company Identifier, Class Prefix of your choice and click on Next. It will ask for the location to save the project. Select the location and create project. Once the project is created, you will see following type of file stricture in Xcode.

It may be different for in you based on entered class prefix by you. After this open AppDelegate.h file. Add following line after line #import <Cocoa/Cocoa.h>

#import <WebKit/WebKit.h>

After this add following line after @property (assign) IBOutlet NSWindow *window;

@property (assign) IBOutlet WebView *webView;

Once done open MainMenu.xib file. Select window as displayed in following image and add webview as show in the following image.


After that we have to add IBOutlet for that Select App delegate from the right pane and Control + Drag from it to Webview. It will show context menu with Webview. Select the webview and it will add IBOutlet. See the following image.


Now we have to load URL in Webview. Open your AppDelegate.m file and find the applicationDidFinishLaunching function and add following code to it.

NSRect frame=[NSScreen mainScreen].frame ;
[self.window setFrame:frame display:YES animate:YES];

Above lines will open the window in fullscreen mode. 

NSURL *url = [NSURL URLWithString:@"http://davehiren.blogspot.com"];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
[[[self webView] mainFrame] loadRequest:urlRequest];
[self.window setContentView:self.webView];

Above lines will load the URL in webview. You can also set title of window with

[self.window setTitle:@"title"];

When you run the app you should see following screen.

That's it, your application is ready. You can change the URL the way you want.






Monday, May 13, 2013

Passing Query String With Index.html file in iOS Phonegap (Cordova)

Hello,

Imagine a scenario that you have Sencha Touch/ jQuery mobile application and you have used Phonegap (Cordova) to compile it to native iOS application.  Now your application expects some params like authentication token, or user id or anything it can be. You need to send this params along with your index.html file so that you can use it in app launch function. Have you ever faced this situation. If yes then here is the solution. Please remember this solution is specifically for iOS Cordova application.

First let's see how normally it works. When you create a Cordova based application in XCode, You have AppDelegate.h and AppDelegate.m file. Open AppDelegate.m file and find a function below.


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

In this function find the following lines of code.

self.viewController.wwwFolderName = @"www";
self.viewController.startPage = @"index.html";

This specify that web assets folder is WWW and start page is index.html. If you have some other start page you can change the name here. Now if you try to pass query string as follow, it will not work.


self.viewController.startPage = @"index.html?query1=value1";

Because it treats it as page name and there is no such page. So how to resolve this.

First of all remove the start page name from code.

self.viewController.startPage = @"";

Now we will implement NSURL interface and add some custom functions in it to handle the query string. Add following code to your AppDelegate.m file above implementation of AppDelegate

@implementation NSURL (Additions)

- (NSURL *)URLByAppendingQueryString:(NSString *)queryString {
    if (![queryString length]) {
        return self;
    }
    
    NSString *URLString = [[NSString alloc] initWithFormat:@"%@%@%@", [self absoluteString],
                           [self query] ? @"&" : @"?", queryString];
    NSURL *theURL = [NSURL URLWithString:URLString];
    [URLString release];
    return theURL;
}

@end

This function accepts query string and add it to URL and create new URL. Now add following code at end of didFinishLaunchingWithOptions function.

NSString* newQueryString = @"query1=value1&query2=value2&query3=value3";
NSURL *newurl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"index" ofType:@"html" inDirectory:@"www"]];

newurl = [newurl URLByAppendingQueryString:newQueryString];
[self.viewController.webView loadRequest:[NSURLRequest requestWithURL:newurl]];


Above code will build custom URL with query string and load the URL in iOS webview.

Hope this helps you.




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.





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.




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.