swift4.0 仿新版微信浮窗效果

作者: 75cba68968ad | 来源:发表于2018-07-11 11:37 被阅读14次

微信里面阅读公众号或其他文章,经常需要暂时退出文章.
在新版微信中,可以把浏览的文章缩小为浮窗.点击浮窗继续阅读.对于经常在微信里阅读的人来说,这简直就是人类之光.

网上OC版本的模仿有很多,最近不是很忙,写了一个swift版本
demo下载地址

效果预览.gif

demo下载地址

使用简单

// AppDelegate 中加入需要保持用来悬壶的控制器
FloatManager.addFloatVcClass(vcClass: ["XLSecondViewController"])

主要用到的技术

1、定时器、用来时时获取滑动返回的手势信息

//定时器
    lazy var link: CADisplayLink = {
        let obj = CADisplayLink.init(target: self, selector: #selector(panBack(currentLink:)))
        obj.add(to: RunLoop.main, forMode: .commonModes)
        obj.isPaused = true
        return obj
    }()

2、滑动返回兼听

extension FloatManager : UIGestureRecognizerDelegate {
    
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        if self.currentNavigationController().viewControllers.count > 1 {
            self.beginScreenEdgePanBack(ges: gestureRecognizer)
            
            return true
        }
        return false
    }
    
}

@objc func panBack(currentLink: CADisplayLink) {
        print("------")
        if self.edgePan?.state == UIGestureRecognizerState.changed {
            let tPoint = self.edgePan?.translation(in: kWindow)
            let x = max(UISCREEN_WIDTH + xFloatMargin - xCoef * (tPoint?.x)!, UISCREEN_WIDTH - xFloatAreaR)
            let y = max(UISCREEN_HEIGHT + xFloatMargin - xCoef * (tPoint?.x)!, UISCREEN_HEIGHT - xFloatAreaR)
            let rect = CGRect.init(x: x, y: y, width: xFloatAreaR, height: xFloatAreaR)
            self.floatArea.frame = rect
            let touchPoint = kWindow?.convert((self.edgePan?.location(in: kWindow))!, to: self.floatArea)
            print("touchPoint==\(touchPoint)")
            if ((touchPoint?.x)! > CGFloat(0) && (touchPoint?.y)! > CGFloat(0)) {
                
                if (pow(xFloatAreaR - (touchPoint?.x)!, 2) + pow(xFloatAreaR - (touchPoint?.y)!, 2) <= pow(xFloatAreaR, 2)) {
                    if (self.showFloatBall == false) {
                        self.showFloatBall = true
                    }
                } else {
                    if self.showFloatBall {
                        self.showFloatBall = false
                    }
                }
                
            } else {
                if self.showFloatBall {
                    self.showFloatBall = false
                }
            }
        } else if self.edgePan?.state == UIGestureRecognizerState.possible {
            UIView.animate(withDuration: 0.5, animations: {
                self.floatArea.frame = CGRect(x: UISCREEN_WIDTH, y: UISCREEN_HEIGHT, width: xFloatAreaR, height: xFloatAreaR)
            }) { (finished) in
                self.floatArea.removeFromSuperview()
                self.link.isPaused = true
                if self.showFloatBall {
                    self.floatVC = self.tempFloatVC
                    if self.haveIconImage() {
                        
                        let image = self.floatVC?.value(forKey: "xlIconImage")
                        if image != nil {
                            self.floatBall.imageView?.image = (image as! UIImage)
                        }
                    }
                    self.floatBall.frame = CGRect(x: UISCREEN_WIDTH - xBallSizeR - 15, y: UISCREEN_HEIGHT / 3, width: xBallSizeR, height: xBallSizeR)
                    self.floatBall.alpha = 1
                    self.kWindow?.addSubview(self.floatBall)
                }
                
            }
            
        }
        
    }

3、半圆状态

var highlight: Bool = false {
        
        didSet {
            
            if style == .XLFloatAreaViewStyle_default {
                self.title = highlight ? "释放开启浮窗" : "拖动到此,开启浮窗"
            } else if style == .XLFloatAreaViewStyle_cancel {
                self.title = highlight ? "释放关闭浮窗" : "拖动到此,关闭浮窗"
            }
            
            if highlight {
                if #available(iOS 10.0, *) {
                    let impactLight = UIImpactFeedbackGenerator.init(style: .medium)
                    impactLight.impactOccurred()
                } else {
                    // Fallback on earlier versions
                }
                
            }
            
            self.setNeedsDisplay()
            
        }
        
    }

4、悬浮按钮的事件处理

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        
        for obj in touches {
            
            self.center = obj.location(in: UIApplication.shared.keyWindow)
            if self.delegate != nil {
                self.delegate?.floatBallBeginMove()
            }
            
        }
        
    }
    
    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        
        self.endTouch(touces: touches)
        
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        
        self.endTouch(touces: touches)

    }
    
    //手势结束时对视图位置重新设定
    func endTouch(touces: Set<UITouch>) {
        var frame = self.frame
        let navHeight: CGFloat = UIApplication.shared.statusBarFrame.height + 44
        for obj in touces {
            let point = obj.location(in: UIApplication.shared.keyWindow)
            if point.x > (UISCREEN_WIDTH / 2) {
                frame.origin.x = UISCREEN_WIDTH - frame.size.width - margin
            } else {
                frame.origin.x = margin
            }
            
            if frame.origin.y > (UISCREEN_HEIGHT - frame.size.height - margin) {
                frame.origin.y = UISCREEN_HEIGHT - frame.size.height - margin
            } else if frame.origin.y < navHeight {
                frame.origin.y = navHeight
            }
            
            UIView.animate(withDuration: 0.3) {
                self.frame = frame
            }
        }
        
        if self.delegate != nil {
            self.delegate?.floatBallEndMove()
        }
        
    }
    
     @objc private func ballClick() {
        
        if self.delegate != nil {
            self.delegate?.floatBallClick()
        }
    }

5、push、pop动画

push

extension XLTransitionPush: UIViewControllerAnimatedTransitioning {
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return xTimeInterval
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        self.transitionContext = transitionContext
        
        let fromVC = transitionContext.viewController(forKey: .from)
        let toVC = transitionContext.viewController(forKey: .to)
        
        let contView = transitionContext.containerView
        contView.addSubview((fromVC?.view)!)
        contView.addSubview((toVC?.view)!)
        
        let floatBallRect = FloatManager.shareFloatManager.floatBall.frame
        fromVC?.view.addSubview(self.coverView)
        
        let startBezierPath = UIBezierPath.init(roundedRect: CGRect(x: floatBallRect.origin.x, y: floatBallRect.origin.y, width: floatBallRect.size.width, height: floatBallRect.size.height), cornerRadius: floatBallRect.size.width / 2)
        let finishBezierPath = UIBezierPath.init(roundedRect: CGRect(x: 0, y: 0, width: UISCREEN_WIDTH, height: UISCREEN_HEIGHT), cornerRadius: floatBallRect.size.width / 2)
        
        let layer = CAShapeLayer()
        layer.path = startBezierPath.cgPath
        toVC?.view.layer.mask = layer
        
        let layerAnimation = CABasicAnimation(keyPath: "path")
        layerAnimation.fromValue = startBezierPath.cgPath
        layerAnimation.toValue = finishBezierPath.cgPath
        layerAnimation.duration = xTimeInterval
        layerAnimation.timingFunction = CAMediaTimingFunction.init(name: kCAMediaTimingFunctionEaseInEaseOut)
        layerAnimation.delegate = self
        layer.add(layerAnimation, forKey: "path")
        
        UIView.animate(withDuration: xTimeInterval) {
            FloatManager.shareFloatManager.floatBall.alpha = 0
        }
    }

    
}

extension XLTransitionPush: CAAnimationDelegate {
    
    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        self.transitionContext?.completeTransition(true)
        self.transitionContext?.viewController(forKey: .from)?.view.layer.mask = nil
        self.transitionContext?.viewController(forKey: .to)?.view.layer.mask = nil
        self.coverView.removeFromSuperview()
    }
    
}

pop

extension XLTransitionPop: UIViewControllerAnimatedTransitioning {
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return xTimeInterval
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        self.transitionContext = transitionContext
        
        let fromVC = transitionContext.viewController(forKey: .from)
        let toVC = transitionContext.viewController(forKey: .to)
        
        let contView = transitionContext.containerView
        contView.addSubview((toVC?.view)!)
        contView.addSubview((fromVC?.view)!)
        
        let floatBallRect = FloatManager.shareFloatManager.floatBall.frame
        toVC?.view.addSubview(self.coverView)
        
        let startBezierPath = UIBezierPath.init(roundedRect: CGRect(x: floatBallRect.origin.x, y: floatBallRect.origin.y, width: floatBallRect.size.width, height: floatBallRect.size.height), cornerRadius: floatBallRect.size.width / 2)
        let finishBezierPath = UIBezierPath.init(roundedRect: CGRect(x: 0, y: 0, width: UISCREEN_WIDTH, height: UISCREEN_HEIGHT), cornerRadius: floatBallRect.size.width / 2)
        
        let layer = CAShapeLayer()
        layer.path = startBezierPath.cgPath
        fromVC?.view.layer.mask = layer
        
        let layerAnimation = CABasicAnimation(keyPath: "path")
        layerAnimation.toValue = startBezierPath.cgPath
        layerAnimation.fromValue = finishBezierPath.cgPath
        layerAnimation.duration = xTimeInterval
        layerAnimation.timingFunction = CAMediaTimingFunction.init(name: kCAMediaTimingFunctionEaseInEaseOut)
        layerAnimation.delegate = self
        layer.add(layerAnimation, forKey: "path")
        
        UIView.animate(withDuration: xTimeInterval) {
            self.coverView.alpha = 0
            FloatManager.shareFloatManager.floatBall.alpha = 1
        }
    }
    
    
}

extension XLTransitionPop: CAAnimationDelegate {
    
    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        self.transitionContext?.completeTransition(true)
        self.transitionContext?.viewController(forKey: .from)?.view.layer.mask = nil
        self.transitionContext?.viewController(forKey: .to)?.view.layer.mask = nil
        self.coverView.removeFromSuperview()
    }
    
}

demo下载地址

以上便是实现该效果的全部实现.上方含有部分伪代码,全部代码已上传至 Github下载地址 欢迎(跪求) Star.

相关文章

  • 仿新版微信浮窗效果

    仿新版微信浮窗效果 仿新版微信浮窗效果

  • swift4.0 仿新版微信浮窗效果

    微信里面阅读公众号或其他文章,经常需要暂时退出文章.在新版微信中,可以把浏览的文章缩小为浮窗.点击浮窗继续阅读.对...

  • 仿新版微信浮窗效果

    阅读公众号或其他文章,经常需要暂时退出文章.在新版微信中,可以把浏览的文章缩小为浮窗.点击浮窗继续阅读.对于经常在...

  • 微信浮窗功能更新 新增多种浮窗设置

    昨日,微信iOS版发布了新版本,对微信浮窗功能进行了升级,新增支持浏览文件、收藏笔记、小程序时设置浮窗。 此外,在...

  • 仿新版微信首页效果

    微信首页下拉效果很炫酷,作为一个安卓开发人员忍不住就要尝试一下了~ 实现的大致思路:使用recyclerview实...

  • iOS 一行代码集成微信浮窗功能

    XWFloatingWindow 一行代码集成微信浮窗功能 1. 效果 2. 使用 在需要浮窗展示的地方调用: 现...

  • Swift4.1 仿微信浮窗

    相信大家都有用过了,业余时间模仿了一下 浮窗的移动、隐藏显示、移动、移除部分 正文 思想:把整个功能拆分为三个部分...

  • Android仿微信文章悬浮窗效果

    Android仿微信文章悬浮窗效果 序言 (转载) 阅读公众号文章如果有人给你发微信你可以把这篇文章当作悬浮窗悬浮...

  • 「0722」淘客云-淘客动态

    淘客云今日快讯头条 2019年7月22日 星期一 ​ 1.微信安卓新版内侧:可将小程序设置成浮窗功能 微信 7.0...

  • 如何仿一个微信浮窗效果(转场动画篇)

    前言 微信的浮窗转场动画看着一闪而过,但是背后需要对转场动画的运行原理,相关类有哪些,他们都是如何作用的,又...

网友评论

    本文标题:swift4.0 仿新版微信浮窗效果

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