Wednesday, July 24, 2013

Setting and Testing localizations in iOS 6

To Set:
http://stackoverflow.com/questions/5349066/how-to-localize-my-app-with-xcode-4

7) in your code use  NSLocalizedString(@"TEST", @"test") where the first arg is the string and the second arg is the description (optional can be nil)
1) Create strings file
2) call it "Localizable.strings"
3) fill the localizable.string with all the english terms you use in the app "TEST" = "TEST";
4) file inspector "Make Localized"
5) + add localization you want
6) add only the localizable.strings file to the list
8) translate and smile

To Test:
http://useyourloaf.com/blog/2013/07/22/using-launch-arguments-to-test-localizations.html

1) go into the current Scheme
2) goto arguments
3) add "
-AppleLanguages (Russian)" without quotes
4) click on and off to test the various languages and smile

Thursday, July 11, 2013

How to create custom map icons in uimapview in iOS 6 with ARC

For this example, we will assume you are making a real estate app since most people that want this are. It looks a little confusing at first but once you get over the scary factor, it is very easy.

You will need;

-an MKAnnotation object (this holds all the details about each object so it is easier to find)
-an MKAnnotationview object (this holds all the unique view details about the object)
-an MKMapView to put it on (this is the map itself)

we will call our annotation PropertyUnit and our annotation view called PropertyUnitView.

We have created an object called "PropertyListing" with the actual details of our property. We are linking one to one for each unit.
in PropertyUnit.h;
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#import "PropertyListing.h"

@interface PropertyUnit : NSObject <MKAnnotation>{
    
}

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *address;
@property (nonatomic, assign) CLLocationCoordinate2D theCoordinate;
@property (nonatomic, strong) PropertyListing *property;
//propertyListing is just or actual details for the object and can be anything 


- (id)initWithName:(NSString*)name address:(NSString*)address coordinate:(CLLocationCoordinate2D)coordinate;
- (MKMapItem*)mapItem;


@end

we need to assign information when someone clicks on the item in the map, they are commonly set as "title" and "subtitle" we are just assigning our own information to these texts.
in PropertyUnit.m;
#import "PropertyUnit.h"
#import <AddressBook/AddressBook.h>


@implementation PropertyUnit

- (id)initWithName:(NSString*)name address:(NSString*)address coordinate:(CLLocationCoordinate2D)coordinate {
    if ((self = [super init])) {
        if ([name isKindOfClass:[NSString class]]) {
            self.name = name;
        } else {
            self.name = @"Unknown charge";
        }
        self.address = address;
        self.theCoordinate = coordinate;
        
    }
    return self;
}

- (NSString *)title {
    return _address;
}

- (NSString *)subtitle {
    return [NSNumberFormatter localizedStringFromNumber:_property.price numberStyle:NSNumberFormatterCurrencyStyle];
}

- (CLLocationCoordinate2D)coordinate {
    return _theCoordinate;
}

- (MKMapItem*)mapItem {
    NSDictionary *addressDict = @{(NSString*)kABPersonAddressStreetKey : _address};
    
    MKPlacemark *placemark = [[MKPlacemark alloc]
                              initWithCoordinate:self.coordinate
                              addressDictionary:addressDict];
    
    MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
    mapItem.name = self.title;
    
    return mapItem;
}

@end

The views are very basic and supposedly don't even really need to exist because most of the custom details are done in the main mapview but I left it here for consistancey sake. 
In PropertyUnitView.h;

#import <MapKit/MapKit.h>
#import "PropertyListing.h"

@interface PropertyUnitView : MKAnnotationView

@property (nonatomic, strong) PropertyListing *property;

@end

In PropertyUnivewView.m;
#import "PropertyUnitView.h"

@implementation PropertyUnitView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}

@end

Now for the real meat and potatoes.
In MapViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import "PropertyListing+methods.h"
#import "PropertyUnit.h"
#import "PropertyUnitView.h"
#import "PropertyInfoViewController.h"
#import "ScrollNavigationViewController.h"
#import "SearchViewController.h"
#define METERS_PER_MILE 1609.344

@interface MapViewController : UIViewController <MKMapViewDelegate> {
}

@property(nonatomic,retain) MKMapView *mapView;

@end

in MapViewController.m

@implementation MapViewController
-(id)init{
    self = [super init];
    if (self){
        //create the mapview
        _mapView = [[MKMapView alloc] init];
        [self.view addSubview:_mapView];
        if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone){
            //iphone
            _mapView.frame = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y-20, self.view.frame.size.width, self.view.frame.size.height+27);
        }else{
            //ipad
            _mapView.frame = CGRectMake(self.view.frame.origin.x-20, self.view.frame.origin.y, 1024, self.view.frame.size.height-90);
        }
        _mapView.delegate = self;
    }
        return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    _mapView.showsUserLocation = YES;
    [self setDefaultTorontoView];
}

-(void)setDefaultTorontoView{
    //center on the office and create a zoom level to go by. 
    CLLocationCoordinate2D zoomLocation;
    zoomLocation.latitude = 43.662431;
    zoomLocation.longitude= -79.383667;
    
    MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(zoomLocation, 120000, 120000);
    
    [_mapView setRegion:viewRegion animated:YES];
}

#pragma mark - unit detail methods

- (NSInteger)numberOfUnits
{
    id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][0];
    return [sectionInfo numberOfObjects];
}

-(void)printframe:(CGRect)frame{
    //just a way to print off the frames if needed
    NSLog(@"current x:%.0f, y:%.0f, length:%.0f, height:%.0f",frame.origin.x, frame.origin.y,frame.size.width,frame.size.height);
}

#pragma mark - map delegate

- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views{
    //called Tells the delegate that one or more annotation views were added to the map.
    if (existingProperties == nil){
        existingProperties = [[NSMutableArray alloc] init];
    }
    for (PropertyUnitView *annView in views)
    {
        if (![existingProperties containsObject:annView]){
            CGRect endFrame = annView.frame;  
            annView.frame = CGRectMake(endFrame.origin.x, endFrame.origin.y + endFrame.size.height, endFrame.size.width, 0);
            //NSLog(@"Start point");
            //[self printframe:annView.frame];
            //[self printframe:endFrame];
            [UIView animateWithDuration:0.5 animations:^{ annView.frame = endFrame; }];
            [existingProperties addObject:annView];
        }
    }
}

- (PropertyUnitView *)mapView:(MKMapView *)map viewForAnnotation:(id <MKAnnotation>)annotation {
    static NSString *annotationViewReuseIdentifier = @"annotationViewReuseIdentifier";
    PropertyUnitView *annotationView = (PropertyUnitView *)[_mapView dequeueReusableAnnotationViewWithIdentifier:annotationViewReuseIdentifier];
    if (annotationView == nil)
    {
        annotationView = [[PropertyUnitView alloc] initWithAnnotation:annotation reuseIdentifier:annotationViewReuseIdentifier];
    }
    if ([annotation isMemberOfClass:[PropertyUnit class]]){
        //if the annotation is a property 
        annotationView.image = [UIImage imageNamed:@"house20.png"];
        annotationView.draggable = FALSE;
        annotationView.canShowCallout = YES;
        UIButton *goToDetail = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
        annotationView.rightCalloutAccessoryView = goToDetail;
    } else {
        //it is probably the MKUserLocation
        if ([annotation isKindOfClass:[MKUserLocation class]]) {
        }
    }
    annotationView.annotation = annotation;
    return annotationView;
}

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{
    PropertyUnit *unit = (PropertyUnit*)view.annotation;
    PropertyListing *listing = unit.property;
    PropertyInfoViewController *infoView = [[PropertyInfoViewController alloc] initWithPropertyListing:listing]; 
    [self.navigationController pushViewController:infoView animated:YES];
}
@end

How to create a subview that will be a popup on large devices and full screen on small ones in iOS 6 with ARC

Universal apps are becoming more and more necessary today. They are becoming more complicated as well. At least they don't hundreds of different size screens to deal with like android.

First you should have a subview that is the size of a standard smaller screen (itouch, iphone). We will call this "SearchViewController".  You might want to use a delegate to interact with the views but we won't worry about that in this exercise.

in the SearchViewController.m;

-(void) closeView{
    //all keyboard items should have "resignFirstResponder" attached to it here just in case
    [self.view removeFromSuperview];
}

in the MainViewController.h;

#import "SearchViewController.h"

@interface MainViewController <UIPopoverControllerDelegate>{
    SearchViewController *searcher;
}

in the MainViewController.m;

-(void)OpenSearch{
    static bool isSearchOpen = FALSE;
    //open up the subview for the search function. Open up as a mini view for the ipad and a full window view for the iphone.
    if (isSearchOpen == FALSE){
        isSearchOpen = TRUE;
        if (searcher == nil){
            searcher = [[SearchViewController alloc]init];
            searcher.delegate = self;
            if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone){
                CGRect frame = searcher.view.frame;
                searcher.view.frame = CGRectMake(0, 0, frame.size.width, frame.size.height-88);
            }
        }
        if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone){
            //iphone
            [self addChildViewController:searcher];
            [self.view addSubview:searcher.view];
            [searcher didMoveToParentViewController:self];
        }else{
            //ipad
            popSearch = [[UIPopoverController alloc] initWithContentViewController:searcher];
            popSearch.popoverContentSize = searcher.view.frame.size;
            popSearch.delegate = self;
            [popSearch presentPopoverFromRect:CGRectMake(self.view.frame.size.width - 5,0, 10, 10) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
        }
    }else{
        isSearchOpen = FALSE;
        [self closeSearch];
    }    

}

-(void)closeSearch{
    //cancels the search and closes the search view
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone){
        [searcher.view removeFromSuperview];
    }else{
        [popSearch dismissPopoverAnimated:YES];
    }
    isSearchOpen = FALSE;
}

Done :)

Wednesday, June 12, 2013

How to Add a Reload or Refresh to the top of your view in iOS 6 with ARC

This has been around for a while but apple standardized it finally.

in the .h file of your tableview or collectionview or pretty much any view;

@interface myViewController : UICollectionViewController {
    //all your other declarations
    UIRefreshControl *refreshControl;
}

In your .m file, wherever you initialize the view;
if (self) {
    //other special setup items once you know your view exists
    refreshControl = [[UIRefreshControl alloc] initWithFrame:CGRectMake(0, -44, 320, 44)];
    [refreshControl addTarget:self action:@selector(reload:) forControlEvents:UIControlEventValueChanged];
    [self.collectionView addSubview:refreshControl];
}

- (void) reload:(id) sender {
    //all your reload commands go here like [tableview reloaddata];
}


Different ways to differentiate between devices in iOS

There are many ways to figure out how to tell which device you are using, some are specific, some are general. They all serve their purpose. Here are some examples;

If you have a universal app and want to use a different nib for the ipad or iphone;
    NSString *nibName = [NSStringFromClass([self class]) stringByAppendingString:([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)? @"-iPhone" : @"-iPad"];
    self = [super initWithNibName:nibName bundle:nil];
* note be sure and name you nibs correctly.

If you have some specific code the you want to perform on either the iphone or ipad;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone){
    //iphone code
}else{
    //ipad code
}

If you want a quick definition change depending on if it is an ipad or iphone;
    int total = ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)? iPhone_Total :iPad_Total;

Thursday, June 6, 2013

Making something appear and disappear nicely in iOS6.0 with ARC

All UIViews have a hidden option that you can use to hide items when you don't want the user to see them but instead of just getting rid of them, you can make them appear and disappear nicely;

-(void)FlipView:(UIView*)flipView{

    if (flipView.hidden == FALSE){
        CGContextRef context = UIGraphicsGetCurrentContext();
        [UIView beginAnimations:nil context:context];
        [UIView setAnimationTransition: UIViewAnimationTransitionCurlUp forView:flipView cache:NO];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
        [UIView setAnimationDuration:1.0];
        [UIView commitAnimations];
        
        flipView.hidden = TRUE;
    }else{
        CGContextRef context = UIGraphicsGetCurrentContext();
        [UIView beginAnimations:nil context:context];
        [UIView setAnimationTransition: UIViewAnimationTransitionCurlDown forView:flipView cache:NO];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
        [UIView setAnimationDuration:1.0];
        [UIView commitAnimations];

        flipView.hidden = FALSE;
    }

}

The option UIViewAnimationTransitionCurlUp can be instead replaced with UIViewAnimationTransitionCurlDownUIViewAnimationTransitionFlipFromLeft, or UIViewAnimationTransitionFlipFromRight

Tuesday, May 14, 2013

How to Create a gif animation in Photoshop cs6

This will work with most recent versions of Photoshop but I am using CS6 so if you are using a different version, there might be some discrepancy. I am creating an animation using several pictures taken at different times from the same location and orientation to effectively setup a time lapse.

1) Create a computer folder and put all the images you wish to turn into gif in there.

2) Run the command "File > Scripts > Load Files into Stack". This will open up a window where you can select all the images you wish to use. Bring them in as new layers for each image.

3) Order the images the way you want.

4) The photos you took might not all be perfectly alligned so you might have to move them around a bit. The easiest way to do this is the following. First "Image > Canvas Size" and increase the size by about 50%.

5) Hold down the shift key and select all but the bottom layer on the right side. Then right above the layers, change the opacity to about 40%. Then click "Layer > Hide Layers" to hide all the images for now.

6) With this setup, you should see only the last picture. You can now select any one other image and either click "Layer > Show Layers" or just click on the little square next to each layer to turn on the eye icon. With that layer selected, pick a center point that you would like to use on your images and line up the two visible images to that point. When this image is set, hide it and show the next one. Repeat until done.

7) Now you want to do some cleanup and get rid of all the empty space that you created around your images. Select the rectangle Marque tool (m) which is the second from the top left and create a square in which every image completely overlaps. Click "Image > Crop" to clear out the empty space

8) With the images lined up properly, you can select them all again, show them all and turn the opacity back to 100%.

9) Now to actually make the gif. Click on "Window > Timeline" to open up... the timeline window.

10) In the top right corner of the new window, there should be a little set of horizontal bars, click on that and select "Make Frames From Layers".

11) Under each frame there is a duration, you can select all or each frame and change that duration to whatever you want. You can hit the play button on the bottom to see the result.

12) When you are happy with how it is setup, click "File > Save For Web...". There are lots of other options there but all you need to make sure of is that it is a gif and then save it. Done!

If you would like to read another yet similar way of doing this, you can follow this link.
http://www.briandalessandro.com/blog/create-an-animated-gif-in-photoshop-cs5/





Thursday, May 2, 2013

iOS Creating Custom Delegates in iOS6 with ARC

So you want two Objects to talk to each other more than a little bit? Here is what to do by example.  Lets say we have our MainViewController and a new SearchViewController that you want a delegate attached to it. This is all you need to do.

In SearchViewController.h
@protocol SearchViewDelegate <NSObject>
@optional
@required
-(void)didAskToSearch:(NSString*)string;
@end

@interface SearchViewController : UIViewController {
}
@property (nonatomic, assign) id <SearchViewDelegate> delegate;
@end

optional has non-required methods and required of course has required methods. 

In SearchViewController.m;
    [_delegate didAskToSearch: [self searchRequestDetails]];

this is put anywhere in the code when you want it to send the information back to the other object. 

In the MainViewController.h;
@interface MainViewController : UIViewController <SearchViewDelegate> {
}

In MainViewController.m;
SearchViewController *searcher = [[SearchViewController alloc]init];
searcher.delegate = self;

#pragma mark - SearchViewControllerDelegates
-(void)didAskToSearch:(NSString*)string{
     //do whatever you want with the string results
}

When creating the object, make sure the tell the delegate you have those methods. Then you just have to create the methods as per it is required. 

Helpful link
http://stackoverflow.com/questions/12020539/how-to-make-custom-delegate-in-ios-app

Thursday, March 7, 2013

Programmatically Adding UINavigationController to existing app in iOS 6 with ARC

Whenever you build an app, you think you know how it should be structured. How are the views going to be layed out for the user to see. However, sometimes designs have to change (Mostly because the client keeps a moving target). How can you add that very helpful Navigation Controller into an existing app quickly and easily...

First, don't panic. It is actually quite easy. the guys at apple feel your pain and tried to make this as confortable as possible. 

In the AppDelegate.m;

    UINavigationController *navCtrlr = [[UINavigationController alloc]initWithRootViewController:self.viewController];
    [self.window setRootViewController:navCtrlr];


Where "self.viewController" is your existing viewController that has your first view. 
Done. 
Thats it. 
Ta Da!

now all you need to do is go through your code and turn the "addSubview" to this;

     [self.navigationController pushViewController:_detailViewController animated:YES];

Where "_detailViewController" is your next view in the stack you want to view. 

As you know, navigationController has a auto back button, but if you want to do it manually;

 [self.navigationController popViewControllerAnimated:YES];

or
 [self.navigationController popToRootViewControllerAnimated:YES];
if you want to go all the way back to the beginning. 

Other quick cosmetic items you can do is;

-(void) viewWillAppear:(BOOL)animated{
    self.title = @"My Title";

    self.navigationController.navigationBarHidden = NO;
}

Which will let you change the title on the NavBar. And make sure it is not hidden.

- (void)viewDidLoad{
    [super viewDidLoad];
    [self.navigationController.navigationBar setBarStyle:UIBarStyleBlack];
}

Which will let you change the colour of the NavBar. 

There is a lot more that can be done, but I hope that gets you off the ground and in the right direction. 

Here is an add on that looks interesting;
iOS Open Source: Drop-Down Navigation Menu






Monday, February 25, 2013

Import and Export data via email

iOS6 Getting image from Photo Library

What you would need to do to capture a image from a local photo library.

First ensure that the controller you create works these delegates;


<UIImagePickerControllerDelegate, UINavigationControllerDelegate>



Method to Start request to grab the image;

- (void) useCameraRoll:(id)sender
{
    if ([UIImagePickerController isSourceTypeAvailable:
          UIImagePickerControllerSourceTypeSavedPhotosAlbum])
    {
       UIImagePickerController *imagePicker =
           [[UIImagePickerController alloc] init];
       imagePicker.delegate = self;
       imagePicker.sourceType =
           UIImagePickerControllerSourceTypePhotoLibrary;
        imagePicker.mediaTypes = @[(NSString *) kUTTypeImage];
       imagePicker.allowsEditing = NO;
       [self presentViewController:imagePicker 
           animated:YES completion:nil];
       _newMedia = NO;
    }
}


Delegate Methods to Receive the Image;


#pragma mark -
#pragma mark UIImagePickerControllerDelegate

-(void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
   NSString *mediaType = info[UIImagePickerControllerMediaType];

   [self dismissViewControllerAnimated:YES completion:nil];

    if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) {
        UIImage *image = info[UIImagePickerControllerOriginalImage];

        _imageView.image = image;
        if (_newMedia)
            UIImageWriteToSavedPhotosAlbum(image,
               self,
               @selector(image:finishedSavingWithError:contextInfo:),
               nil);
        }
        else if ([mediaType isEqualToString:(NSString *)kUTTypeMovie])
        {
                // Code here to support video if enabled
        }
}

-(void)image:(UIImage *)image
finishedSavingWithError:(NSError *)error
contextInfo:(void *)contextInfo
{
   if (error) {
        UIAlertView *alert = [[UIAlertView alloc]
           initWithTitle: @"Save failed"
           message: @"Failed to save image"
           delegate: nil
           cancelButtonTitle:@"OK"
           otherButtonTitles:nil];
        [alert show];
   }
}

-(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
   [self dismissViewControllerAnimated:YES completion:nil];
}

References:  Accessing the iPhone Camera and Photo Library (iOS 6)