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 :)