模仿微信封装popView动画效果

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


效果图一
效果图二

         说明一点,效果图中所弹出的绿色的边框(包括小三角形)不是通过图片处理,而是绘制出来的,具体的背景颜色和边框宽度都可随意调节。另外小三角形的位置支持三种显示模式:左上角、中间顶部、右上角显示,外部通过设置枚举值即可简单调用。第一个效果图中的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层的思路如下:

       绘制这个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内部视图,完全是使用者自定义。具体如何使用以及实现逻辑,可以参照这篇文章和源代码一块学习。源代码链接见上方。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,540评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,640评论 2 374
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,643评论 0 326
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,672评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,525评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,387评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,809评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,450评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,743评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,787评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,560评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,406评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,824评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,048评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,335评论 1 253
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,784评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,997评论 2 337

推荐阅读更多精彩内容