iOS仿微信悬浮窗

仿微信悬浮窗,可直接协议加入悬浮窗或者直接调用方法注册,可自定义转场动画

Github地址(WMZFloatView)

演示

myFloat.gif

用法1 在Appdelegate中注册 传入对应控制器的className

 //只带控制器的className 
 [[WMZFloatManage shareInstance] registerControllers:@[@"ViewController"]];
  //带其他配置(标题和图片)
[[WMZFloatManage shareInstance]    registerControllers:@[@{@"controllerName":@"ViewController",@"icon":@"float_circle_full"}]];

用法2 实现协议 WMZFloatViewProtocol 即可

 //可选实现协议的方法 传入标题和图片
  - (NSDictionary *)floatViewConfig{
    return @{@"name":@"实际显示在悬浮窗的标题",@"icon":@"float_image"};
 }

用法3 改变转场动画 传入继承UIViewControllerAnimatedTransitioning协议的类即可

//自定义push动画
@property(nonatomic,strong)NSObject<UIViewControllerAnimatedTransitioning> *pushAnimal;
//自定义pop动画
@property(nonatomic,strong)NSObject<UIViewControllerAnimatedTransitioning>  *popAnimal;

依赖

无任何依赖

讲解

来说下我的实现思路

1 浮动框

1 实现一个悬浮窗的单例管理器,继承UINavigationControllerDelegate,UIGestureRecognizerDelegate

2 监听侧滑手势是否开始

  //监听侧滑手势开始
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
if ([gestureRecognizer isKindOfClass:[UIScreenEdgePanGestureRecognizer class]]&&self.nowVC.navigationController.viewControllers.count>1) {
    //判断是否实现了协议 加入可悬浮
    BOOL isConform = [self.nowVC conformsToProtocol:@protocol(WMZFloatViewProtocol)];
    NSString *tmpName = NSStringFromClass([self.nowVC class]);
    if (isConform) {
        if ([self.reginerVCName indexOfObject:tmpName] == NSNotFound) {
            NSMutableDictionary *mdic = [NSMutableDictionary new];
            [mdic setObject:tmpName forKey:@"controllerName"];
            //实现了配置的协议 加入配置
            if ([self.nowVC respondsToSelector:@selector(floatViewConfig)]) {
                NSDictionary *dic = [self.nowVC performSelector:@selector(floatViewConfig)];
                [dic enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
                    [mdic setObject:obj forKey:key];
                }];
            }
            [self.reginerVCName addObject:tmpName];
            [self.reginerVCConfig setObject:mdic forKey:tmpName];
        }
    }
    //达到可悬浮的条件
    if ([self.reginerVCName indexOfObject:tmpName] != NSNotFound && [self.cacheKey indexOfObject:[self getKey:self.nowVC]] == NSNotFound){
        self.edge = (UIScreenEdgePanGestureRecognizer *) gestureRecognizer;
        [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
        [faloatWindow addSubview:self.floatView];
        self.normalRect = self.floatView.frame;
        CGRect rect = self.floatView.frame;
        rect.origin.x = FloatWidth;
        rect.origin.y = FloatHeight;
        self.floatView.frame = rect;
        self.floatView.full = (self.cacheKey.count>=5);
    }
    
}
return YES;

}

3 监听侧滑触碰点是否碰到悬浮窗

 //圆心到点的距离>?半径
 + (BOOL)point:(CGPoint)point inCircleRect:(CGRect)rect {
     CGFloat radius = rect.size.height;
     CGPoint center = CGPointMake(rect.origin.x + radius, rect.origin.y + radius);
     double dx = fabs(point.x - center.x);
     double dy = fabs(point.y - center.y);
     double dis = hypot(dx, dy);
     return dis <= radius;
 }

2 悬浮窗图案 1~5

 NSDictionary *dic = @{
    @(1):@[
            @{
                @"frame":[NSValue valueWithCGRect:CGRectMake(panSize*0.15, panSize*0.15, width, height)],
                @"cornerRadius":@(height/2)
            }
    ],
    @(2):@[
            @{
                @"frame":[NSValue valueWithCGRect:CGRectMake(panSize*scale-panSize/6, panSize*scale , width, height)],
                @"pathAngle":@(M_PI_4)
            },
            @{
                @"frame":[NSValue valueWithCGRect:CGRectMake(panSize*scale-panSize/6+panSize*scale-4, panSize*scale , width, height)],
                @"cornerRadius":@(height/2)
            }
    ],
    @(3):@[
          @{
            @"frame":[NSValue valueWithCGRect:CGRectMake(panSize/2-panSize*scale/2, panSize*scale, width, height)],
            @"pathAngle":@(M_PI_4),
            @"toValue":@(M_PI_2+M_PI_4)
          },
          @{
            @"frame":[NSValue valueWithCGRect:CGRectMake(panSize*scale,panSize*scale  + height -4, width, height)],
            @"pathAngle":@(M_PI_4),
            @"toValue":@(M_PI*2),
            @"fromValue":@(M_PI)
          },
          @{
            @"frame":[NSValue valueWithCGRect:CGRectMake(panSize*scale +width  -2, panSize*scale + height -4, width, height)],
            @"pathAngle":@(M_PI_4),
            @"toValue":@(-M_PI_2-M_PI_4)
            },
    ],
    @(4):@[
            @{
                @"frame":[NSValue valueWithCGRect:CGRectMake(panSize/2-panSize*scale/2, panSize*scale , width, height)],
                @"pathAngle":@(M_PI_4),
                @"toValue":@(M_PI_2+M_PI_4)
            },
            @{
                @"frame":[NSValue valueWithCGRect:CGRectMake(panSize/2-panSize*scale,panSize*scale + height-5, width, height)],
                @"pathAngle":@(M_PI_4),
                @"toValue":@(M_PI_4),
            },
            @{
                @"frame":[NSValue valueWithCGRect:CGRectMake(panSize/2-panSize*scale + width +2, panSize*scale + height-7, width, height)],
                @"pathAngle":@(M_PI_4),
                @"toValue":@(-M_PI_2-M_PI_4)
                },
            @{
                @"frame":[NSValue valueWithCGRect:CGRectMake(panSize/2-panSize*scale/2+2, panSize*scale  + height*2 -7-5, width, height)],
                @"pathAngle":@(M_PI_4),
                @"toValue":@(-M_PI_4)
            },
    ],
    @(5):@[

            @{
                @"frame":[NSValue valueWithCGRect:CGRectMake(panSize/2-panSize*scale/2, panSize*scale, width, height)],
                @"pathAngle":@(M_PI_4),
                @"toValue":@(M_PI_2+M_PI_4),
                @"controlPoint":[NSValue valueWithCGPoint:CGPointMake(width/3, height/3)]
            },
            @{
                @"frame":[NSValue valueWithCGRect:CGRectMake(panSize/2-panSize*scale/2-panSize*scale/2,panSize*scale + height -5, width, height)],
                @"pathAngle":@(M_PI_4),
                @"toValue":@(M_PI_4+M_PI_4/2),
                @"controlPoint":[NSValue valueWithCGPoint:CGPointMake(width/3, height/3)]
            },
            @{
                @"frame":[NSValue valueWithCGRect:CGRectMake(panSize/2 +3 ,panSize*scale + height -5 -2, width, height)],
                @"pathAngle":@(M_PI_4),
                @"toValue":@(-M_PI_2-M_PI_4),
                @"controlPoint":[NSValue valueWithCGPoint:CGPointMake(width/3, height/3)]
                },
            @{
                @"frame":[NSValue valueWithCGRect:CGRectMake(panSize/2-panSize*scale+3, panSize*scale + height*2 -5 -2 -1, width, height)],
                @"pathAngle":@(M_PI_4),
                @"toValue":@(2*M_PI),
                @"fromValue":@(M_PI),
                @"controlPoint":[NSValue valueWithCGPoint:CGPointMake(width/3, height/3)]
            },
            @{
                @"frame":[NSValue valueWithCGRect:CGRectMake(panSize/2-panSize*scale+3 + width -2,  panSize*scale + height*2 -5 -2 -1-1, width, height)],
                @"pathAngle":@(M_PI_4),
                @"toValue":@(-M_PI_2/3-M_PI_4),
                @"controlPoint":[NSValue valueWithCGPoint:CGPointMake(width/3, height/3)]
            },
    ],
};

NSArray *config = dic[@(data.count)];

 //创建图片
NSMutableArray *imageArr = [NSMutableArray new];
for (NSString *key in data) {
    UIImageView *image = [UIImageView new];
    NSDictionary *detailDic = [cache objectForKey:key];
    if (detailDic[@"icon"]) {
        image.image = [UIImage imageNamed:detailDic[@"icon"]];
    }else{
        image.backgroundColor = FloatShowColor;
    }
    image.layer.masksToBounds = YES;
    [imageArr addObject:image];
    [self addSubview:image];
    [self bringSubviewToFront:image];
}

CFTimeInterval time = ((data.count == normalCount)?0.01:1);

 //图片绘制图案和动画
for (int i = 0; i<imageArr.count; i++) {
    UIImageView *image = imageArr[i];
    NSDictionary *frameDic = config[i];
    image.frame = [frameDic[@"frame"] CGRectValue];
    if (frameDic[@"cornerRadius"]) {
        image.layer.cornerRadius = [frameDic[@"cornerRadius"] floatValue];
    }
    if (frameDic[@"pathAngle"]) {
        CGPoint point = [frameDic[@"controlPoint"] CGPointValue];
        CAShapeLayer *layer = [CAShapeLayer layer];
        layer.path = [self getPathWithRadius:image.frame.size.height/2 center:CGPointMake(image.frame.size.width/2, image.frame.size.height/2) angle:[frameDic[@"pathAngle"] doubleValue] controlPoint:point].CGPath;
        layer.frame = image.bounds;
        if (frameDic[@"toValue"]) {
            double toValue = [frameDic[@"toValue"] doubleValue];
            double fromValue =  [frameDic[@"fromValue"] doubleValue];
            [layer addAnimation:[self getAnimationWithValue:toValue fromValue:fromValue duration:time] forKey:nil];
        }
        image.layer.mask = layer;
        
    }
}

Github地址(WMZFloatView)
有什么问题欢迎给我提issue,欢迎star

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

推荐阅读更多精彩内容

  • 关键词 转场动画,手势监听,核心动画 运行效果 使用简介 主要使用类目及功能 整体涉及以下几个主要的类,并注明其功...
    Colleny_Z阅读 7,353评论 0 5
  • 先看看效果图 demo在这里。 代码结构 HXSuspendViewManager是一个单例,负责主要的逻辑,控制...
    大泡沫阅读 4,181评论 0 55
  • 序言 前些日子跟朋友聊天,朋友Z果粉,前些天更新了微信,说微信出了个好方便的功能啊,我问是啥功能啊,看看我大And...
    24K纯帅豆阅读 6,822评论 2 31
  • 01这世界上有一只鸟,他有一个名字,叫树轮鸟。他有一条细细长长的青色尾羽,身上的羽毛也是青色的,胸前有着五色的花纹...
    桃司阅读 336评论 0 3
  • 1. 我最开始认识303班的路小小,是在那次严老师的光火中。 那天上完课,想在办公室里写几句后记。门“砰”地一下开...
    朴朴纳蓝阅读 933评论 0 2