美文网首页为了更好的活着iOS精选博文iOS技术中心
iOS悬浮窗口(无论界面跳转、View始终在视图上显示,可移动)

iOS悬浮窗口(无论界面跳转、View始终在视图上显示,可移动)

作者: 何年何月 | 来源:发表于2016-09-24 23:52 被阅读5720次

    让所有界面都显示,最好还是封装一个继承Window的类:JYCWindow。

    先看看效果:

    mygif.gif

    关键代码如下:

    - (instancetype)initWithFrame:(CGRect)frame mainImageName:(NSString*)name bgcolor:(UIColor *)bgcolor animationColor:animationColor
    
    {
        if(self = [super initWithFrame:frame])
        {
            NSAssert(name != nil, @"mainImageName can't be nil !");
            
            self.backgroundColor = [UIColor clearColor];
            self.windowLevel = UIWindowLevelAlert + 1;  //如果想在 alert 之上,则改成 + 2
            self.rootViewController = [UIViewController new];
            [self makeKeyAndVisible];
            
            _bgcolor = bgcolor;
            _frameWidth = frame.size.width;
            _animationColor = animationColor;
            
            
            _mainImageButton =  [UIButton buttonWithType:UIButtonTypeCustom];
            [_mainImageButton setFrame:(CGRect){0, 0,frame.size.width, frame.size.height}];
            [_mainImageButton setImage:[UIImage imageNamed:name] forState:UIControlStateNormal];
            //        _mainImageButton.layer.cornerRadius = frame.size.width*0.5;
            //        _mainImageButton.layer.masksToBounds= YES;
            _mainImageButton.alpha = normalAlpha;
            [_mainImageButton addTarget:self action:@selector(click:) forControlEvents:UIControlEventTouchUpInside];
            if (_animationColor) {
                [_mainImageButton addTarget:self action:@selector(mainBtnTouchDown) forControlEvents:UIControlEventTouchDown];
            }
            
            [self addSubview:_mainImageButton];
            
            
             // 增加拖动window的手势
            _pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(locationChange:)];
            _pan.delaysTouchesBegan = NO;
            [self addGestureRecognizer:_pan];
            _tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(click:)];
            [self addGestureRecognizer:_tap];
            
    
            [self performSelector:@selector(justbegin) withObject:nil afterDelay:statusChangeDuration];
    
            
        }
        return self;
    }
    
    

    至于怎么移动,怎么动画,直接看.h和.m文件如下:

    JYCWindow.h###

    
    #import <UIKit/UIKit.h>
    
    typedef void(^CallTheService)(void);
    
    @interface JYCWindow : UIWindow
    
    //重要:所有图片都要是圆形的,程序里并没有自动处理成圆形
    
    //  warning: frame的长宽必须相等
    - (instancetype)initWithFrame:(CGRect)frame mainImageName:(NSString*)name bgcolor:(UIColor *)bgcolor;
    
    // 长按雷达辐射效果
    - (instancetype)initWithFrame:(CGRect)frame mainImageName:(NSString*)name bgcolor:(UIColor *)bgcolor animationColor:animationColor;
    
    // 显示(默认)
    - (void)showWindow;
    
    // 隐藏
    - (void)dissmissWindow;
    
    @property (nonatomic,copy)CallTheService callService;
    @end
    
    

    JYCWindow.m###

    
    
    #import "JYCWindow.h"
    #define kk_WIDTH self.frame.size.width
    #define kk_HEIGHT self.frame.size.height
    
    #define kScreenWidth [[UIScreen mainScreen] bounds].size.width
    #define kScreenHeight [[UIScreen mainScreen] bounds].size.height
    
    #define animateDuration 0.3       //位置改变动画时间
    #define showDuration 0.1          //展开动画时间
    #define statusChangeDuration  3.0    //状态改变时间
    #define normalAlpha  1.0           //正常状态时背景alpha值
    #define sleepAlpha  0.5           //隐藏到边缘时的背景alpha值
    #define myBorderWidth 1.0         //外框宽度
    #define marginWith  5             //间隔
    
    #define WZFlashInnerCircleInitialRaius  20
    
    @interface JYCWindow ()
    
    @property(nonatomic)NSInteger frameWidth;
    @property(nonatomic,strong)UIPanGestureRecognizer *pan;
    @property(nonatomic,strong)UITapGestureRecognizer *tap;
    @property(nonatomic,strong)UIButton *mainImageButton;
    @property(nonatomic,strong)UIColor *bgcolor;
    @property(nonatomic,strong)CAAnimationGroup *animationGroup;
    @property(nonatomic,strong)CAShapeLayer *circleShape;
    @property(nonatomic,strong)UIColor *animationColor;
    
    @end
    @implementation JYCWindow
    - (instancetype)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
            // Initialization code
        }
        return self;
    }
    
    - (instancetype)initWithFrame:(CGRect)frame mainImageName:(NSString*)name bgcolor:(UIColor *)bgcolor{
        return [self initWithFrame:frame mainImageName:name bgcolor:bgcolor animationColor:nil];
    }
    
    - (instancetype)initWithFrame:(CGRect)frame mainImageName:(NSString*)name bgcolor:(UIColor *)bgcolor animationColor:animationColor
    
    {
        if(self = [super initWithFrame:frame])
        {
            NSAssert(name != nil, @"mainImageName can't be nil !");
            
            self.backgroundColor = [UIColor clearColor];
            self.windowLevel = UIWindowLevelAlert + 1;  //如果想在 alert 之上,则改成 + 2
            self.rootViewController = [UIViewController new];
            [self makeKeyAndVisible];
            
            _bgcolor = bgcolor;
            _frameWidth = frame.size.width;
            _animationColor = animationColor;
            
            
            _mainImageButton =  [UIButton buttonWithType:UIButtonTypeCustom];
            [_mainImageButton setFrame:(CGRect){0, 0,frame.size.width, frame.size.height}];
            [_mainImageButton setImage:[UIImage imageNamed:name] forState:UIControlStateNormal];
            //        _mainImageButton.layer.cornerRadius = frame.size.width*0.5;
            //        _mainImageButton.layer.masksToBounds= YES;
            _mainImageButton.alpha = normalAlpha;
            [_mainImageButton addTarget:self action:@selector(click:) forControlEvents:UIControlEventTouchUpInside];
            if (_animationColor) {
                [_mainImageButton addTarget:self action:@selector(mainBtnTouchDown) forControlEvents:UIControlEventTouchDown];
            }
            
            [self addSubview:_mainImageButton];
            
    //        [self doBorderWidth:myBorderWidth color:nil cornerRadius:_frameWidth/2];
            
            _pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(locationChange:)];
            _pan.delaysTouchesBegan = NO;
            [self addGestureRecognizer:_pan];
            _tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(click:)];
            [self addGestureRecognizer:_tap];
            
    
            [self performSelector:@selector(justbegin) withObject:nil afterDelay:statusChangeDuration];
    
            
        }
        return self;
    }
    
    - (void)dissmissWindow{
        self.hidden = YES;
    }
    - (void)showWindow{
        self.hidden = NO;
    }
    
    - (void)justbegin{
        
        [self performSelector:@selector(changeStatus) withObject:nil afterDelay:statusChangeDuration];
    
        CGPoint panPoint = CGPointMake(kScreenWidth-80, kScreenHeight-150);
        
        [self changBoundsabovePanPoint:panPoint];
    }
    
    - (void)changBoundsabovePanPoint:(CGPoint)panPoint{
        
        if(panPoint.x <= kScreenWidth/2)
        {
            if(panPoint.y <= 40+kk_HEIGHT/2 && panPoint.x >= 20+kk_WIDTH/2)
            {
                [UIView animateWithDuration:animateDuration animations:^{
                    self.center = CGPointMake(panPoint.x, kk_HEIGHT/2);
                }];
            }
            else if(panPoint.y >= kScreenHeight-kk_HEIGHT/2-40 && panPoint.x >= 20+kk_WIDTH/2)
            {
                [UIView animateWithDuration:animateDuration animations:^{
                    self.center = CGPointMake(panPoint.x, kScreenHeight-kk_HEIGHT/2);
                }];
            }
            else if (panPoint.x < kk_WIDTH/2+20 && panPoint.y > kScreenHeight-kk_HEIGHT/2)
            {
                [UIView animateWithDuration:animateDuration animations:^{
                    self.center = CGPointMake(kk_WIDTH/2, kScreenHeight-kk_HEIGHT/2);
                }];
            }
            else
            {
                CGFloat pointy = panPoint.y < kk_HEIGHT/2 ? kk_HEIGHT/2 :panPoint.y;
                [UIView animateWithDuration:animateDuration animations:^{
                    self.center = CGPointMake(kk_WIDTH/2, pointy);
                }];
            }
        }
        else if(panPoint.x > kScreenWidth/2)
        {
            if(panPoint.y <= 40+kk_HEIGHT/2 && panPoint.x < kScreenWidth-kk_WIDTH/2-20 )
            {
                [UIView animateWithDuration:animateDuration animations:^{
                    self.center = CGPointMake(panPoint.x, kk_HEIGHT/2);
                }];
            }
            else if(panPoint.y >= kScreenHeight-40-kk_HEIGHT/2 && panPoint.x < kScreenWidth-kk_WIDTH/2-20)
            {
                [UIView animateWithDuration:animateDuration animations:^{
                    self.center = CGPointMake(panPoint.x, kScreenHeight-kk_HEIGHT/2);
                }];
            }
            else if (panPoint.x > kScreenWidth-kk_WIDTH/2-20 && panPoint.y < kk_HEIGHT/2)
            {
                [UIView animateWithDuration:animateDuration animations:^{
                    self.center = CGPointMake(kScreenWidth-kk_WIDTH/2, kk_HEIGHT/2);
                }];
            }
            else
            {
                CGFloat pointy = panPoint.y > kScreenHeight-kk_HEIGHT/2 ? kScreenHeight-kk_HEIGHT/2 :panPoint.y;
                [UIView animateWithDuration:animateDuration animations:^{
                    self.center = CGPointMake(kScreenWidth-kk_WIDTH/2, pointy);
                }];
            }
        }
    
    }
    //改变位置
    - (void)locationChange:(UIPanGestureRecognizer*)p
    {
        CGPoint panPoint = [p locationInView:[[UIApplication sharedApplication] keyWindow]];
        if(p.state == UIGestureRecognizerStateBegan)
        {
            [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(changeStatus) object:nil];
            _mainImageButton.alpha = normalAlpha;
        }
        if(p.state == UIGestureRecognizerStateChanged)
        {
            self.center = CGPointMake(panPoint.x, panPoint.y);
        }
        else if(p.state == UIGestureRecognizerStateEnded)
        {
            [self stopAnimation];
            [self performSelector:@selector(changeStatus) withObject:nil afterDelay:statusChangeDuration];
            
            [self changBoundsabovePanPoint:panPoint];
    
        }
    }
    //点击事件
    - (void)click:(UITapGestureRecognizer*)p
    {
        [self stopAnimation];
        
        _mainImageButton.alpha = normalAlpha;
        
        //拉出悬浮窗
        if (self.center.x == 0) {
            self.center = CGPointMake(kk_WIDTH/2, self.center.y);
        }else if (self.center.x == kScreenWidth) {
            self.center = CGPointMake(kScreenWidth - kk_WIDTH/2, self.center.y);
        }else if (self.center.y == 0) {
            self.center = CGPointMake(self.center.x, kk_HEIGHT/2);
        }else if (self.center.y == kScreenHeight) {
            self.center = CGPointMake(self.center.x, kScreenHeight - kk_HEIGHT/2);
        }
        
        
        if (self.callService) {
            
            self.callService();
        }
    
    
    
    }
    
    - (void)changeStatus
    {
        [UIView animateWithDuration:1.0 animations:^{
            _mainImageButton.alpha = sleepAlpha;
        }];
        [UIView animateWithDuration:0.5 animations:^{
            CGFloat x = self.center.x < 20+kk_WIDTH/2 ? 0 :  self.center.x > kScreenWidth - 20 -kk_WIDTH/2 ? kScreenWidth : self.center.x;
            CGFloat y = self.center.y < 40 + kk_HEIGHT/2 ? 0 : self.center.y > kScreenHeight - 40 - kk_HEIGHT/2 ? kScreenHeight : self.center.y;
            
            //禁止停留在4个角
            if((x == 0 && y ==0) || (x == kScreenWidth && y == 0) || (x == 0 && y == kScreenHeight) || (x == kScreenWidth && y == kScreenHeight)){
                y = self.center.y;
            }
            self.center = CGPointMake(x, y);
        }];
    }
    
    //- (void)doBorderWidth:(CGFloat)width color:(UIColor *)color cornerRadius:(CGFloat)cornerRadius{
    //    //  self.layer.masksToBounds = YES;
    //    self.layer.cornerRadius = cornerRadius;
    //    self.layer.borderWidth = width;
    //    if (!color) {
    //        self.layer.borderColor = [UIColor whiteColor].CGColor;
    //    }else{
    //        self.layer.borderColor = color.CGColor;
    //    }
    //}
    
    #pragma mark  ------- animation -------------
    
    - (void)buttonAnimation{
        
        self.layer.masksToBounds = NO;
        
        CGFloat scale = 1.0f;
    //
        CGFloat width = self.mainImageButton.bounds.size.width, height = self.mainImageButton.bounds.size.height;
    //
        CGFloat biggerEdge = width > height ? width : height, smallerEdge = width > height ? height : width;
        CGFloat radius = smallerEdge / 2 > WZFlashInnerCircleInitialRaius ? WZFlashInnerCircleInitialRaius : smallerEdge / 2;
        
        scale = biggerEdge / radius + 0.5;
        _circleShape = [self createCircleShapeWithPosition:CGPointMake(width/2, height/2)
                                                  pathRect:CGRectMake(0, 0, radius * 3, radius * 3)
                                                    radius:radius];
        
        // 方形放大效果
    //            scale = 2.5f;
    //            _circleShape = [self createCircleShapeWithPosition:CGPointMake(width/2, height/2)
    //                                                     pathRect:CGRectMake(-CGRectGetMidX(self.mainImageButton.bounds), -CGRectGetMidY(self.mainImageButton.bounds), width, height)
    //                                                       radius:self.mainImageButton.layer.cornerRadius];
        
        
        [self.mainImageButton.layer addSublayer:_circleShape];
        
        CAAnimationGroup *groupAnimation = [self createFlashAnimationWithScale:scale duration:1.0f];
        
        [_circleShape addAnimation:groupAnimation forKey:nil];
    }
    
    - (void)stopAnimation{
        
        [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(buttonAnimation) object:nil];
        
        if (_circleShape) {
            [_circleShape removeFromSuperlayer];
        }
    }
    
    - (CAShapeLayer *)createCircleShapeWithPosition:(CGPoint)position pathRect:(CGRect)rect radius:(CGFloat)radius
    {
        CAShapeLayer *circleShape = [CAShapeLayer layer];
        circleShape.path = [self createCirclePathWithRadius:rect radius:radius];
        circleShape.position = position;
        
        
        // 雷达覆盖区域
        circleShape.bounds = CGRectMake(0, 0, radius * 3, radius * 3);
        circleShape.fillColor = _animationColor.CGColor;
        
        //  圆圈放大效果
        //  circleShape.fillColor = [UIColor clearColor].CGColor;
        //  circleShape.strokeColor = [UIColor purpleColor].CGColor;
        
        circleShape.opacity = 0;
        circleShape.lineWidth = 1;
        
        return circleShape;
    }
    
    - (CAAnimationGroup *)createFlashAnimationWithScale:(CGFloat)scale duration:(CGFloat)duration
    {
        CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
        scaleAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
        scaleAnimation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(scale, scale, 1)];
        
        CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
        alphaAnimation.fromValue = @1;
        alphaAnimation.toValue = @0;
        
        _animationGroup = [CAAnimationGroup animation];
        _animationGroup.animations = @[scaleAnimation, alphaAnimation];
        _animationGroup.duration = duration;
        _animationGroup.repeatCount = INFINITY;
        _animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
        
        return _animationGroup;
    }
    
    
    - (CGPathRef)createCirclePathWithRadius:(CGRect)frame radius:(CGFloat)radius
    {
        return [UIBezierPath bezierPathWithRoundedRect:frame cornerRadius:radius].CGPath;
    }
    
    
    - (void)mainBtnTouchDown{
        
        [self performSelector:@selector(buttonAnimation) withObject:nil afterDelay:0.5];
    }
    
    
    /*
    // Only override drawRect: if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    - (void)drawRect:(CGRect)rect {
        // Drawing code
    }
    */
    
    @end
    

    最后附上小demo

    相关文章

      网友评论

      • 指尖猿:楼主在吗?请教个问题
      • 李某lkb:做成单例会好一点
      • 布丶Ding:https://github.com/shanghaiMichael/DYYFloatWindow/issues/2 这个链接中说得重影问题楼主有解决吗?原因应该CGPoint panPoint = [p locationInView:[[UIApplication sharedApplication] keyWindow]];取到的连个点差距太远造成,但我不知道为什么会取到这样的情况,以为他应该取的是实时触摸的点,但貌似在取到实时点之后接着多取到一个点,然后设置center造成了重影
        红尘_客:重影问题解决了,CGPoint panPoint = [p locationInView:[UIApplication sharedApplication].delegate.window];
      • 7d1d377fde8d:[self makeKeyAndVisible];你的窗口是放在这个层级上面,所以不管怎么样调用,界面上窗口上还是会留着一个浮标;我能想到的隐藏的办法是[UIApplication sharedApplication].keyWindow.hidden = true;这个可以关闭隐藏浮标,但当没有浮标的时候,会把应用的主窗口给隐藏.....
        何年何月:@7d1d377fde8d 其实我没有太懂你的意思!我的自定义window和APPDelegate里的window不是同一个,我调用 [self.jycWindow dissmissWindow];直接就OK了,也不存在你说的那些问题。实在不行给我发张图片看看吧!QQ:1945509474
      • 7d1d377fde8d:如果是一个完整的app,那么注销用户,浮标还是显示状态.怎么处理
        何年何月:@7d1d377fde8d 额,里面有关于隐藏的代码dissmissWindow,调用下喽 :smile:
      • Pusswzy:是原创么
        何年何月:@Pusswzy 不是,参考了其他人的,不过有什么问题可以讨论一下:smile:

      本文标题:iOS悬浮窗口(无论界面跳转、View始终在视图上显示,可移动)

      本文链接:https://www.haomeiwen.com/subject/ssbxyttx.html