Monday, September 29, 2014

iOS Tutorial - Part 21 - Gestures

Gestures


Video Description 
If we want to recognize the user's touch events we have to use gesture recognizers. Events like pan, swipe, pinch ... are recognizable view UIGestureRecognizer class. We need to do the following steps for implementing Gestures.
1. Add the gesture recognizer to the view in order to recognize the gesture
2. Provide an implementation to handle the gesture
There are two approach in order to implement the above steps. First through the storyboard, second programatically. Lets start with the storyboard:

Add GestureRecognizer using Storyboard

1. From right bar side of Xcode in Object pallet, drag your desired gesture and drop it on the view that you want to implement the gesture.
2. Once you are done with the previous step, an icon will appear at the top of the view controller. If you go hover it, it will show the type of the gesture (e.g Swipe Gesture Recognizer). Control drag from this icon to your controller and make an IBAction. You can have your implementation inside of that method

Add GestureRecognizer programmatically

1. Create an outlet for the view that we want to have gesture for it, inside of it's controller. Control drag from your custom view to it's controller. If your view is created programmatically you don't need to create an outlet, just use the name of your view for next step.
2. Add the desired gesture recognizer to the outlet.
[self.yourCustomView addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self.yourCustomView action:@selector(pan:)]]; 
3. Implement or handle the gesture inside of custom view class (not controller).

State property

Gesture recognizers have a property called "state" and it analyzes the current state of a touch. For example if user just started pan gesture, it's on the state "UIGestureRecognizerStateBegan". Another state is when the gesture is changing for example for pan, user dragging his finger on the screen and the position of his finger is changing. This state is called "UIGestureRecognizerStateChanged". When the gesture ends, the state will go to "UIGestureRecognizerStateEnded". The first two states are for the gestures that are continuous like pan and pinch. Gestures like tap and swipe doesn't have began and changed state, instead they have state called "UIGestureRecognizerStateRecognized". This state is for the time that device recognizes the gesture. If our gesture depends on the state of the gesture we can check by these properties.

Download

Download the Gesture App from here

Sunday, September 7, 2014

iOS Tutorial - Part 20 - Draw a Custom View

Draw a Custom View


Video Description 
Previously we drag and drop whatever view that we want from storyboard pallet and inset it inside of the view controller. But what if we want a custom view like a blue circle! In this case we need to draw the view by code. In order to draw we need to get familiar with some C struct types:

CGFloat: It's just a float type but we use it only for drawing
CGFloat myCGFloat = 12.6;
CGPoint: It represents a point in view. It has x (horizontal distance) and y (vertical distance). Both x and y are type CGFloat. We can make a CGPoint with the help of method CGPointMake(x, y)
CGPoint myCGPoint = CGPointMake(27.5, 16.7)
CGSize: It represents with and height of the view. Both width and height are type CGFloat. We can make a CGSize with the help of method CGSizeMake(width, height)
 
CGSize myCGSize = CGSizeMake(45.0, 35.5)
CGRect: We can specify a rectangle with the origin of CGPoint and size of CGSize. We can make a CGRect with the help of method CGRectMake(x, y, width, height)
CGRect myCGRect = CGRectMake(10.0, 20.0, 50.0, 45.5)

Coordinates: Origin of the view coordinate system is upper left corner of the device

Views have 3 properties related to their location:

bounds: Your view's internal drawing space's origin and size the bounds property is what you use inside your view's own implementation.
center: The center of your view in your superview's coordinate space
frame: A rectangle in your superview's coordinate space which entirely contains your views bounds.size

In the above picture here are the View B's properties:
bounds = ((0, 0), (200, 250));
frame = ((140, 65), (320, 320));
center = (300, 255)

Steps to draw something inside of the view

First of all drag and drop UIView from Object Library or pallet inside of view controller. Then Create a new file by clicking on file/new/File... . Put a name (e.g MyCustomView) for this file and set supperclass to UIView. Once you create this file, tap on this view (UIView) in storyboard and from right side menu select identity inspector tab. From class dropdown menu select the file name that you just created (e.g MyCustomView). Now open .m file (MyCustomView.m) and follow the steps.

1. Add the following method in .m file
- (void)drawRect:(CGRect)rect
{
   //Draw here
}
2. Use UIBezierPath to draw your custom path like an example bellow
UIBezierPath *trianglePath = [[UIBezierPath alloc] init];
3. Define your custom path like an example bellow
    [trianglePath moveToPoint:CGPointMake(25, 10)];
    [trianglePath addLineToPoint:CGPointMake(120, 140)];
    [trianglePath addLineToPoint:CGPointMake(7, 120)];
you can also close the path by the following command
[trianglePath closePath];
4. Set Stroke and Fill like bellow
    [[UIColor greenColor] setFill];
    [[UIColor redColor] setStroke];
    [trianglePath fill];
    [trianglePath stroke];

Some Useful UIBezierPath methods

UIBezierPath has a lot of useful methods that you can find in it's documentation but here are some of cool and important ones:
Rounded Rect: In order to draw a rounded rect we can use the following method
UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:12];
[roundedRect stroke];
Oval Rect:
UIBezierPath *ovalRect = [UIBezierPath bezierPathWithOvalInRect:self.bounds];
[ovalRect fill];
addClip: This would clip all drawing to be inside the roundedRect
[roundedRect addClip];

What if your bounds changes!

By default, there is no redraw when your bounds change. Instead the "bit" of your view will be stretched or moved. You can change this default behavior by using a property called contentMode. You can set it like bellow:
self.contentMode = UIViewContentModeRedraw;

Download

Download the Web App from here