jueves, 31 de diciembre de 2015

iOS: How to close soft keyboard

Objective C

- (void)viewDidLoad {
    [super viewDidLoad];
    

    [self setCloseKeyboard];

    ...
}

- (void)setCloseKeyboard
{
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
                                   initWithTarget:self
                                   action:@selector(dismissKeyboard)];
    tap.numberOfTapsRequired = 1;
    tap.cancelsTouchesInView = NO;
    
    [self.navigationController.navigationBar addGestureRecognizer:tap];
    [self.view addGestureRecognizer:tap];
}

-(void)dismissKeyboard {
    [self.view endEditing:YES];
}

- (void)viewWillAppear:(BOOL)animated
{
    [self.view endEditing:YES];
    
    [super viewWillAppear:animated];
}

Swift

let gesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
        gesture.numberOfTapsRequired = 1
        gesture.cancelsTouchesInView = false
       
        view.addGestureRecognizer(gesture)


lunes, 23 de noviembre de 2015

iOS: CocoaPods

CocoaPods is an open source dependency manager for Swift and Objective-C Cocoa projects.

1) Get CocoaPods

     $ sudo gem install cocoapods

2) If you have a project with no podfile

2.1) Open the terminal and navigate to your projects location
2.2) Execute "pod init"*
2.3) Open your podfile in Xcode or use "open -a Xcode Podfile"
2.4) Remove the comment tag 


     # Uncomment this line to define a global platform for your project
     # platform :ios, "6.0"

Use the proper platform number

Your Podfile should look like the following by now:

xcodeproj ‘MyProject.xcodeproj'

     # Uncomment this line to define a global platform for your project
     # platform :ios, '8.0'
     # Uncomment this line if you're using Swift
     # use_frameworks!

     platform :ios, '8.0'

     target 'MyProject' do

     end

     target 'MyProjectTest’ do

     end

3) Then you can add any cocoa pod, for instance AFNetworking by adding the following between platform and target:

     source 'https://github.com/CocoaPods/Specs.git'
     pod 'GoogleMaps'

and execute

     $ cd <path-to-project>

     $ pod install

4) From now on you should always open your project via .xcworkspace rather than from the .xcodeproj

NOTES:

*In Swift you need to use "use_frameworks!" inside the target. Sometimes the podfile es empty it need to have a target, such as:

target '<proyect_name>' do
use_frameworks!
#pod "AFNetworking"

end

jueves, 29 de octubre de 2015

iOS: UITableView, UITableViewDataSource and UITableViewDelegate

Objective C

UITableView
1) Register cell
    [self.tableView registerNib:[UINib nibWithNibName:@"YourCellView" bundle:nil] forCellReuseIdentifier:@"Cell"];
2) Register section
    [self.tableView registerNib:[UINib nibWithNibName:@"YourSectionView" bundle:nil] forHeaderFooterViewReuseIdentifier:@"Section"];
UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return number;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    // Setup cell
    return cell;
}


- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
    return height;
}


- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    return height;
}


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return number;

}


// Note: 
//            1) Must subclass UITableViewHeaderFooterView
//            1) [self.tableView registerNib:[UINib nibWithNibName:@"YourNibName" bundle:nil] forHeaderFooterViewReuseIdentifier:@"SectionIdentifier"];
//
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    return section;
}
UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // ...
}

- (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 settings
    if ([cell respondsToSelector:@selector(setPreservesSuperviewLayoutMargins:)]) {
        [cell setPreservesSuperviewLayoutMargins:NO];
    }
    
    // Explictly set your cell's layout margins
    if ([cell respondsToSelector:@selector(setLayoutMargins:)]) {
        [cell setLayoutMargins:UIEdgeInsetsZero];
    }

}

Swift

func numberOfSections(in tableView: UITableView) -> Int {
        return SECTIONS    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return COUNTS
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! SessionTableViewCell
        
        // ...

        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        // ...
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 70
    }

miércoles, 21 de octubre de 2015

iOS: UIScrollView

Set content size in IB

Identity Inspector > User defined runtime attributes:

Key Path: contentSize
Type: Size
Value: {W, H}

viernes, 16 de octubre de 2015

iOS: DownPicker

Use

YourViewController.h


@interface EJWelcomeFinalStepView

...

@property(weak, nonatomic) IBOutlet UITextField* dropDownTextField;

...

@end

YourViewController.m


@interface YourViewController()
{
    DownPicker* workingAtPicker;
    DownPicker* emailPicker;
    DownPicker* myIndustryPicker;
    EJDayMonthPicker* birthdayPicker;
    DownPicker* rolePicker;
    EJYearMonthPicker* dateLeftSAPPicker;
    DownPicker* lineOfBusinessPicker;
    DownPicker* skillListPicker;
    NSString* searchString;
    CoreSkillCell* _sizingCell;

}
@end

@implementation EJWelcomeFinalStepView

// Somewhere in code
dropDownPicker = [[DownPicker alloc] initWithTextField: dropDownTextField withData:array];

@end

DownPicker.h

#import <UIKit/UIKit.h>

@protocol DownPickerDelegate <NSObject>

- (void)itemSelected:(id)sender;

@end

@interface DownPicker : UIControl<UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate>
{
    UIPickerView* pickerView;
    IBOutlet UITextField* textField;
    NSArray* dataArray;
    NSString* placeholder;
    NSString* placeholderWhileSelecting;
NSString* toolbarDoneButtonText;
    NSString* toolbarCancelButtonText;
UIBarStyle toolbarStyle;
}

@property (nonatomic) NSString* text;
@property (nonatomic) NSInteger selectedIndex;
@property (weak, nonatomic) id<DownPickerDelegate> delegate;

-(id)initWithTextField:(UITextField *)tf;
-(id)initWithTextField:(UITextField *)tf withData:(NSArray*) data;

@property (nonatomic) BOOL shouldDisplayCancelButton;

/**
 Sets an alternative image to be show to the right part of the textbox (assuming that showArrowImage is set to TRUE).
 @param image
 A valid UIImage
 */
-(void) setArrowImage:(UIImage*)image;

-(void) setData:(NSArray*) data;
-(void) setPlaceholder:(NSString*)str;
-(void) setPlaceholderWhileSelecting:(NSString*)str;
-(void) setAttributedPlaceholder:(NSAttributedString *)attributedString;
-(void) setToolbarDoneButtonText:(NSString*)str;
-(void) setToolbarCancelButtonText:(NSString*)str;
-(void) setToolbarStyle:(UIBarStyle)style;

/**
 TRUE to show the rightmost arrow image, FALSE to hide it.
 @param b
 TRUE to show the rightmost arrow image, FALSE to hide it.
 */
-(void) showArrowImage:(BOOL)b;

-(UIPickerView*) getPickerView;
-(UITextField*) getTextField;

/**
 Retrieves the string value at the specified index.
 @return
 The value at the given index or NIL if nothing has been selected yet.
 */
-(NSString*) getValueAtIndex:(NSInteger)index;

/**
 Sets the zero-based index of the selected item: -1 can be used to clear selection.
 @return
 The value at the given index or NIL if nothing has been selected yet.
 */
-(void) setValueAtIndex:(NSInteger)index;
@end

DownPicker.m

#import "DownPicker.h"

@implementation DownPicker
{
    NSString* _previousSelectedString;
}

-(id)initWithTextField:(UITextField *)tf
{
    return [self initWithTextField:tf withData:nil];
}

-(id)initWithTextField:(UITextField *)tf withData:(NSArray*) data
{
    self = [super init];
    if (self) {
        self->textField = tf;
        self->textField.delegate = self;
       
        // set UI defaults
        self->toolbarStyle = UIBarStyleDefault;
        // set language defaults
        self->placeholder = @"Tap to choose...";
        self->placeholderWhileSelecting = @"Pick an option...";
self->toolbarDoneButtonText = @"Done";
        self->toolbarCancelButtonText = @"Cancel";
        
        // hide the caret and its blinking
        [[textField valueForKey:@"textInputTraits"]
         setValue:[UIColor clearColor]
         forKey:@"insertionPointColor"];
        
        // set the placeholder
        self->textField.placeholder = self->placeholder;
        
        // setup the arrow image
        UIImage* img = [UIImage imageNamed:@"downArrow.png"];   // non-CocoaPods
        if (img == nil) img = [UIImage imageNamed:@"DownPicker.bundle/downArrow.png"]; // CocoaPods
        if (img != nil) self->textField.rightView = [[UIImageView alloc] initWithImage:img];
        self->textField.rightView.contentMode = UIViewContentModeScaleAspectFit;
        self->textField.rightView.clipsToBounds = YES;
        
        // show the arrow image by default
        [self showArrowImage:YES];

        // set the data array (if present)
        if (data != nil) {
            [self setData: data];
        }
        
        self.shouldDisplayCancelButton = YES;
    }
    return self;
}


- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView;
{
    return 1;
}

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
    self->textField.text = [dataArray objectAtIndex:row];
}

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component;
{
    return [dataArray count];
}

- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component;
{
    return [dataArray objectAtIndex:row];
}

-(void)doneClicked:(id) sender
{
    //hides the pickerView
    [textField resignFirstResponder];
    
    if (self->textField.text.length == 0 || ![self->dataArray containsObject:self->textField.text]) {
        // self->textField.text = [dataArray objectAtIndex:0];
        [self setValueAtIndex:-1];
        self->textField.placeholder = self->placeholder;
    }
    /*
    else {
        if (![self->textField.text isEqualToString:_previousSelectedString]) {
            [self sendActionsForControlEvents:UIControlEventValueChanged];
        }
    }
    */
    [self sendActionsForControlEvents:UIControlEventValueChanged];
    
    if ([self.delegate respondsToSelector:@selector(itemSelected:)]) {
        [self.delegate itemSelected:self];
    }
}

-(void)cancelClicked:(id)sender
{
    [textField resignFirstResponder]; //hides the pickerView
    if (_previousSelectedString.length == 0 || ![self->dataArray containsObject:_previousSelectedString]) {
        self->textField.placeholder = self->placeholder;
    }
    self->textField.text = _previousSelectedString;
}


- (IBAction)showPicker:(id)sender
{
    _previousSelectedString = self->textField.text;
    
    pickerView = [[UIPickerView alloc] init];
    pickerView.showsSelectionIndicator = YES;
    pickerView.dataSource = self;
    pickerView.delegate = self;
    
    //If the text field is empty show the place holder otherwise show the last selected option
    if (self->textField.text.length == 0 || ![self->dataArray containsObject:self->textField.text])
    {
        if (self->placeholderWhileSelecting) {
            self->textField.placeholder = self->placeholderWhileSelecting;
        }
        // 0.1.31 patch: auto-select first item: it basically makes placeholderWhileSelecting useless, but
        // it solves the "first item cannot be selected" bug due to how the pickerView works.
        [self setSelectedIndex:0];
    }
    else
    {
        if ([self->dataArray containsObject:self->textField.text]) {
            [self->pickerView selectRow:[self->dataArray indexOfObject:self->textField.text] inComponent:0 animated:YES];
        }
    }

    UIToolbar* toolbar = [[UIToolbar alloc] init];
    toolbar.barStyle = self->toolbarStyle;
    [toolbar sizeToFit];
    
    //space between buttons
    UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
                                                                                   target:nil
                                                                                   action:nil];
    
    UIBarButtonItem* doneButton = [[UIBarButtonItem alloc]
                                   initWithTitle:self->toolbarDoneButtonText
                                   style:UIBarButtonItemStyleDone
                                   target:self
                                   action:@selector(doneClicked:)];
    
    if (self.shouldDisplayCancelButton) {
        UIBarButtonItem* cancelButton = [[UIBarButtonItem alloc]
                                         initWithTitle:self->toolbarCancelButtonText
                                         style:UIBarButtonItemStylePlain
                                         target:self
                                         action:@selector(cancelClicked:)];
        
        [toolbar setItems:[NSArray arrayWithObjects:cancelButton, flexibleSpace, doneButton, nil]];
    } else {
        [toolbar setItems:[NSArray arrayWithObjects:flexibleSpace, doneButton, nil]];
    }


    //custom input view
    textField.inputView = pickerView;
    textField.inputAccessoryView = toolbar;  
}

- (BOOL)textFieldShouldBeginEditing:(UITextField *)aTextField
{
    if ([self->dataArray count] > 0) {
        [self showPicker:aTextField];
        return YES;
    }
    return NO;
}

- (void)textFieldDidEndEditing:(UITextField *)aTextField {
    // [self doneClicked:aTextField];
    aTextField.userInteractionEnabled = YES;
}

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    return NO;
}

-(void) setData:(NSArray*) data
{
    dataArray = data;
}

-(void) showArrowImage:(BOOL)b
{
    if (b == YES) {
      // set the DownPicker arrow to the right (you can replace it with any 32x24 px transparent image: changing size might give different results)
        self->textField.rightViewMode = UITextFieldViewModeAlways;
    }
    else {
        self->textField.rightViewMode = UITextFieldViewModeNever;
    }
}

-(void) setArrowImage:(UIImage*)image
{
    [(UIImageView*)self->textField.rightView setImage:image];
}

-(void) setPlaceholder:(NSString*)str
{
    self->placeholder = str;
    self->textField.placeholder = self->placeholder;
}

-(void) setPlaceholderWhileSelecting:(NSString*)str
{
    self->placeholderWhileSelecting = str;
}

-(void) setAttributedPlaceholder:(NSAttributedString *)attributedString
{
    self->textField.attributedPlaceholder = attributedString;
}

-(void) setToolbarDoneButtonText:(NSString*)str
{
    self->toolbarDoneButtonText = str;
}

-(void) setToolbarCancelButtonText:(NSString*)str
{
    self->toolbarCancelButtonText = str;
}

-(void) setToolbarStyle:(UIBarStyle)style;
{
    self->toolbarStyle = style;
}

-(UIPickerView*) getPickerView
{
    return self->pickerView;
}

-(UITextField*) getTextField
{
    return self->textField;
}

-(NSString*) getValueAtIndex:(NSInteger)index
{
    return (self->dataArray.count > index) ? [self->dataArray objectAtIndex:index] : nil;
}

-(void) setValueAtIndex:(NSInteger)index
{
    if (index >= 0) [self pickerView:nil didSelectRow:index inComponent:0];
    else [self setText:nil];
}

/**
 Getter for text property.
 @return
 The value of the selected item or NIL NIL if nothing has been selected yet.
 */
- (NSString*) text {
    return self->textField.text;
}

/**
 Setter for text property.
 @param txt
 The value of the item to select or NIL to clear selection.
 */
- (void) setText:(NSString*)txt {
    if (txt != nil) {
        NSInteger index = [self->dataArray indexOfObject:txt];
        if (index != NSNotFound) [self setValueAtIndex:index];
    }
    else {
        self->textField.text = txt;
    }
}

/**
 Getter for selectedIndex property.
 @return
 The zero-based index of the selected item or -1 if nothing has been selected yet.
 */
- (NSInteger)selectedIndex {
    NSInteger index = [self->dataArray indexOfObject:self->textField.text];
    return (index != NSNotFound) ? (NSInteger)index : -1;
}

/**
 Setter for selectedIndex property.
 @param index
 Sets the zero-based index of the selected item using the setValueAtIndex method: -1 can be used to clear selection.
 */
- (void)setSelectedIndex:(NSInteger)index {
    [self setValueAtIndex:(NSInteger)index];
}

@end

jueves, 15 de octubre de 2015

iOS: Drop shadow of of a view

view.layer.shadowColor = [[UIColor blackColor] CGColor];
view.layer.shadowOffset = CGSizeMake(0.0f,0.0f);
view.layer.shadowOpacity = 0.5f;

view.layer.shadowRadius = 3.0f;

viernes, 9 de octubre de 2015

iOS: TabBar application with login


AppDelegate.h

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@property(nonatomic, strong) UITabBarController* tabBarController;

@end

AppDelegate.m

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    [[EJCacheService sharedCacheService] initialize];
    
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    
    MyViewController * myViewController = [[MyViewController alloc] init];
    myViewController.tabBarItem.title = @"MyViewController";
    
    self.tabBarController = [[UITabBarController alloc]init];
    self.tabBarController.viewControllers = @[myViewController];
    
    // Login and Splash setup
    LoginViewController *loginViewController = [[LoginViewController alloc] init];
    
    // If you want a navigation controller because your login has varius steps or something
    // UINavigationController *navigationController = [[UINavigationController alloc    initWithRootViewController: loginViewController];
    // navigationController.navigationBarHidden = YES;
    // self.window.rootViewController = navigationController;

    // else 
    /self.window.rootViewController = loginViewController;
    
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    
    return YES;
}

...

@end


LoginViewController.m

Somewhere inside your LoginViewController you will push the TabBarController:

AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
    
    [UIView transitionWithView:appDelegate.window duration:0.5 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
        appDelegate.window.rootViewController = appDelegate.tabBarController;
    } completion:nil];


jueves, 8 de octubre de 2015

iOS: Custom TableViewCell

CustomViewController.h

@interface CustomViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>

...
@property(weak, nonatomic) IBOutlet UITableView* tableView;


@end


CustomViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];

   ...
    
    UINib *cellNib = [UINib nibWithNibName:@"CustomTableViewCell" bundle:nil];
    [[self tableView] registerNib:cellNib forCellReuseIdentifier:@"Cell"];
    
    UINib *sectionNib = [UINib nibWithNibName:@"CustomTableViewHeaderFooterView" bundle:nil];
    [[self tableView] registerNib:sectionNib forCellReuseIdentifier:@"Section"];

}

...

#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [items count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *simpleTableIdentifier = @"Cell";
    
    CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
    
    if (cell == nil) {
        cell = [[CustomTableViewHeaderFooterView allocinitWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
    }
    
   // Do something with the cell
   ...
    
    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
    return ROW_HEIGHT;
}

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    CustomTableViewHeaderFooterView *cell = [tableView dequeueReusableCellWithIdentifier:@"Section"];
    
    if (cell == nil) {
        cell = [[CustomTableViewHeaderFooterView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Section"];
    }

...
    
    return cell;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;

}

CustomTableViewCell.h

@interface EJRecommendationsTableViewCell : UITableViewCell

// Properties

@end

CustomTableViewCell.m

@implementation EJRecommendationsTableViewCell

- (void)awakeFromNib {
    // Initialization code
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

@end

Note:

1) Link the TableView outlet to the TableView view. 
2) Create a .xib and named it "CustomTableViewCell.xib" in IdentityInspector set the class to "CustomTableViewCell". The top most view of this view has to be a UITableViewCell.
3) "CustomTableViewHeaderFooterView" is identical to "CustomTableViewCell".