Wednesday 31 December 2014

iOS 8 UITableView separator inset not working

There are two ways to handle this.



Method 1: Override the margin property in the cell

-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Remove seperator inset
    if ([cell respondsToSelector:@selector(setSeparatorInset:)]) {
           [cell setSeparatorInset:UIEdgeInsetsZero];
    }

    // Prevent the cell from inheriting the Table View's margin property
    if ([cell respondsToSelector:@selector(setPreservesSuperviewLayoutMargins:)]) {
        [cell setPreservesSuperviewLayoutMargins:NO];
    }

    // cell's layout margins
    if ([cell respondsToSelector:@selector(setLayoutMargins:)]) {
        [cell setLayoutMargins:UIEdgeInsetsZero];
    }
}



Method2: Override the tableview creation 

-(void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];

    // add margins to tableview
    if ([self.tableView respondsToSelector:@selector(setSeparatorInset:)]) {
        [self.tableView setSeparatorInset:UIEdgeInsetsZero];
    }

    if ([self.tableView respondsToSelector:@selector(setLayoutMargins:)]) {
        [self.tableView setLayoutMargins:UIEdgeInsetsZero];
    }
} 

Friday 28 November 2014

UIColor generate random color. Using methods colorWithHue: saturation: brightness: alpha: and colorWithRed: green: blue: alpha:


+(UIColor*)getRandomColor{
    CGFloat hue = ( arc4random() % 256 / 256.0 );  //  0.0 to 1.0
    CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5;  //  0.5 to 1.0, away from white
    CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5;  //  0.5 to 1.0, away from black
    UIColor *color = [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1];
    return color;
}
+(UIColor*)getRandomColorUsingRGB{
  
    CGFloat redLevel    = rand() / (float) RAND_MAX;
    CGFloat greenLevel  = rand() / (float) RAND_MAX;
    CGFloat blueLevel   = rand() / (float) RAND_MAX;
    
    UIColor *color = [UIColor colorWithRed: redLevel
                                     green: greenLevel
                                      blue: blueLevel
                                     alpha: 1.0];
    return color;
}

Thursday 14 August 2014

MultiPart Image Upload. Sequencial UIImage mutlipart upload.

Code from Github

In this the UIImage is converted into in NSData
Data is divided into chunks.
Chucks are send to server sequentially , 
if failed to upload same chuck is resend,
else if successful next chuck is sent server.

//HDMultiPartImageUpload.h file----------------

Add these file to your project.

//================////////////////////.h file /////////////////////////=============

//
//  HDMultiPartImageUpload.h
//  HDMultiPartImageUpload
//
//  Created by HDDEV 2014.
//  Copyright (c) 2014 HDDev. All rights reserved.
//
//
//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.
//



#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>



//static const int oneChunkSize = (1024 * 4);

#define BOUNDARY @"--ARCFormBoundaryb6kap934u6jemi"

#define uploadingImageMappingKeyOnServer @"file" //Use Same key string which your read as input for image filename at your server implementation

typedef enum : NSUInteger {
    eImageTypePNG,
    eImageTypeJPG,
} eImageType;



@interface HDMultiPartImageUpload : NSObject

@property (nonatomic,assign) int                  oneChunkSize;
@property (nonatomic,assign) eImageType           selectedImageType;
@property (nonatomic,strong) NSString            *imageFilePath;
@property (nonatomic,strong) NSString            *uploadURLString;
@property (nonatomic,strong) NSMutableDictionary *postParametersDict;

-(void)startUploadImagesToServer;

@end

//================////////////////////.h file /////////////////////////=============








//   HDMultiPartImageUpload.m file-------------------

Also the below the file below

//================////////////////////.m file /////////////////////////=============

//
//  HDMultiPartImageUpload.m
//  HDMultiPartImageUpload
//
//  Created by HarshDuggal on 13/08/14.
//  Copyright (c) 2014 HDDev. All rights reserved.
//

#import "HDMultiPartImageUpload.h"




@interface HDMultiPartImageUpload ()
{
    int totalChunksTobeUploaded;
    int chunksUploadedSuccessfully;
}
-(void)uploadImageChunkToServerFullImageData:(NSData*)imageData withParam:(NSMutableDictionary*)param withOffset:(NSUInteger)offset;

@end

@implementation HDMultiPartImageUpload


- (NSData*)getPostDataFromDictionary:(NSDictionary*)dict
{
    id boundary = BOUNDARY;
    NSArray* keys = [dict allKeys];
    NSMutableData* result = [NSMutableData data];
    
    // add params (all params are strings)
    [result appendData:[[NSString stringWithFormat:@"\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];

    for (int i = 0; i < [keys count]; i++)
    {
        id tmpKey = [keys objectAtIndex:i];
        id tmpValue = [dict valueForKey: tmpKey];
        
        [result appendData: [[NSString stringWithFormat:@"Content-Disposition: form-data; name=%@\r\n\r\n \n%@", tmpKey,tmpValue] dataUsingEncoding:NSUTF8StringEncoding]];
        // Append boundary after every key-value
        [result appendData:[[NSString stringWithFormat:@"\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    }

    return result;
}



-(void)startUploadImagesToServer
{
    
#warning these variable shud be set properly before starting upload
//    // Set the following parameter and start upload---
//    self.oneChunkSize;
//    self.selectedImageType;
//    self.imageFilePath;
//    self.uploadURLString;
//    self.postParametersDict;
#warning these variable shud be set properly before starting upload
    
    UIImage *imageTobeUploaded = [UIImage imageWithContentsOfFile:self.imageFilePath];
    
    NSData *imageData;
    NSString *fileType;
    
    if (self.selectedImageType == eImageTypeJPG){
        imageData = UIImageJPEGRepresentation(imageTobeUploaded, 1.0);
        fileType = @"image/jpg";
    }
    else if (self.selectedImageType == eImageTypePNG) {
        imageData = UIImagePNGRepresentation(imageTobeUploaded);
        fileType = @"image/png";
    }
    
    
//    unsigned long long totalFileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil] fileSize];
    
    
    NSUInteger totalFileSize = [imageData length];
    //    int totalChunks = ceil(totalFileSize/oneChunkSize);
    int totalChunks = round((totalFileSize/self.oneChunkSize)+0.5);//round-off to nearest  largest valua 1.01 is considered as 2
    

    // Start multipart upload chunk sequentially-
    NSUInteger offset = 0;
    totalChunksTobeUploaded = totalChunks;
    chunksUploadedSuccessfully = 0;
    [self uploadImageChunkToServerFullImageData:imageData withParam:self.postParametersDict withOffset:offset];
    
}







-(void)uploadImageChunkToServerFullImageData:(NSData*)imageData withParam:(NSMutableDictionary*)param withOffset:(NSUInteger)offset
{
    
    //    NSData* myBlob = imageData;
    NSUInteger totalBlobLength = [imageData length];
    NSUInteger chunkSize = self.oneChunkSize;
    NSUInteger thisChunkSize = totalBlobLength - offset > chunkSize ? chunkSize : totalBlobLength - offset;
    NSData* chunk = [NSData dataWithBytesNoCopy:(char *)[imageData bytes] + offset
                                         length:thisChunkSize
                                   freeWhenDone:NO];
    // upload the chunk
    
    
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:self.uploadURLString]];
    
    [request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
    [request setHTTPShouldHandleCookies:NO];
    [request setTimeoutInterval:60];
    [request setHTTPMethod:@"POST"];
    
    NSString *boundary = BOUNDARY;
    //
    //    // set Content-Type in HTTP header
    NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
    [request setValue:contentType forHTTPHeaderField: @"Content-Type"];
    //
    // post body
    NSMutableData *body = [[NSMutableData alloc]init];
    
    // add params (all params are strings)
    [body appendData:[self getPostDataFromDictionary:param]];
    
    // add image data
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=%@; filename=blob\r\n", @"file"] dataUsingEncoding:NSUTF8StringEncoding]];
    
    [body appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    
    [body appendData:chunk];
    [body appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
    
    [body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    

    // setting the body of the post to the reqeust
    NSString * bodyString =[[NSString alloc]initWithData:body encoding:NSASCIIStringEncoding];
    NSLog(@"body sent to server: \n %@ \n",bodyString);
    
    [request setHTTPBody:body];
    
    // set the content-length
    //    NSString *postLength = [NSString stringWithFormat:@"%d", [body length]];
    //    [request setValue:postLength forHTTPHeaderField:@"Content-Length"];
    
    
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue currentQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        
        NSLog(@"response:\n %@, \n error = %@",response,error.localizedDescription);
        if (error) { // Failed
            NSLog(@"error = %@",error.localizedDescription);
            
            // Retry resending same chuck
            [self uploadImageChunkToServerFullImageData:imageData withParam:param withOffset:offset];
            return;
        }
        else if(data.length > 0) {// Data recieved from server
            
#warning parse the revieved data from server accordingly
//                NSString* newStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
//                NSLog(@"MsgFromServer:%@ \n Response: %@ \n ",newStr,response);
//                // Convert String to dict
//                NSError* parsingError;
//                NSDictionary *responseRxDict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&parsingError];
//                
//                if (parsingError) {
//                    NSLog(@"%@",parsingError.localizedDescription);
//                    // Retry resending same chuck
//                    [self uploadImageChunkToServerFullImageData:imageData withParam:param withOffset:offset];
//                    return;
//                }
#warning parse the revieved data from server accordingly
            
            //check success
            NSLog(@"Offset:%lu totalLenght:%lu",(unsigned long)offset,(unsigned long)totalBlobLength);
            NSLog(@"Chunk:%d Total Chunks:%d",chunksUploadedSuccessfully,totalChunksTobeUploaded);
            
            BOOL successfulUpload = YES; // Check success msg from server in "responseRxDict" .
            if (successfulUpload) {
                chunksUploadedSuccessfully += 1;

#warning update your post param dict if needed, accoording to server implementation
                [param setObject:[NSString stringWithFormat:@"%d",chunksUploadedSuccessfully] forKey:@"chunk"];
// above line is example should altered according to the server key in needed
#warning update your post param dict if needed, accoording to server implementation
                
                
                    NSUInteger thisChunkSize = totalBlobLength - offset > chunkSize ? chunkSize : totalBlobLength - offset;
                    NSUInteger newOffset= offset + thisChunkSize;
                    
                    // stop no more data to upload
                    if(offset >= totalBlobLength){
                        NSLog(@"offset >= totalBlobLength");
                        return;
                    }
                    if (chunksUploadedSuccessfully > totalChunksTobeUploaded-1) {
                        NSLog(@"chunk > chunks-1");
                        return;
                    }
                    
                    // send next Chunck To server
                    [self uploadImageChunkToServerFullImageData:imageData withParam:param withOffset:newOffset];
                }
                else { // Retry resending same chuck
                    [self uploadImageChunkToServerFullImageData:imageData withParam:param withOffset:offset];
                    return;
                }
            }
            else { // Retry resending same chuck
                [self uploadImageChunkToServerFullImageData:imageData withParam:param withOffset:offset];
                return;
            }
    }];
}

//================////////////////////.m file /////////////////////////=============







Usage Example:-

Import the above two files "HDMultiPartImageUpload.h" and "HDMultiPartImageUpload.m" to your project and write following code to start multipart upload.


#Import "HDMultiPartImageUpload.h"


-(void)demoupload
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    documentsDirectory = [NSString stringWithFormat:@"%@/ProfilePic/",documentsDirectory];
    NSFileManager*fmanager = [NSFileManager defaultManager];
    if(![fmanager fileExistsAtPath:documentsDirectory])
    {
        [fmanager createDirectoryAtPath:documentsDirectory withIntermediateDirectories:YES attributes:nil error:nil];
    }
    NSString * filePath =  [NSString stringWithFormat:@"%@",documentsDirectory];

    NSMutableDictionary *postParam = [[NSMutableDictionary alloc]init];
    [postParam addEntriesFromDictionary:[self demoPostDict]];
    
    HDMultiPartImageUpload *obj = [[HDMultiPartImageUpload alloc]init];

    obj.oneChunkSize = 1024 *10;
    obj.selectedImageType = eImageTypePNG;
    obj.imageFilePath =filePath;
    obj.uploadURLString = @"http://example.com/upload";
    obj.postParametersDict = postParam;
    
    [obj startUploadImagesToServer];
    
}

-(NSMutableDictionary*)demoPostDict
{
    NSMutableDictionary *param = [[NSMutableDictionary alloc]init];
    
#warning - These key values in post dictionary varies according to the server implementation----
    UIImage *imageTobeUploaded = [UIImage imageWithContentsOfFile:self.imageFilePath];
    
    NSData *imageData;
    NSString *fileType;
    
    if (self.selectedImageType == eImageTypeJPG){
        imageData = UIImageJPEGRepresentation(imageTobeUploaded, 1.0);
        fileType = @"image/jpg";
    }
    else if (self.selectedImageType == eImageTypePNG) {
        imageData = UIImagePNGRepresentation(imageTobeUploaded);
        fileType = @"image/png";
    }
    
    NSUInteger totalFileSize = [imageData length];
    //    int totalChunks = ceil(totalFileSize/oneChunkSize);
    int totalChunks = round((totalFileSize/self.oneChunkSize)+0.5);//round-off to nearest  largest valua 1.01 is considered as 2
    
    // Create your Post parameter dict according to server
    NSString* originalFilename = @"tmpImageToUpload.png";//uniqueFileName;
    
    //Creating a unique file to upload to server
    NSString *prefixString = @"Album";
    //    This method generates a new string each time it is invoked, so it also uses a counter to guarantee that strings created from the same process are unique.
    NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString] ;//
    NSString *uniqueFileName = [NSString stringWithFormat:@"%@_%@", prefixString, guid];
    
    //Add key values your post param Dict
    [param setObject:uniqueFileName
              forKey:@"uniqueFilename"];
    [param setObject:[NSString stringWithFormat:@"%lu",(unsigned long)totalFileSize]
              forKey:@"totalFileSize"];
    [param setObject:@"0" forKey:@"chunk"];
    [param setObject:[NSString stringWithFormat:@"%d",totalChunks]
              forKey:@"chunks"];
    [param setObject:fileType
              forKey:@"fileType"];
    [param setObject:originalFilename
              forKey:@"originalFilename"];
    
#warning - These key values in post dictionary varies according to the server implementation----
    return param;
    
}


//  Copyright (c) HDDev. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.



Read More: 

Tuesday 12 August 2014

Convert NSData to NSDictionary or Convert NSData to NSArray.

-(void)parseDataFromServer:(NSData*)responseData 
{
NSError* error;
// If response JSON starts with {}, it represents dictionary
NSDictionary *dicData = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
// If response JSON starts with [], it represents array
NSArray* arrData = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error]; 

if (error) NSLog(@"%@",error.localizedDescription);
}
Class: NSJSONSerialization
Method: + (NSData *)dataWithJSONObject:(id)obj
options:(NSJSONWritingOptions)opt  
error:(NSError **)error;
Reference Links: Apple Doc

Tuesday 22 July 2014

Collections of Objects: NSSet v/s NSArray v/s NSDictionary

Collection is a Foundation framework class used for storing and managing groups of objects.

Art/collections_intro_2x.png

NSSet v/s NSArray v/s NSDictionary

Arrays are ordered collections of any sort of object.

Dictionaries manage pairs of keys and values. A key-value pair within a dictionary is called an entry, the keys are unique.

Sets (such as NSSet , NSMutableSet, and NSCountedSet) are unordered collections of objects. 
Sets allow for fast insertion and deletion operations. 
They also allow you to quickly see whether an object is in a collection. 



NSSet and NSMutableSet store collections of distinct objects, 
while NSCountedSet stores a collection of non-distinct objects.
For example, suppose you have a number of city objects and you want to visit each one only once.
If you store each city that you visit in a set, you can quickly and easily see whether you have visited it.


An NSSet object manages an immutable set of distinct objects—that is, after you create the set, you cannot add, remove, or replace objects, but can modify individual objects themselves (if they support modification). The mutability of the collection does not affect the mutability of the objects inside the collection.
NSMutableSet, a subclass of NSSet, is a mutable set of distinct objects, which allows the addition and deletion of entries at any time, automatically allocating memory as needed.
NSCountedSet, a subclass of NSMutableSet, is a mutable set to which you can add a particular object more than once; in other words, the elements of the set aren’t necessarily distinct. A counted set(a bag)  keeps a counter associated with each distinct object inserted.
There is only one instance of an object in a counted set, even if the object has been added multiple times. 


The NSHashTable class is configured by default to hold objects much like NSMutableSet does. It also allows additional storage options that you can tailor for specific cases.
For example, the map table in Figure below is configured to hold weak references to its elements. You can also specify whether you want to copy objects entered into the set.
Figure   Hash table object ownership
You can use an NSHashTable object when you want an unordered collection of elements that uses weak references. For example, suppose you have a global hash table that contains some objects. Because global objects are never collected, none of its contents can be deallocated unless they are held weakly. Hash tables configured to hold objects weakly do not own their contents. If there are no strong references to objects within such a hash table, those objects are deallocated. For example, the hash table in Figure above holds weak references to its contents. Objects A, C, and Z will be deallocated, but the rest of the objects remain.
Usage: 
- Use an immutable set if the set rarely changes, or changes wholesale.
- Use a mutable set if the set changes incrementally, or is very large—as large collections take more time to initialize.
- Use counted set if one instance of an object in a set, has to added multiple times. 




@interface NSMutableSet (NSExtendedMutableSet)
- (void)addObjectsFromArray:(NSArray *)array;
- (void)intersectSet:(NSSet *)otherSet;
- (void)minusSet:(NSSet *)otherSet;
- (void)removeAllObjects;
- (void)unionSet:(NSSet *)otherSet;
- (void)setSet:(NSSet *)otherSet;


Eg:
    NSMutableSet *set1 = [[NSMutableSet alloc]initWithObjects:@"1",@"3",@"2", nil];
    NSLog(@"set1 before : %@",set1);

    NSMutableSet *set2 = [[NSMutableSet alloc]initWithObjects:@"1", nil];
    [set1 minusSet:set2];
    
    NSLog(@"set1 after deletion : %@",set1);

Output:-------
 set1 before : {(
    1,
    3,
    2
)}
set1 after deletion : {(
    3,
    2

)}





NSOrderedSet is available in iOS 5+ so with that the main difference becomes whether you want duplicate objects in the data structure.
Read More of NSOrderedSet