Friday, 14 November 2014

iOS 6 Grouped Style UITableView in iOS 8 App

I came across a requirement where i have to show  iOS 6 grouped style UITableView in iOS8 App.
So i did some workaround and it really works.

Steps:

  • I added a UITableView on UIViewController (Scene) in UIStoryboard.
  • I reduced the width of the UITableView and centre aligned it (eg. UIViewController width - 320px so i take 300px UITableView).
  • Add & Import QuartzCore framework.
  • In cellForRowAtIndexPath method make the cell layer corner round as per requirement and set masktobounds as shown below.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"TableCell";
    
    UITableViewCell *tableCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (tableCell == nil) {
        tableCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
      }
    
    tableCell.layer.cornerRadius = 5;
    tableCell.layer.masksToBounds = YES;
    
    [tableCell setBackgroundColor:[UIColor lightGrayColor]];
}


Wednesday, 23 April 2014

Text to Speech in iOS7

iOS7 makes Text to Speech really easy and that also in multiple languages.
iOS7 has added two new classes for Text to Speech functionality:
1. AVSpeechSynthesizer
2. AVSpeechUtterance

First Link AVFoundation and AudioToolbox Framework from Application Target -> Build Phases -> Link Binary With Libraries.

Import both frameworks in Header file (.h file)
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>

add delegate <AVSpeechSynthesizerDelegate>.

In implementation file (.m file)
    AVSpeechSynthesizer *speechSynthesizer = [[AVSpeechSynthesizer alloc] init];
    AVSpeechUtterance *speechUtterance = [AVSpeechUtterance speechUtteranceWithString:@"Welcome to iOS App."];
    speechUtterance.rate = AVSpeechUtteranceMaximumSpeechRate / 4.0f;
    speechUtterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"en-US"]; // language
    [speechSynthesizer speakUtterance: speechUtterance];

By adding the above code you can add text to speech functionality to your iOS7 app.

Display HTML in NSAttributedString in iOS7

iOS7 has really added some best things like displaying HTML in NSAttributedString.
Suppose in a case you want to display some HTML in your UITextView from any website.
Just pass the HTML to NSAttributedString and set this attributed string to the UITextView and that's it, you are done.

In below snippet i have passed the html string from my blog site to NSAttributedString and this NSAttributedString is set to UITextView.
Now the html from my blog site will be displayed in the UITextView along with clickable links.

      NSString *strHtml = [NSString stringWithContentsOfURL:[NSURL   URLWithString:@"http://objectivecwithsuraj.blogspot.in/"] encoding:NSUTF8StringEncoding error:nil];
    
    NSAttributedString *attrStrWithHtml = [[NSAttributedString alloc] 
                   initWithData:[strHtml dataUsingEncoding:NSUTF8StringEncoding]
                   options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
                                               NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)}
                   documentAttributes:nil error:nil];
    
    UITextView *textView = [[UITextView alloc] initWithFrame:self.view.frame];
    [textView setAttributedText: attrStrWithHtml];
    [textView setDataDetectorTypes:UIDataDetectorTypeLink];
    [textView setEditable:NO];
    [self.view addSubview:textView];

Wednesday, 9 April 2014

Calculating Height of UITextView in iOS7



Many times in application we need to calculate height of UITextView to make it grow and shrink according to the text contained within it.

Now many of you will say calculating height of UITextView is not a big deal.

Yes its true for iOS6 and below version. But not true from iOS7 and higher.

In iOS6 we can get height of a UITextView simply using the contentSize.

eg.

  [myTextView setText:@"here goes my text"];

  CGFloat heightOfTextView = myTextView.contentSize.height;


In iOS7 may be it's a bug or due to deprecation, contentSize does not provide you the accurate height of the UITextView.

So below is the code snippet i used in my application for getting the height of UITextView:

Just set text NSString to UITextView and call this method sending UITextView as parameter


  [myTextView setText:@"here goes my text"];

  [self measureHeightOfUITextView: myTextView];


- (CGFloat)measureHeightOfUITextView:@"here goes my text"];​(UITextView *)textView

{

    if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1)

    {

        // This is the code for iOS 7. contentSize no longer returns the correct value, so

        // we have to calculate it.

        CGRect frame = textView.bounds;

        // Take account of the padding added around the text.​

        UIEdgeInsets textContainerInsets = textView.textContainerInset;

        UIEdgeInsets contentInsets = textView.contentInset;

        CGFloat leftRightPadding = textContainerInsets.left + textContainerInsets.right + textView.textContainer.lineFragmentPadding * 2 + contentInsets.left + contentInsets.right;

        CGFloat topBottomPadding = textContainerInsets.top + textContainerInsets.bottom + contentInsets.top + contentInsets.bottom;

        frame.size.width -= leftRightPadding;

        frame.size.height -= topBottomPadding;

        NSString *textToMeasure = textView.text;

        if ([textToMeasure hasSuffix:@"\n"])

        {

            textToMeasure = [NSString stringWithFormat:@"%@-", textView.text];

        }       

        // NSString class method: boundingRectWithSize:options:attributes:context is

        // available only on ios7.0 sdk.

        NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];

        [paragraphStyle setLineBreakMode:NSLineBreakByWordWrapping];

        NSDictionary *attributes = @{ NSFontAttributeName: textView.font, NSParagraphStyleAttributeName : paragraphStyle };

        CGRect size = [textToMeasure boundingRectWithSize:CGSizeMake(CGRectGetWidth(frame), MAXFLOAT)

                                                  options:NSStringDrawingUsesLineFragmentOrigin

                                               attributes:attributes

                                                  context:nil];

        CGFloat measuredHeight = ceilf(CGRectGetHeight(size) + topBottomPadding);

        return measuredHeight;

    }

    else

    {

        return textView.contentSize.height;

    }

}



I got help from below links for above code snippet:
Link 1
Link 2

Wednesday, 26 March 2014

Hyperlinks in iOS App by detecting Links, Phone Number, Address and CalendarEvent in UITextView


In many scenarios we have to detect Links(URLs) and Phone Numbers from given NSString and make it similar to hyperlink in HTML.
Suppose you have a string with multiple url's and you want to make it clickable, so as to be able to open it in Safari browser.

In below code i have taken a string which consists of two url's and i want to make it clickable.
So i take a UITextView "txtViewClickableHyperLink" and assigned the NSString "strText" to it.
Now i have used a magic line to setDataDetector of UITextView, this line has power to recognise my url.
By changing type of DataDetector we can recognise:
UIDataDetectorTypeLink                  - URL (Link)
UIDataDetectorTypePhoneNumber   - Phone Number
UIDataDetectorTypeAddress             - Address
UIDataDetectorTypeCalendarEvent   - Calendar Event
UIDataDetectorTypeAll                      - Detects all types

NSString *strText = [NSString stringWithFormat: @"Hey this is my blog: http://objectivecwithsuraj.blogspot.in/ which is hosted on https://www.blogger.com/. This is my blog here is share my experiences with all of you. So it can help others as well as me in future. "];
UITextView *txtViewClickableHyperLink = [[UITextView alloc] init];
[txtViewClickableHyperLink setFrame:CGRectMake(X_COORDINATE, Y_COORDINATE, WIDTH, HEIGHT)];
[txtViewClickableHyperLink setEditable:NO];
[txtViewClickableHyperLink setDataDetectorTypes:UIDataDetectorTypeLink];
[txtViewClickableHyperLink setText:strText];
[self.view addSubview:txtViewClickableHyperLink];

Now we can detect url, phone number , address and make it clickable using a UITextView in iPhone App.

Sample Code : HERE








Friday, 21 March 2014

UIPanGestureRecognizer to create Screen similar to Notification Center


In my application i wanted a Screen similar to the Notification Center of the iPhone.

Where user can navigate through 3 screens using swipe gesture and also by selecting the UISegmentedControl index.

So i have figured out below code.

 

-(void)viewDidLoad {

[super viewDidLoad];

 

// I have added a ScrollView on Xib file of width 960px here.

// added content(UIControls) required on first screen at 0px to 320px.

// added content(UIControls) required on second screen at 320px to 640px.

// added content(UIControls) required on third screen at 640px to 960px.

 

// I have added a UISegmentedControl on Xib file and set an action: - (IBAction)changeTab:(id)sender

 

// Add PangestureRecognizer to view

   UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(swipeThroughTabs:)];

   [panRecognizer setMinimumNumberOfTouches:1];

   [panRecognizer setMaximumNumberOfTouches:1];

   [panRecognizer setDelegate:self];

   [self.view addGestureRecognizer:panRecognizer];

}

 

 

-(void)swipeThroughTabs:(id)sender {

   [[[(UITapGestureRecognizer*)sender view] layer] removeAllAnimations];

   

   CGPoint velocity = [(UIPanGestureRecognizer*)sender velocityInView:[sender view]];

   

   if ([(UIPanGestureRecognizer*)sender state] == UIGestureRecognizerStateEnded) {

 

       if (velocity.x < 0) {

           if (segmentedControlForTab.selectedSegmentIndex == 0) {

               [self changeTabManually:1];

           } else if (segmentedControlForTab.selectedSegmentIndex == 1) {

               [self changeTabManually:2];

           }

       } else {

           if (segmentedControlForTab.selectedSegmentIndex == 2) {

               [self changeTabManually:1];

           } else if (segmentedControlForTab.selectedSegmentIndex == 1) {

               [self changeTabManually:0];

           }

       }

       

   }

}

 

-(void)changeTabManually:(NSInteger )selectedIndex {

   [self hideKeyboard];

   [UIView animateWithDuration:0.5

                         delay:0.0

                       options:UIViewAnimationOptionCurveEaseIn

                    animations:^ {

                        [segmentedControlForTab setSelectedSegmentIndex:selectedIndex];

                        CGRect frameOfScrollView = scrollViewSettingPage.frame;

                        switch (selectedIndex) {

                            case 0: {

                                frameOfScrollView.origin.x = 0;

                                scrollViewSettingPage.frame = frameOfScrollView;

                            }

                                break;

                               

                            case 1: {

                                frameOfScrollView.origin.x = -320;

                                scrollViewSettingPage.frame = frameOfScrollView;

                            }

                                break;

                               

                            case 2: {

                                frameOfScrollView.origin.x = -640;

                                scrollViewSettingPage.frame = frameOfScrollView;

                            }

                                break;

                               

                            default:

                                break;

                        }

                    }

                    completion:^(BOOL finished) {

                    }];

}

 

- (IBAction)changeTab:(id)sender {

   [self hideKeyboard];

   UISegmentedControl *segmentedControl = (id)sender;

   

   [UIView animateWithDuration:0.5

                         delay:0.0

                       options:UIViewAnimationOptionCurveEaseIn

                    animations:^ {

                        CGRect frameOfScrollView = scrollViewSettingPage.frame;

                        switch (segmentedControl.selectedSegmentIndex) {

                            case 0: {

                                frameOfScrollView.origin.x = 0;

                                scrollViewSettingPage.frame = frameOfScrollView;

                            }

                                break;

                               

                            case 1: {

                                frameOfScrollView.origin.x = -320;

                                scrollViewSettingPage.frame = frameOfScrollView;

                            }

                                break;

                               

                            case 2: {

                                frameOfScrollView.origin.x = -640;

                                scrollViewSettingPage.frame = frameOfScrollView;

                            }

                                break;

                               

                            default:

                                break;

                        }

                    }

                    completion:^(BOOL finished) {

                    }];

}




Wednesday, 12 February 2014

Placeholder text in UITextView iOS


As we all know UITextView does not have a Placeholder unlike a UITextField.
So it is really a painful job to have a placeholder text in a UITextView.
I got stuck on the same issue and did some work around.
This trick will make you think that you are actually having a placeholder text inside a UITextView.

So here we go:
1. Place a UILabel on nib (xib) file at the position where you want UITextView.
2. Now set text what you want on the placeholder and change it text color to lightGraycolor.
3. Take a UITextView and place it above the UILabel and set its background color to clearcolor.
4. It will appear as a placeholder inside UITextView.
5. Set delegates of UITextView as follows:

In Header file:
#import <UIKit/UIKit.h>


@interface LoginViewController : UIViewController <UITextViewDelegate>

@end

In Implementation file:

- (void)viewDidLoad {

    [super viewDidLoad];
    [myTextView setDelegate:self];
}

6. Take textViewDidChange delegate method of UITextView.
6. Here set UILabel to hidden YES and set background color of UITextView to white when UITextView does not have text.
7. And in else condition set UILabel to hidden NO and set background color of UITextView to clearcolor when UITextView.

- (void)textViewDidChange:(UITextView *)textView {
    // Enable and disable lblPlaceHolderText
    if ([textView.text length] > 0) {
        [textView setBackgroundColor:[UIColor whiteColor]];
        [lblPlaceHolderText setHidden:YES];
    } else {
        [textView setBackgroundColor:[UIColor clearColor]];
        [lblPlaceHolderText setHidden:NO];
    }
}


Sample code: HERE



Image: Before typing the placeholder is visible.






Image: After typing placeholder hides.



Thursday, 23 January 2014

Unique Identifier UUID - iOS

UDID is a hash value composed from various hardware identifiers such as the device serial number.
It is unique for each device. 
The UDID is independent of the device name, SIM card.
It is really easy to get it from the device programmatically:

NSString *uuid =  [[UIDevice currentDevice] uniqueIdentifier];

But UDID is no longer available onwards iOS 6 due to security / privacy reasons.
UDID is deprecated by Apple; developers can not make use of it in any iOS application.


So what now? How can we identify the device?

I found many solutions on searching over internet like identifierForVendor or advertisingIdentifier or generate our own UUID (store & persist it on your own).

Many examples show how to generate UUID and store it in NSUserDefault:

-(NSString *)generateUUID {
    NSString *CFUUID = nil;
    if ([[NSUserDefaults standardUserDefaults] valueForKey:@"UUID"] == nil) {
        CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
        CFUUID = (NSString *)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, uuid));
        [[NSUserDefaults standardUserDefaults] setValue:CFUUID forKey:@"UUID"];
        [[NSUserDefaults standardUserDefaults] synchronize];
    } else {
        CFUUID = [[NSUserDefaults standardUserDefaults] valueForKey:@"UUID"];
    }

But if you store the UUID in NSUserDefaults you would loose it as soon as the app gets uninstalled from the device.
So the better idea is to store it in Keychain. In case the app is uninstalled Keychain will preserve the UUID and on next installation we can fetch it from there.

Create & Store UUID in Keychain: Source Code


Friday, 10 January 2014

Upload Multiple Images/Photos to Server Using MulipartFormatData via AFNetworking

Below is the code snippet which would help in uploading multiple Images along with Text to Server:


-(void)uploadMultipleImagesWithTextMessageUsingAFNetworkingMultipartFormat:(id)sender {
    // Upload multiple images to server using multipartformatdata (AFNetworking)
   
    NSString *stringMessage = @"I am uploading multiple images to server";  
   
    AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL: [NSURL URLWithString:BASEURL]]; // replace BASEURL
    client.parameterEncoding = AFJSONParameterEncoding;

    NSMutableURLRequest *request = [client multipartFormRequestWithMethod:@"POST" path:@"/PostMultImagesWithTextAPI" parameters:nil constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
       
        [formData appendPartWithFormData:[[[NSUserDefaults standardUserDefaults] objectForKey:KServerAccessToken] dataUsingEncoding:NSUTF8StringEncoding] name:@"AccessToken"];
       
        [formData appendPartWithFormData:[stringMessage dataUsingEncoding:NSUTF8StringEncoding] name:@"PostText"];
       
        // arrayChosenImages is NSArray of UIImage to be uploaded
        for (int i=0; i<[arrayChosenImages count]; i++) {
            [formData appendPartWithFileData:UIImageJPEGRepresentation([arrayChosenImages objectAtIndex:i], 0.5)
                                        name:[NSString stringWithFormat:@"image%d",i]
                                    fileName:[NSString stringWithFormat:@"image%d.jpg",i]
                                    mimeType:@"image/jpeg"];
        }
    }];
   
    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    [operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
        float uploadPercentge = (float)totalBytesWritten / (float)totalBytesExpectedToWrite;
        float uploadActualPercentage = uploadPercentge * 100;
        NSLog(@"Sent %lld of %lld bytes", totalBytesWritten, totalBytesExpectedToWrite);
        NSLog(@"Multipartdata upload in progress: %@",[NSString stringWithFormat:@"%.2f %%",uploadActualPercentage]);
        if (uploadActualPercentage >= 100) {
            NSLog(@"Waitting for response ...");
        }
        progressBar.progress = uploadPercentge; //  progressBar is UIProgressView to show upload progress
    }];
    [client enqueueHTTPRequestOperation:operation];
   
    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSData *dataResponseJSON = [operation.responseString dataUsingEncoding:NSUTF8StringEncoding];
        NSDictionary *dictResponseJSON = [NSJSONSerialization JSONObjectWithData:dataResponseJSON options:NSJSONReadingMutableContainers error:nil];
        NSLog(@"PostMultImagesWithTextAPI API Response: %@", dictResponseJSON);      
    }
     failure:^(AFHTTPRequestOperation *operation, NSError *error) {
         NSLog(@"PostMultImagesWithTextAPI API failed with error: %@", operation.responseString);
     }];
    [operation start];
}

Hope it helps you...