模仿微信封装popView动画效果

          废话不多说,首先看看下面实现的效果图。请参照源代码和文章一块看,文章写得有些乱。附上源代码下载链接地址:https://github.com/ZhengYaWei1992/ZWPopView


模仿微信封装popView动画效果_第1张图片
效果图一
模仿微信封装popView动画效果_第2张图片
效果图二

         说明一点,效果图中所弹出的绿色的边框(包括小三角形)不是通过图片处理,而是绘制出来的,具体的背景颜色和边框宽度都可随意调节。另外小三角形的位置支持三种显示模式:左上角、中间顶部、右上角显示,外部通过设置枚举值即可简单调用。第一个效果图中的popView中不仅仅只能显示文字,还能显示图片加文字,因为内部是一个tableView,具体内容可以通过自定义tableViewCell自行调整。第二个效果图中popView中的感叹号按钮是一个自定义控件,外部调用时可以自定义里面的内容,然后添加点击事件即可。

         首先先看一下使用popView外部的简单调用方法。rightBarButtonItem监听事件中的方法:

- (void)optionClick:(UIButton *)sender{

           NSArray *menus = @[@"发起群聊", @"添加朋友",@"扫一扫",@"收付款"];

         //这里的44是tableView默认的行高

           ZWCustomPopView *pView = [[ZWCustomPopView alloc]initWithBounds:CGRectMake(0, 0, 120, 44 * menus.count) titleMenus:menus maskAlpha:0.0];

           pView.delegate = self;

          //可以用来调节边界线的颜色

          pView.containerBackgroudColor = RGBCOLOR(0, 100, 14);

          //设置popView在哪个控件下面显示,以及小三角显示的位置

          [pView showFrom:sender alignStyle:CPAlignStyleRight];

}

下面这是代理方法,负责监听点击了哪一个cell

- (void)popOverView:(ZWCustomPopView *)pView didClickMenuIndex:(NSInteger)index

{

           NSArray *menus = @[@"发起群聊", @"添加朋友",@"扫一扫",@"收付款"];

           UIAlertView *alert = [[UIAlertView alloc]initWithTitle:[NSString stringWithFormat:@"你点击了: %@", menus[index]] message:nil delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];

           [alert show];

}

使用起来还是很简单的,接下来简单看看具体是实现思路。

        首先创建一个继承于UIView的ZWCustomPopView类,这个类主要是负责整个视图的显示,将该view放在window上。然后将第带有小三角形layer层对应的UIView实例子对象放置在这个view上。这个view类主要是充当蒙板作用,借助UIWindow可以实现坐标的转换,在特定按钮下弹出这个绿色的layer层。最后layer层上在放置相应的控件,如上面效果图中的tableView。

       在ZWCustomPopView这个类的.m文件中,添加一个继承于UIView的ZWCustomPopContainerView类扩展,这个类主要是负责带有小三角形的绿色的layer层的显示。提示:这种写法叫做类扩展,用于构建不公开的属性和方法。写在.h里面,谁都知道谁都可以改,写在.m里面只要自己知道,别人看不到也拿不到。这里我们不需要对外可见,写在.m文件中即可。

        接着,在ZWCustomPopContainerView这个类扩展中initWithFrame:方法中添加,写上如下代码:[self addObserver:self forKeyPath:@"frame" options:0 context:NULL];主要是通过KVO监听这个类扩展的frame属性值。只要ZWCustomPopContainerView的实例对象的frame值发生变化,就会调用[self setLayerFrame:newFrame];方法。

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:     (NSDictionary*)change context:(void *)context{

       if([keyPath isEqualToString:@"frame"]) {

             CGRect newFrame = CGRectNull;

             if([object valueForKeyPath:keyPath] != [NSNull null]) {

                     newFrame = [[object valueForKeyPath:keyPath] CGRectValue]; 

                      //这个方法是实现绘制的绿色背景的主要方法

                       [self setLayerFrame:newFrame];

              }

         }

}

绘制这个绿色layer层的思路如下:

模仿微信封装popView动画效果_第3张图片

       绘制这个layer首先要获取关键点的坐标,然后根据关键点的绘制路径,最后填充。仔细观察上面的效果图,不仅仅有小三角形,矩形的四周还有圆角设置。绘制这个layer层路径的关键点是从小三角形定点开始,从左到右逆时针围绕layer层外边的七个点,除了这起个点之外,还有矩形四个顶角圆弧所在的中心圆的中心点和圆弧所圆的半径。另外,我们还要知道三角形的高和底边长度。接下来看一下layer层的实现代码,先定义三个宏分别为三角形的默认高度和宽度,以及圆弧半径。

#define kTriangleHeight 8.0

#define kTriangleWidth 10.0

#define kPopOverLayerCornerRadius 5.0

再看一下[self setLayerFrame:newFrame];方法的主要实现。

//只要有三角形顶点坐标,再加上自己的宽和高,便可确定位置,所以小三角的顶点坐标设置为属性

- (void)setLayerFrame:(CGRect)frame

{

        float apexOfTriangelX;

      if (_apexOftriangelX == 0) {

          apexOfTriangelX = frame.size.width - 60;

        }else

         {

                  apexOfTriangelX = _apexOftriangelX;

          }

        //从三角形的顶点开始画线,刚好是7个点,即0-6

          if (apexOfTriangelX > frame.size.width - kPopOverLayerCornerRadius) {

                   apexOfTriangelX = frame.size.width - kPopOverLayerCornerRadius - 0.5 * kTriangleWidth;

          }else if (apexOfTriangelX < kPopOverLayerCornerRadius) {

                  apexOfTriangelX = kPopOverLayerCornerRadius + 0.5 * kTriangleWidth;

         }

        //这里是从三角形的顶点到三角形的坐标的点开始画线

        CGPoint point0 = CGPointMake(apexOfTriangelX, 0);

        CGPoint point1 = CGPointMake(apexOfTriangelX - 0.5 * kTriangleWidth, kTriangleHeight);

         CGPoint point2 = CGPointMake(kPopOverLayerCornerRadius, kTriangleHeight);

        //圆弧所在元对应的圆心坐标

        CGPoint point2_center = CGPointMake(kPopOverLayerCornerRadius, kTriangleHeight + kPopOverLayerCornerRadius);

       CGPoint point3 = CGPointMake(0, frame.size.height - kPopOverLayerCornerRadius);

       //圆弧所在元对应的圆心坐标

       CGPoint point3_center = CGPointMake(kPopOverLayerCornerRadius, frame.size.height - kPopOverLayerCornerRadius);

      CGPoint point4 = CGPointMake(frame.size.width - kPopOverLayerCornerRadius, frame.size.height);

    //圆弧所在元对应的圆心坐标

      CGPoint point4_center = CGPointMake(frame.size.width - kPopOverLayerCornerRadius, frame.size.height - kPopOverLayerCornerRadius);

       CGPoint point5 = CGPointMake(frame.size.width, kTriangleHeight + kPopOverLayerCornerRadius);

     //圆弧所在元对应的圆心坐标

      CGPoint point5_center = CGPointMake(frame.size.width - kPopOverLayerCornerRadius, kTriangleHeight + kPopOverLayerCornerRadius);

      CGPoint point6 = CGPointMake(apexOfTriangelX + 0.5 * kTriangleWidth, kTriangleHeight);

       UIBezierPath *path = [UIBezierPath bezierPath];

      [path moveToPoint:point0];

      [path addLineToPoint:point1];

      [path addLineToPoint:point2];

       [path addArcWithCenter:point2_center radius:kPopOverLayerCornerRadius startAngle:3*M_PI_2 endAngle:M_PI clockwise:NO];

     [path addLineToPoint:point3];

      [path addArcWithCenter:point3_center radius:kPopOverLayerCornerRadius startAngle:M_PI endAngle:M_PI_2 clockwise:NO];

       [path addLineToPoint:point4];

       [path addArcWithCenter:point4_center radius:kPopOverLayerCornerRadius startAngle:M_PI_2   endAngle:0 clockwise:NO];

      [path addLineToPoint:point5];

      [path addArcWithCenter:point5_center radius:kPopOverLayerCornerRadius startAngle:0 endAngle:3*M_PI_2 clockwise:NO];

      [path addLineToPoint:point6];

    [path closePath];

     self.popLayer.path = path.CGPath;

      //如果设置_layerColor就显示_layerColor的颜色,否者默认为greenColor 

      self.popLayer.fillColor = _layerColor? _layerColor.CGColor : [UIColor greenColor].CGColor;

}

       接着还要在这个类别中要重写一个很重要系统的方法:- (void)didMoveToSuperview。

- (void)didMoveToSuperview{

        [super didMoveToSuperview];

        // animations support 

         //设置刚出来时的动画效果显示1.2倍,然后变回原来大小

        self.transform = CGAffineTransformMakeScale(1.2,1.2);

        self.alpha = 0;

       [UIView beginAnimations:nil context:nil];

       [UIView setAnimationDuration:0.2];

       self.transform = CGAffineTransformMakeScale(1.0,1.0);

       self.alpha = 1;

       [UIView commitAnimations];

}

       另外还要写类扩展ZWCustomPopContainerView两个属性的set方法,apexOftriangelX和layerColor属性,前者是小三角形的顶点坐标,后者是layer层的颜色属性。在设置小三角的顶点属性以及layer层的属性时,调用上面的[self setLayerFrame:self.frame];方法。主要目的是为了当外部调用- (void)showFrom:(UIView *)from alignStyle:(CPAlignStyle)style(对外提供的接口)方法时,设置小三角形的不同位置,重新调用[self setLayerFrame:self.frame];方法。

- (void)setApexOftriangelX:(CGFloat)apexOftriangelX

{

        _apexOftriangelX = apexOftriangelX;

        [self setLayerFrame:self.frame];

}


- (void)setLayerColor:(UIColor *)layerColor

{

          _layerColor = layerColor;

          [self setLayerFrame:self.frame];

}

       至于ZWCustomPopView这个类中具体实现的方法在,就不在详细说明了。里面都是一些很常规的布局,当外部调用- (instancetype)initWithBounds:(CGRect)bounds titleMenus:(NSArray *)titles maskAlpha:(CGFloat)alpha;方法时,就将popView内部视图设置为tableView;当调用+ (instancetype)popOverView;方法时,就不设置popView内部视图,完全是使用者自定义。具体如何使用以及实现逻辑,可以参照这篇文章和源代码一块学习。源代码链接见上方。

你可能感兴趣的:(模仿微信封装popView动画效果)