Showing posts with label view. Show all posts
Showing posts with label view. Show all posts

Sunday, 11 June 2017

Custom View With Transparent Oval.

UIBezierPath define a path consisting of straight and curved line segment and render the path in custom views. We can use this for specify the geometry of path, path can define simple shapes such as rectangle, oval, arc or can define complex polygons.For more details about UIBezierPath Click Here.

I am going to draw a custom view with transparent oval.I will use UIBenzierPath  fro achieving result.

First create a SubClass of UIView.
Ex. named sub class to CustomView
Inside drawRect: method of CustomView.m

- (void)drawRect:(CGRect)rect {
    // Drawing code

//First Create path for Oval
    UIBezierPath *apath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(5, 5,  self.frame.size.width-10, self.frame.size.height-10)];
    //Create another path
    UIBezierPath *clipPath = [ UIBezierPath bezierPathWithRect:CGRectInfinite];

//Append both path
    [clipPath appendPath:apath];
    clipPath.usesEvenOddFillRule = YES; //

usesEvenOddFillRule property is used to determine whether the even-odd or non-zero rule is used to determine the area enclosed by the path.   

  CGContextSaveGState(UIGraphicsGetCurrentContext());
    [clipPath addClip];
    CGContextSaveGState(UIGraphicsGetCurrentContext());
    [[UIColor orangeColor] setFill];
    [[UIColor blackColor] setStroke];
    clipPath.lineWidth = 2;
    [clipPath stroke];
    [clipPath fill];
}


Friday, 3 February 2017

Draw a curve on UIView

As we all know, User interface is a key part for all application. User interface of an application is only way to interact with end user. A better user interface provide better experiences to end user.
In this block, we are going to create a curve at the upper part of a view.
Resultant screen will be look line below image


                                                                 


For drawing a curve on the upper part of a view, we need to do some math works for setting the radius and center on view. 
We need 3 things for drawing a curve:
1. Center.
2. Radius
3. Start angle.
4. End angle.
At the initial point we are not having any point and angle.
Suppose, we want to draw a curve on the upper part of view with area w * 100, w is the width of view.
now we have 3 points on the curve
1. view x origin and 100.
2. view center and 0.
3. view width and 100.
so all points are:
initial point - (0, 100)
mid point - (w/2, 0)
end point -  (w, 100).

Now we are having three points, so we can draw a triangle using these points
Lets say our triangle is ∆ABC and ø be the angle opposite to side A.
We can calculate distance between all points of triangle AB, BC and CA

Suppose A is the distance between A and B(AB), similarly B is the distance between BC and C is the distance between CA. 
suppose r be the radius of circle/arc.
then A/sinø = 2r,
This isn't hard (just drop a perpendicular from O to A, and use the definition of sin on the similar triangles).

Area of triangle  ∆ABC
K = (BCsinø)/2
sinø = 2K/BC

Now
2r = A/(2K/BC)
r = ABC/4K

Draw a perpendicular for A to BC, we will get height of triangle which is 100(curve draw area as we defined initially)
so K = (base * height) / 2;
K = B * h / 2;

so radius  r = AC / 2h.
we have calculated radius. lets calculate center
x point of center will be in the mid of the curve area and y point will be radius.
so center c = (w/2, r)

now we are ready for draw a curve on the upper part of a view.

Sub-classing of view is the best option for customization and redrawing.
Create a new class(subclass of UIView) namely CurveView(you can choose any name for a class)

in a CurveView.h file declare the following methods and variables
//----variables
    CGFloat radius;
    CGPoint centerPoint;
    CGFloat curveTopMargin;

//----methods
-(instancetype) initWithFrame:(CGRect)frame withUpperCurveMargin:(CGFloat)topMargin;

also declare a macro for convert angle from degree to radians
#define   DEGREES_TO_RADIANS(degrees)  ((M_PI * degrees)/ 180)

Switch to CurveView.m file and following code
//method for initialize an instance of CurveView
-(instancetype) initWithFrame:(CGRect)frame withUpperCurveMargin:(CGFloat)topMargin{
    self = [super initWithFrame: frame];
    if(self){
        curveTopMargin = topMargin;
        [self calculateRadius];
    }
    return self;
}

Now add code for calculate center point and radius
#pragma mark - Calculate radius and center
-(void) calculateRadius{
    CGFloat a = [self calculateDistance:CGPointMake(0, curveTopMargin) andPoint:CGPointMake(self.frame.size.width/2, 0)];
    CGFloat c = [self calculateDistance:CGPointMake(self.frame.size.width, curveTopMargin) andPoint:CGPointMake(self.frame.size.width/2, 0)];
    radius = (a * c)/(2*curveTopMargin);
    centerPoint = CGPointMake(self.frame.size.width/2, radius);
}
-(CGFloat) calculateDistance:(CGPoint)p1 andPoint:(CGPoint)p2{
    CGFloat x =sqrt((pow((p2.x - p1.x), 2)+(pow(p2.y - p1.y, 2))));
    return x;
}


And now add drawRect() method for drawing a view with curve
- (void)drawRect:(CGRect)rect {
    // Drawing code
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSaveGState(context);
    //-------draw
    CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
    CGContextSetFillColorWithColor(context, [UIColor cyanColor].CGColor);
    UIBezierPath *curvePath = [UIBezierPath bezierPath];
    [curvePath addArcWithCenter:centerPoint radius:radius-1  startAngle:DEGREES_TO_RADIANS(0) endAngle:DEGREES_TO_RADIANS(360) clockwise:YES];
    CGPathRef cgPath = CGPathCreateCopy(curvePath.CGPath);
    CGContextAddPath(context, cgPath);
    CGContextSetLineWidth(context, 1);
    CGContextSetShadowWithColor(context, CGSizeMake(0, -1.0), 0.3, [UIColor lightGrayColor].CGColor);
    CGContextDrawPath(context, kCGPathEOFillStroke);
    CGPathRelease(cgPath);
    CGContextRestoreGState(context);
}


Go to controller class and add curve view as a subview
-(void) addCurveView{
    CGSize screenSize = [UIScreen mainScreen].bounds.size;
    CurveView *topCurveView = [[CurveView alloc] initWithFrame:CGRectMake(0, screenSize.height, screenSize.width, 250) withUpperCurveMargin:60];
    topCurveView.backgroundColor = [UIColor clearColor];
    [self.view addSubview:topCurveView];
    CGRect f = topCurveView.frame;
    f.origin.y = screenSize.height - f.size.height;
    [UIView animateWithDuration:0.3 animations:^{
        topCurveView.frame = f;
    }];
}


Run the app, w can see a curve at the upper part.

Click here for Complete project.

Monday, 10 October 2016

CABasicAnimation

CABasicAnimation provides basic, single-keyframe animation capabilities for a layer property.
CABasicAnimation class animates a layer property between two values, a starting and an ending value. To move a layer from one point to another in its containing window, for example, we can create a basic animation using the keypath position. We give the animation a start value and an ending value and add the animation to the layer.

Here i am going to create a search animation, animation start with a small circle with minimum radius and continue increase the radius and after maximum radius again its start animate the circle from minimum radius.

First create  a Project(SearchActivityIndicatorViewSample)

Now create a new file(SearchActivityIndicatorView) which is subclass of UIView.
We are going to display a animated circle, so for creating a  circle i am going to declare another Class(SearchActivityIndicatorCircleView) inside "SearchActivityIndicatorView".
SearchActivityIndicatorCircleView class also a subclass of UIView.

Ok, Why i am creating a separate class for display a circle?
Answer is, if we want to display more than one circle as a search indicator so we can use multiple instance of that class and we don't need to write code for multiple circle. So by using this approach     we can avoid duplication of code.
 and also declare a property for stroke color inside "SearchActivityIndicatorCircleView Class".

here the code snippet

@interface SearchActivityIndicatorCircleView : UIView
{
   
}
@property (nonatomic, strong) UIColor *strokeColor;
@end


//Implementation 
@implementation SearchActivityIndicatorCircleView

@end


Add the following methods  inside SearchActivityIndicatorCircleView implementation.
-(instancetype) initWithFrame:(CGRect)frame{
    self = [super initWithFrame: frame];
    if (self) {
        self.backgroundColor = [UIColor clearColor];
    }
    return self;
}


-(void) drawRect:(CGRect)rect{
    //Drawing Code
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSaveGState(context);
    CGContextSetStrokeColorWithColor(context, self.strokeColor.CGColor);
    UIBezierPath *circlePath = [UIBezierPath bezierPath];
    [circlePath addArcWithCenter:CGPointMake(self.bounds.size.width/2, self.frame.size.height/2) radius:MIN(self.frame.size.width, self.frame.size.height)/2 - 5  startAngle:DEGREES_TO_RADIANS(0) endAngle:DEGREES_TO_RADIANS(360) clockwise:YES];
    CGPathRef cgPath = CGPathCreateCopy(circlePath.CGPath);
    CGContextAddPath(context, cgPath);
    CGContextSetLineWidth(context, 1);
    CGContextDrawPath(context, kCGPathStroke);
    CGPathRelease(cgPath);
}


In the first method i override initWithFrame: method for creating a new instance for the class.
and in the second method drawRect: i am creating a circle. Radius of circle is not static, its will depend on the frame size of View.
For calculating the radius i used smallest value between width and height and after getting smallest value reduce 5 from the that, and with the resultant value drawing circle.

Now we have enough code for draw a circle, only need to create a instance of a class and as a sub-view.

Now Switch to SearchActivityIndicatorView.h Class and add following codes.
@interface SearchActivityIndicatorView : UIView
{
    CGFloat maxScale, minScale;
}
@property (nonatomic, assign) BOOL isAnimating;
@property (nonatomic, strong) UIColor *strokeColor;
@property (nonatomic, assign) NSTimeInterval animationDuration;

-(void) startSearching;
-(void) stopSearching;


Now switch to SearchActivityIndicatorView.m and add following code
-(instancetype) init{
    self = [super init];
    if (self) {
        [self initializeDefaultValues];
    }
    return self;
}
-(instancetype) initWithFrame:(CGRect)frame{
   
    self = [super initWithFrame:frame];
    if (self) {
        [self initializeDefaultValues];
    }
    return self;
}
-(void) awakeFromNib{
    [super awakeFromNib];
    [self initializeDefaultValues];
}
-(void) initializeDefaultValues{
    //set animation suration
    self.animationDuration = 1.9f;
    //set default stroke color
    self.strokeColor = [UIColor redColor];
    //set minimum scale
    minScale = 0.3f;
    //set maximum scale

    maxScale = 8.0f;
}


First 3 methods are used to creating new instance for the class
and the last method set default value with the time of new instance creation of class.
Inside this method, i am defining default value for animation duration, color of circle' stroke, minimum and maximum scale for circle view.

Now its time to add SearchActivityIndicatorCircleView
-(void) addCircleSearchView{
   //1. get minimum value between width and height
    CGFloat width = MIN(self.frame.size.width, self.frame.size.height)/4;

   //2. Create an instance of SearchActivityIndicatorCircleView
    SearchActivityIndicatorCircleView *circleSearchView = [[SearchActivityIndicatorCircleView alloc] initWithFrame:CGRectMake(0, 0, width, width)];
    [circleSearchView setStrokeColor:self.strokeColor];
    circleSearchView.center = self.center;
    //3.Add CABasicAnimation for scale view
    CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    //4. set animation duration time
    scaleAnimation.duration = self.animationDuration;
    //5. set repeat count to maximum
    scaleAnimation.repeatCount = CGFLOAT_MAX;
    //6. set minimum value for animation
    scaleAnimation.fromValue = @(minScale);
    //7. set maximum value for animation
    scaleAnimation.toValue = @(maxScale);
    //8. set timing function
    scaleAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];

    //9. add animation to Circle layer
    [circleSearchView.layer addAnimation:scaleAnimation forKey:kSearchScaleAnimationKey];
   //10. add SearchActivityIndicatorCircleView as a subview
    [self addSubview:circleSearchView];
}


Above method create an instance for  SearchActivityIndicatorCircleView, add basic animation for animate a circle and add newly created instances as a subview.
Here step by step explanations:
1. Find minimum value between width and height of frame and divide by 4 means the frame of circle view is 4 times less than SearchActivityIndicatorView.
2. Create an instance of  SearchActivityIndicatorCircleView with the minimum frame size.
3. Create an instance of CABasicAnimation with key "transform.scale".
4. set animation duration, the time require for animation of a circle from minimum scale to maximum scale.
5. set repeat count for animation.
6. set minimum scale or starting scale for animation.
7. set maximum scale for animating the circle.
8. timingFunction: A timing function defining the pacing of the animation.
    CAMediaTimingFunction represents one segment of a function describing a timing curve. The
    function maps an input time normalized to the range [0,1] to an
    output time also in the range [0,1]. E.g. these functions are used
    to define the pacing of an animation over its duration (or over the
   duration of one keyframe). Click here for more detail about CAMediaTimingFunction.

9. add animation to circle layer. CABasicAnimation provide capabilities for animate layer property, so we add animation to layer of circle view.
10. Add circle view as a subview.

We have code for draw a circle, add animation to circle.
So,  now its time to display animation, add below code in SearchActivityIndicatorCircleView.m

-(void) startSearching{
   //check if animation is already started
    if (self.isAnimating) {
        return;
    }

   //set flag value true
    self.isAnimating = YES;

  //add circle
    [self addCircleSearchView];
}


 first we are checking, is animation already started if yes than no need to start animation again.
If no than set the flag value true so we can check animation status later.
and last line send a message to  "addCircleSearchView" method for adding a circle view.




Also write a method for stop animation.
-(void) stopSearching{
    if(!self.isAnimating) {
        return;
    }
    for(UIView *searchView in self.subviews) {
        [UIView animateWithDuration:0.9f animations:^{
            searchView.transform = CGAffineTransformMakeScale(7.0f, 7.0f);
            [searchView setAlpha:0.0f];
        } completion:^(BOOL finished) {
            [searchView.layer removeAllAnimations];
            [searchView removeFromSuperview];
        }];
    }
    self.isAnimating = NO;
}


thats enough.
Now Its time for display our effort to end user.
Switch to ViewController.h class, import SearchActivityIndicatorView

add a property IBOutlet  for SearchActivityIndicatorView
@property (nonatomic, weak) IBOutlet SearchActivityIndicatorView *seachActivityIndicatorView;
and add a IBAction for handle user action.
-(IBAction)handleTapGesture:(UITapGestureRecognizer *)sender;

Now switch to  main.storyboard and drag a UIView from Object library.
Select that view and go to identity inspector and change the class from UIView to SearchActivityIndicatorView
 


Connect IBOutlet to SearchActivityIndicatorView.
Add a Tap Gesture Recognizer  to SearchActivityIndicatorView and connect IBAction method.

Now go to ViewController.m class and method for start animation
-(IBAction)handleTapGesture:(UITapGestureRecognizer *)sender{
   
    if (self.seachActivityIndicatorView.isAnimating == YES) {
        [self.seachActivityIndicatorView stopSearching];
    }
    else{
        [self.seachActivityIndicatorView startSearching];
    }
}


Run the app and tap on the screen, we can see a animated circle on screen.
Click here for complete project.