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閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容

  • 關鍵詞 轉場動畫,手勢監聽,核心動畫 運行效果 使用簡介 主要使用類目及功能 整體涉及以下幾個主要的類,并注明其功...
    Colleny_Z閱讀 7,364評論 0 5
  • 先看看效果圖 demo在這里。 代碼結構 HXSuspendViewManager是一個單例,負責主要的邏輯,控制...
    大泡沫閱讀 4,209評論 0 55
  • 序言 前些日子跟朋友聊天,朋友Z果粉,前些天更新了微信,說微信出了個好方便的功能啊,我問是啥功能啊,看看我大And...
    24K純帥豆閱讀 6,827評論 2 31
  • 01這世界上有一只鳥,他有一個名字,叫樹輪鳥。他有一條細細長長的青色尾羽,身上的羽毛也是青色的,胸前有著五色的花紋...
    桃司閱讀 342評論 0 3
  • 1. 我最開始認識303班的路小小,是在那次嚴老師的光火中。 那天上完課,想在辦公室里寫幾句后記。門“砰”地一下開...
    樸樸納藍閱讀 939評論 0 2